diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 7b335c8870..6c167a9f4d 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py @@ -21,7 +21,7 @@ from toastergui.widgets import ToasterTable from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project -from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task +from orm.models import CustomImageRecipe, Package, Target, Build, LogMessage, Task from orm.models import ProjectTarget from django.db.models import Q, Max, Count, When, Case, Value, IntegerField from django.conf.urls import url @@ -483,8 +483,8 @@ class CustomImagesTable(ToasterTable): def get_context_data(self, **kwargs): context = super(CustomImagesTable, self).get_context_data(**kwargs) project = Project.objects.get(pk=kwargs['pid']) + # TODO put project into the ToasterTable base class context['project'] = project - context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project'])) return context def setup_queryset(self, *args, **kwargs): @@ -502,22 +502,31 @@ class CustomImagesTable(ToasterTable): self.add_column(title="Custom image", hideable=False, + orderable=True, + field_name="name", static_data_name="name", static_data_template=name_link_template) self.add_column(title="Recipe file", static_data_name='recipe_file', - static_data_template='') + static_data_template='', + field_name='local_path') + + approx_packages_template = ''' + + {{data.package_set.all|length}} + ''' - approx_packages_template = '{{data.packages.all|length}}' self.add_column(title="Approx packages", static_data_name='approx_packages', static_data_template=approx_packages_template) - build_btn_template = '''''' + Build + ''' self.add_column(title="Build", hideable=False, @@ -540,12 +549,19 @@ class ImageRecipesTable(RecipesTable): def setup_columns(self, *args, **kwargs): + + name_link_template = ''' + {{data.name}} + ''' + self.add_column(title="Image recipe", help_text="When you build an image recipe, you get an " "image: a root file system you can" "deploy to a machine", hideable=False, orderable=True, + static_data_name="name", + static_data_template=name_link_template, field_name="name") super(ImageRecipesTable, self).setup_columns(*args, **kwargs) @@ -609,8 +625,96 @@ class SoftwareRecipesTable(RecipesTable): self.add_column(**RecipesTable.build_col) +class PackagesTable(ToasterTable): + """ Table to display the packages in a recipe from it's last successful + build""" -class SelectPackagesTable(ToasterTable): + def __init__(self, *args, **kwargs): + super(PackagesTable, self).__init__(*args, **kwargs) + self.title = "Packages included" + self.packages = None + self.default_orderby = "name" + + def create_package_list(self, recipe, project_id): + """Creates a list of packages for the specified recipe by looking for + the last SUCCEEDED build of ther recipe""" + + target = Target.objects.filter(Q(target=recipe.name) & + Q(build__project_id=project_id) & + Q(build__outcome=Build.SUCCEEDED) + ).last() + + if target: + return target.build.package_set.all() + + # Target/recipe never successfully built so empty queryset + return Package.objects.none() + + def get_context_data(self, **kwargs): + """Context for rendering the sidebar and other items on the recipe + details page """ + context = super(PackagesTable, self).get_context_data(**kwargs) + + recipe = Recipe.objects.get(pk=kwargs['recipe_id']) + project = Project.objects.get(pk=kwargs['pid']) + + in_project = (recipe.layer_version.pk in + project.get_project_layer_versions(pk=True)) + + packages = self.create_package_list(recipe, project.pk) + + context.update({'project': project, + 'recipe' : recipe, + 'packages': packages, + 'approx_pkg_size' : packages.aggregate(Sum('size')), + 'in_project' : in_project, + }) + + return context + + def setup_queryset(self, *args, **kwargs): + recipe = Recipe.objects.get(pk=kwargs['recipe_id']) + + self.queryset = self.create_package_list(recipe, kwargs['pid']) + self.queryset = self.queryset.order_by('name') + + def setup_columns(self, *args, **kwargs): + self.add_column(title="Package", + hideable=False, + orderable=True, + field_name="name") + + self.add_column(title="Package Version", + field_name="version", + hideable=False) + + self.add_column(title="Approx Size", + orderable=True, + static_data_name="size", + static_data_template="{% load projecttags %} \ + {{data.size|filtered_filesizeformat}}") + + self.add_column(title="License", + field_name="license", + orderable=True) + + + self.add_column(title="Dependencies", + static_data_name="dependencies", + static_data_template='\ + {% include "snippets/pkg_dependencies_popover.html" %}') + + self.add_column(title="Recipe", + field_name="recipe__name", + orderable=True, + hidden=True) + + self.add_column(title="Recipe version", + field_name="recipe__version", + hidden=True) + + +class SelectPackagesTable(PackagesTable): """ Table to display the packages to add and remove from an image """ def __init__(self, *args, **kwargs): @@ -637,29 +741,25 @@ class SelectPackagesTable(ToasterTable): self.static_context_extra['current_packages'] = \ cust_recipe.packages.values_list('pk', flat=True) + def get_context_data(self, **kwargs): + context = super(SelectPackagesTable, self).get_context_data(**kwargs) + custom_recipe = CustomImageRecipe.objects.get(pk=kwargs['recipe_id']) + + context['recipe'] = custom_recipe + context['approx_pkg_size'] = custom_recipe.package_set.aggregate(Sum('size')) + return context + + def setup_columns(self, *args, **kwargs): - self.add_column(title="Package", - hideable=False, - orderable=True, - field_name="name") - - self.add_column(title="Package Version", - field_name="version") - - self.add_column(title="Approx Size", - orderable=True, - static_data_name="size", - static_data_template="{% load projecttags %} \ - {{data.size|filtered_filesizeformat}}") - self.add_column(title="summary", - field_name="summary") + super(SelectPackagesTable, self).setup_columns(*args, **kwargs) self.add_column(title="Add | Remove", + hideable=False, help_text="Use the add and remove buttons to modify " "the package content of you custom image", static_data_name="add_rm_pkg_btn", static_data_template='{% include "pkg_add_rm_btn.html" %}', - static_data_template='{% include "pkg_add_rm_btn.html" %}' + filter_name="in_current_image" ) def setup_filters(self, *args, **kwargs): @@ -681,12 +781,11 @@ class SelectPackagesTable(ToasterTable): self.filter_not_in_image) ]) - def filter_in_image(self, count_only=False): + def filter_in_image(self): return self.queryset.filter( pk__in=self.static_context_extra['current_packages']) - - def filter_not_in_image(self, count_only=False): + def filter_not_in_image(self): return self.queryset.exclude( pk__in=self.static_context_extra['current_packages']) diff --git a/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html new file mode 100644 index 0000000000..a08409ac7f --- /dev/null +++ b/bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html @@ -0,0 +1,14 @@ +{# Popover that displays the dependences and sizes of a package 'data' used in the Packages table #} +{% with data.package_dependencies_source.count as dep_count %} +{% load projecttags %} +{% if dep_count %} + + {{dep_count}} + +{% endif %} +{% endwith %} diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py index 2164c4c8f7..969a29b228 100644 --- a/bitbake/lib/toaster/toastergui/urls.py +++ b/bitbake/lib/toaster/toastergui/urls.py @@ -109,13 +109,10 @@ urlpatterns = patterns('toastergui.views', tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"), name="newcustomimage"), - url(r'^project/(?P\d+)/layers/$', tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), name="projectlayers"), - - url(r'^project/(?P\d+)/layer/(?P\d+)$', 'layerdetails', name='layerdetails'), @@ -133,17 +130,20 @@ urlpatterns = patterns('toastergui.views', url(r'^project/(?P\d+)/customrecipe/(?P\d+)/selectpackages/$', - tables.SelectPackagesTable.as_view(template_name="generic-toastertable-page.html"), name="recipeselectpackages"), + tables.SelectPackagesTable.as_view(), name="recipeselectpackages"), url(r'^project/(?P\d+)/customrecipe/(?P\d+)$', - 'customrecipe', + tables.SelectPackagesTable.as_view(template_name="customrecipe.html"), name="customrecipe"), url(r'^project/(?P\d+)/customrecipe/(?P\d+)/download$', 'customrecipe_download', name="customrecipedownload"), + url(r'^project/(?P\d+)/recipe/(?P\d+)$', + tables.PackagesTable.as_view(template_name="recipedetails.html"), + name="recipedetails"), # typeahead api end points url(r'^xhr_typeahead/(?P\d+)/layers$', diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index b58f916d8c..f6f1a54597 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py @@ -2594,15 +2594,6 @@ if True: return(vars_managed,sorted(vars_fstypes),vars_blacklist) - def customrecipe(request, pid, recipe_id): - project = Project.objects.get(pk=pid) - context = {'project' : project, - 'projectlayers': [], - 'recipe' : CustomImageRecipe.objects.get(pk=recipe_id) - } - - return render(request, "customrecipe.html", context) - @_template_renderer("projectconf.html") def projectconf(request, pid):