diff options
-rw-r--r-- | bitbake/lib/toaster/toastergui/tables.py | 149 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/snippets/pkg_dependencies_popover.html | 14 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/urls.py | 10 | ||||
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 9 |
4 files changed, 143 insertions, 39 deletions
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 @@ | |||
21 | 21 | ||
22 | from toastergui.widgets import ToasterTable | 22 | from toastergui.widgets import ToasterTable |
23 | from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project | 23 | from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project |
24 | from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task | 24 | from orm.models import CustomImageRecipe, Package, Target, Build, LogMessage, Task |
25 | from orm.models import ProjectTarget | 25 | from orm.models import ProjectTarget |
26 | from django.db.models import Q, Max, Count, When, Case, Value, IntegerField | 26 | from django.db.models import Q, Max, Count, When, Case, Value, IntegerField |
27 | from django.conf.urls import url | 27 | from django.conf.urls import url |
@@ -483,8 +483,8 @@ class CustomImagesTable(ToasterTable): | |||
483 | def get_context_data(self, **kwargs): | 483 | def get_context_data(self, **kwargs): |
484 | context = super(CustomImagesTable, self).get_context_data(**kwargs) | 484 | context = super(CustomImagesTable, self).get_context_data(**kwargs) |
485 | project = Project.objects.get(pk=kwargs['pid']) | 485 | project = Project.objects.get(pk=kwargs['pid']) |
486 | # TODO put project into the ToasterTable base class | ||
486 | context['project'] = project | 487 | context['project'] = project |
487 | context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project'])) | ||
488 | return context | 488 | return context |
489 | 489 | ||
490 | def setup_queryset(self, *args, **kwargs): | 490 | def setup_queryset(self, *args, **kwargs): |
@@ -502,22 +502,31 @@ class CustomImagesTable(ToasterTable): | |||
502 | 502 | ||
503 | self.add_column(title="Custom image", | 503 | self.add_column(title="Custom image", |
504 | hideable=False, | 504 | hideable=False, |
505 | orderable=True, | ||
506 | field_name="name", | ||
505 | static_data_name="name", | 507 | static_data_name="name", |
506 | static_data_template=name_link_template) | 508 | static_data_template=name_link_template) |
507 | 509 | ||
508 | self.add_column(title="Recipe file", | 510 | self.add_column(title="Recipe file", |
509 | static_data_name='recipe_file', | 511 | static_data_name='recipe_file', |
510 | static_data_template='') | 512 | static_data_template='', |
513 | field_name='local_path') | ||
514 | |||
515 | approx_packages_template = ''' | ||
516 | <a href="{% url 'customrecipe' extra.pid data.id %}"> | ||
517 | {{data.package_set.all|length}} | ||
518 | </a>''' | ||
511 | 519 | ||
512 | approx_packages_template = '<a href="#imagedetails">{{data.packages.all|length}}</a>' | ||
513 | self.add_column(title="Approx packages", | 520 | self.add_column(title="Approx packages", |
514 | static_data_name='approx_packages', | 521 | static_data_name='approx_packages', |
515 | static_data_template=approx_packages_template) | 522 | static_data_template=approx_packages_template) |
516 | 523 | ||
517 | 524 | ||
518 | build_btn_template = '''<button data-recipe-name="{{data.name}}" | 525 | build_btn_template = ''' |
526 | <button data-recipe-name="{{data.name}}" | ||
519 | class="btn btn-block build-recipe-btn" style="margin-top: 5px;" > | 527 | class="btn btn-block build-recipe-btn" style="margin-top: 5px;" > |
520 | Build</button>''' | 528 | Build |
529 | </button>''' | ||
521 | 530 | ||
522 | self.add_column(title="Build", | 531 | self.add_column(title="Build", |
523 | hideable=False, | 532 | hideable=False, |
@@ -540,12 +549,19 @@ class ImageRecipesTable(RecipesTable): | |||
540 | 549 | ||
541 | 550 | ||
542 | def setup_columns(self, *args, **kwargs): | 551 | def setup_columns(self, *args, **kwargs): |
552 | |||
553 | name_link_template = ''' | ||
554 | <a href="{% url 'recipedetails' extra.pid data.pk %}">{{data.name}}</a> | ||
555 | ''' | ||
556 | |||
543 | self.add_column(title="Image recipe", | 557 | self.add_column(title="Image recipe", |
544 | help_text="When you build an image recipe, you get an " | 558 | help_text="When you build an image recipe, you get an " |
545 | "image: a root file system you can" | 559 | "image: a root file system you can" |
546 | "deploy to a machine", | 560 | "deploy to a machine", |
547 | hideable=False, | 561 | hideable=False, |
548 | orderable=True, | 562 | orderable=True, |
563 | static_data_name="name", | ||
564 | static_data_template=name_link_template, | ||
549 | field_name="name") | 565 | field_name="name") |
550 | 566 | ||
551 | super(ImageRecipesTable, self).setup_columns(*args, **kwargs) | 567 | super(ImageRecipesTable, self).setup_columns(*args, **kwargs) |
@@ -609,8 +625,96 @@ class SoftwareRecipesTable(RecipesTable): | |||
609 | 625 | ||
610 | self.add_column(**RecipesTable.build_col) | 626 | self.add_column(**RecipesTable.build_col) |
611 | 627 | ||
628 | class PackagesTable(ToasterTable): | ||
629 | """ Table to display the packages in a recipe from it's last successful | ||
630 | build""" | ||
631 | |||
632 | def __init__(self, *args, **kwargs): | ||
633 | super(PackagesTable, self).__init__(*args, **kwargs) | ||
634 | self.title = "Packages included" | ||
635 | self.packages = None | ||
636 | self.default_orderby = "name" | ||
637 | |||
638 | def create_package_list(self, recipe, project_id): | ||
639 | """Creates a list of packages for the specified recipe by looking for | ||
640 | the last SUCCEEDED build of ther recipe""" | ||
641 | |||
642 | target = Target.objects.filter(Q(target=recipe.name) & | ||
643 | Q(build__project_id=project_id) & | ||
644 | Q(build__outcome=Build.SUCCEEDED) | ||
645 | ).last() | ||
646 | |||
647 | if target: | ||
648 | return target.build.package_set.all() | ||
649 | |||
650 | # Target/recipe never successfully built so empty queryset | ||
651 | return Package.objects.none() | ||
652 | |||
653 | def get_context_data(self, **kwargs): | ||
654 | """Context for rendering the sidebar and other items on the recipe | ||
655 | details page """ | ||
656 | context = super(PackagesTable, self).get_context_data(**kwargs) | ||
657 | |||
658 | recipe = Recipe.objects.get(pk=kwargs['recipe_id']) | ||
659 | project = Project.objects.get(pk=kwargs['pid']) | ||
660 | |||
661 | in_project = (recipe.layer_version.pk in | ||
662 | project.get_project_layer_versions(pk=True)) | ||
663 | |||
664 | packages = self.create_package_list(recipe, project.pk) | ||
665 | |||
666 | context.update({'project': project, | ||
667 | 'recipe' : recipe, | ||
668 | 'packages': packages, | ||
669 | 'approx_pkg_size' : packages.aggregate(Sum('size')), | ||
670 | 'in_project' : in_project, | ||
671 | }) | ||
672 | |||
673 | return context | ||
674 | |||
675 | def setup_queryset(self, *args, **kwargs): | ||
676 | recipe = Recipe.objects.get(pk=kwargs['recipe_id']) | ||
677 | |||
678 | self.queryset = self.create_package_list(recipe, kwargs['pid']) | ||
679 | self.queryset = self.queryset.order_by('name') | ||
680 | |||
681 | def setup_columns(self, *args, **kwargs): | ||
682 | self.add_column(title="Package", | ||
683 | hideable=False, | ||
684 | orderable=True, | ||
685 | field_name="name") | ||
686 | |||
687 | self.add_column(title="Package Version", | ||
688 | field_name="version", | ||
689 | hideable=False) | ||
690 | |||
691 | self.add_column(title="Approx Size", | ||
692 | orderable=True, | ||
693 | static_data_name="size", | ||
694 | static_data_template="{% load projecttags %} \ | ||
695 | {{data.size|filtered_filesizeformat}}") | ||
696 | |||
697 | self.add_column(title="License", | ||
698 | field_name="license", | ||
699 | orderable=True) | ||
700 | |||
701 | |||
702 | self.add_column(title="Dependencies", | ||
703 | static_data_name="dependencies", | ||
704 | static_data_template='\ | ||
705 | {% include "snippets/pkg_dependencies_popover.html" %}') | ||
706 | |||
707 | self.add_column(title="Recipe", | ||
708 | field_name="recipe__name", | ||
709 | orderable=True, | ||
710 | hidden=True) | ||
711 | |||
712 | self.add_column(title="Recipe version", | ||
713 | field_name="recipe__version", | ||
714 | hidden=True) | ||
715 | |||
612 | 716 | ||
613 | class SelectPackagesTable(ToasterTable): | 717 | class SelectPackagesTable(PackagesTable): |
614 | """ Table to display the packages to add and remove from an image """ | 718 | """ Table to display the packages to add and remove from an image """ |
615 | 719 | ||
616 | def __init__(self, *args, **kwargs): | 720 | def __init__(self, *args, **kwargs): |
@@ -637,29 +741,25 @@ class SelectPackagesTable(ToasterTable): | |||
637 | self.static_context_extra['current_packages'] = \ | 741 | self.static_context_extra['current_packages'] = \ |
638 | cust_recipe.packages.values_list('pk', flat=True) | 742 | cust_recipe.packages.values_list('pk', flat=True) |
639 | 743 | ||
640 | def setup_columns(self, *args, **kwargs): | 744 | def get_context_data(self, **kwargs): |
641 | self.add_column(title="Package", | 745 | context = super(SelectPackagesTable, self).get_context_data(**kwargs) |
642 | hideable=False, | 746 | custom_recipe = CustomImageRecipe.objects.get(pk=kwargs['recipe_id']) |
643 | orderable=True, | ||
644 | field_name="name") | ||
645 | 747 | ||
646 | self.add_column(title="Package Version", | 748 | context['recipe'] = custom_recipe |
647 | field_name="version") | 749 | context['approx_pkg_size'] = custom_recipe.package_set.aggregate(Sum('size')) |
750 | return context | ||
648 | 751 | ||
649 | self.add_column(title="Approx Size", | 752 | |
650 | orderable=True, | 753 | def setup_columns(self, *args, **kwargs): |
651 | static_data_name="size", | 754 | super(SelectPackagesTable, self).setup_columns(*args, **kwargs) |
652 | static_data_template="{% load projecttags %} \ | ||
653 | {{data.size|filtered_filesizeformat}}") | ||
654 | self.add_column(title="summary", | ||
655 | field_name="summary") | ||
656 | 755 | ||
657 | self.add_column(title="Add | Remove", | 756 | self.add_column(title="Add | Remove", |
757 | hideable=False, | ||
658 | help_text="Use the add and remove buttons to modify " | 758 | help_text="Use the add and remove buttons to modify " |
659 | "the package content of you custom image", | 759 | "the package content of you custom image", |
660 | static_data_name="add_rm_pkg_btn", | 760 | static_data_name="add_rm_pkg_btn", |
661 | static_data_template='{% include "pkg_add_rm_btn.html" %}', | 761 | static_data_template='{% include "pkg_add_rm_btn.html" %}', |
662 | static_data_template='{% include "pkg_add_rm_btn.html" %}' | 762 | filter_name="in_current_image" |
663 | ) | 763 | ) |
664 | 764 | ||
665 | def setup_filters(self, *args, **kwargs): | 765 | def setup_filters(self, *args, **kwargs): |
@@ -681,12 +781,11 @@ class SelectPackagesTable(ToasterTable): | |||
681 | self.filter_not_in_image) | 781 | self.filter_not_in_image) |
682 | ]) | 782 | ]) |
683 | 783 | ||
684 | def filter_in_image(self, count_only=False): | 784 | def filter_in_image(self): |
685 | return self.queryset.filter( | 785 | return self.queryset.filter( |
686 | pk__in=self.static_context_extra['current_packages']) | 786 | pk__in=self.static_context_extra['current_packages']) |
687 | 787 | ||
688 | 788 | def filter_not_in_image(self): | |
689 | def filter_not_in_image(self, count_only=False): | ||
690 | return self.queryset.exclude( | 789 | return self.queryset.exclude( |
691 | pk__in=self.static_context_extra['current_packages']) | 790 | pk__in=self.static_context_extra['current_packages']) |
692 | 791 | ||
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 @@ | |||
1 | {# Popover that displays the dependences and sizes of a package 'data' used in the Packages table #} | ||
2 | {% with data.package_dependencies_source.count as dep_count %} | ||
3 | {% load projecttags %} | ||
4 | {% if dep_count %} | ||
5 | <a data-content="<ul class='unstyled'> | ||
6 | {% for dep in data.package_dependencies_source.all %} | ||
7 | <li>{{dep.depends_on.name}} {% if dep.depends_on.size > 0 %}({{dep.depends_on.size|filtered_filesizeformat}}){% endif %}</li> | ||
8 | {% endfor %} | ||
9 | </ul>" title="" class="btn" data-original-title=" | ||
10 | <strong>{{data.name}}</strong> dependencies - <strong>{{data.package_dependencies_source.get_total_source_deps_size.depends_on__size__sum|filtered_filesizeformat}}</strong>"> | ||
11 | {{dep_count}} | ||
12 | </a> | ||
13 | {% endif %} | ||
14 | {% 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', | |||
109 | tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"), | 109 | tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"), |
110 | name="newcustomimage"), | 110 | name="newcustomimage"), |
111 | 111 | ||
112 | |||
113 | url(r'^project/(?P<pid>\d+)/layers/$', | 112 | url(r'^project/(?P<pid>\d+)/layers/$', |
114 | tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), | 113 | tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), |
115 | name="projectlayers"), | 114 | name="projectlayers"), |
116 | 115 | ||
117 | |||
118 | |||
119 | url(r'^project/(?P<pid>\d+)/layer/(?P<layerid>\d+)$', | 116 | url(r'^project/(?P<pid>\d+)/layer/(?P<layerid>\d+)$', |
120 | 'layerdetails', name='layerdetails'), | 117 | 'layerdetails', name='layerdetails'), |
121 | 118 | ||
@@ -133,17 +130,20 @@ urlpatterns = patterns('toastergui.views', | |||
133 | 130 | ||
134 | 131 | ||
135 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipeid>\d+)/selectpackages/$', | 132 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipeid>\d+)/selectpackages/$', |
136 | tables.SelectPackagesTable.as_view(template_name="generic-toastertable-page.html"), name="recipeselectpackages"), | 133 | tables.SelectPackagesTable.as_view(), name="recipeselectpackages"), |
137 | 134 | ||
138 | 135 | ||
139 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)$', | 136 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)$', |
140 | 'customrecipe', | 137 | tables.SelectPackagesTable.as_view(template_name="customrecipe.html"), |
141 | name="customrecipe"), | 138 | name="customrecipe"), |
142 | 139 | ||
143 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)/download$', | 140 | url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)/download$', |
144 | 'customrecipe_download', | 141 | 'customrecipe_download', |
145 | name="customrecipedownload"), | 142 | name="customrecipedownload"), |
146 | 143 | ||
144 | url(r'^project/(?P<pid>\d+)/recipe/(?P<recipe_id>\d+)$', | ||
145 | tables.PackagesTable.as_view(template_name="recipedetails.html"), | ||
146 | name="recipedetails"), | ||
147 | 147 | ||
148 | # typeahead api end points | 148 | # typeahead api end points |
149 | url(r'^xhr_typeahead/(?P<pid>\d+)/layers$', | 149 | 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 b58f916d8c..f6f1a54597 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -2594,15 +2594,6 @@ if True: | |||
2594 | 2594 | ||
2595 | return(vars_managed,sorted(vars_fstypes),vars_blacklist) | 2595 | return(vars_managed,sorted(vars_fstypes),vars_blacklist) |
2596 | 2596 | ||
2597 | def customrecipe(request, pid, recipe_id): | ||
2598 | project = Project.objects.get(pk=pid) | ||
2599 | context = {'project' : project, | ||
2600 | 'projectlayers': [], | ||
2601 | 'recipe' : CustomImageRecipe.objects.get(pk=recipe_id) | ||
2602 | } | ||
2603 | |||
2604 | return render(request, "customrecipe.html", context) | ||
2605 | |||
2606 | @_template_renderer("projectconf.html") | 2597 | @_template_renderer("projectconf.html") |
2607 | def projectconf(request, pid): | 2598 | def projectconf(request, pid): |
2608 | 2599 | ||