Create a Packages table for use as the image details page. Change the SelectPackagesTable table to inherit from the Packages table. Remove the need for a separate view by adding the additional template context items to the Table's page context.
Signed-off-by: Michael Wood <[email protected]> --- bitbake/lib/toaster/toastergui/tables.py | 149 +++++++++++++++++---- .../snippets/pkg_dependencies_popover.html | 14 ++ bitbake/lib/toaster/toastergui/urls.py | 10 +- bitbake/lib/toaster/toastergui/views.py | 9 -- 4 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 6bbf0d7..7ce6651 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py @@ -21,8 +21,8 @@ from toastergui.widgets import ToasterTable from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project -from orm.models import CustomImageRecipe, Package -from django.db.models import Q, Max +from orm.models import CustomImageRecipe, Package, Target, Build +from django.db.models import Q, Max, Sum from django.conf.urls import url from django.core.urlresolvers import reverse from django.views.generic import TemplateView @@ -440,8 +440,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): @@ -459,22 +459,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 = ''' + <a href="{% url 'customrecipe' extra.pid data.id %}"> + {{data.package_set.all|length}} + </a>''' - approx_packages_template = '<a href="#imagedetails">{{data.packages.all|length}}</a>' self.add_column(title="Approx packages", static_data_name='approx_packages', static_data_template=approx_packages_template) - build_btn_template = '''<button data-recipe-name="{{data.name}}" + build_btn_template = ''' + <button data-recipe-name="{{data.name}}" class="btn btn-block build-recipe-btn" style="margin-top: 5px;" > - Build</button>''' + Build + </button>''' self.add_column(title="Build", hideable=False, @@ -497,12 +506,19 @@ class ImageRecipesTable(RecipesTable): def setup_columns(self, *args, **kwargs): + + name_link_template = ''' + <a href="{% url 'recipedetails' extra.pid data.pk %}">{{data.name}}</a> + ''' + 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) @@ -565,8 +581,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""" + + 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(ToasterTable): +class SelectPackagesTable(PackagesTable): """ Table to display the packages to add and remove from an image """ def __init__(self, *args, **kwargs): @@ -593,24 +697,20 @@ class SelectPackagesTable(ToasterTable): self.static_context_extra['current_packages'] = \ cust_recipe.packages.values_list('pk', flat=True) - def setup_columns(self, *args, **kwargs): - self.add_column(title="Package", - hideable=False, - orderable=True, - field_name="name") + def get_context_data(self, **kwargs): + context = super(SelectPackagesTable, self).get_context_data(**kwargs) + custom_recipe = CustomImageRecipe.objects.get(pk=kwargs['recipe_id']) - self.add_column(title="Package Version", - field_name="version") + context['recipe'] = custom_recipe + context['approx_pkg_size'] = custom_recipe.package_set.aggregate(Sum('size')) + return context - 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") + + def setup_columns(self, *args, **kwargs): + 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", @@ -637,11 +737,10 @@ 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 0000000..a08409a --- /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 %} + <a data-content="<ul class='unstyled'> + {% for dep in data.package_dependencies_source.all %} + <li>{{dep.depends_on.name}} {% if dep.depends_on.size > 0 %}({{dep.depends_on.size|filtered_filesizeformat}}){% endif %}</li> + {% endfor %} + </ul>" title="" class="btn" data-original-title=" + <strong>{{data.name}}</strong> dependencies - <strong>{{data.package_dependencies_source.get_total_source_deps_size.depends_on__size__sum|filtered_filesizeformat}}</strong>"> + {{dep_count}} +</a> +{% endif %} +{% endwith %} diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py index 50acb1b..36e5c38 100644 --- a/bitbake/lib/toaster/toastergui/urls.py +++ b/bitbake/lib/toaster/toastergui/urls.py @@ -103,13 +103,10 @@ urlpatterns = patterns('toastergui.views', tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"), name="newcustomimage"), - url(r'^project/(?P<pid>\d+)/layers/$', tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), name="projectlayers"), - - url(r'^project/(?P<pid>\d+)/layer/(?P<layerid>\d+)$', 'layerdetails', name='layerdetails'), @@ -127,17 +124,20 @@ urlpatterns = patterns('toastergui.views', url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipeid>\d+)/selectpackages/$', - tables.SelectPackagesTable.as_view(template_name="generic-toastertable-page.html"), name="recipeselectpackages"), + tables.SelectPackagesTable.as_view(), name="recipeselectpackages"), url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)$', - 'customrecipe', + tables.SelectPackagesTable.as_view(template_name="customrecipe.html"), name="customrecipe"), url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)/download$', 'customrecipe_download', name="customrecipedownload"), + url(r'^project/(?P<pid>\d+)/recipe/(?P<recipe_id>\d+)$', + tables.PackagesTable.as_view(template_name="recipedetails.html"), + name="recipedetails"), # typeahead api end points url(r'^xhr_typeahead/(?P<pid>\d+)/layers$', diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index c10b19f..53553a5 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py @@ -2886,15 +2886,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): -- 2.1.4 -- _______________________________________________ toaster mailing list [email protected] https://lists.yoctoproject.org/listinfo/toaster
