diff options
| -rw-r--r-- | bitbake/lib/toaster/toastergui/tables.py | 226 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/templates/projects-toastertable.html | 36 | ||||
| -rw-r--r-- | bitbake/lib/toaster/toastergui/urls.py | 6 | 
3 files changed, 265 insertions, 3 deletions
| diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 38088201d8..e5cab48c48 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py | |||
| @@ -21,13 +21,12 @@ | |||
| 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 | 24 | from orm.models import CustomImageRecipe, Package, Build | 
| 25 | from django.db.models import Q, Max | 25 | from django.db.models import Q, Max, Count | 
| 26 | from django.conf.urls import url | 26 | from django.conf.urls import url | 
| 27 | from django.core.urlresolvers import reverse | 27 | from django.core.urlresolvers import reverse | 
| 28 | from django.views.generic import TemplateView | 28 | from django.views.generic import TemplateView | 
| 29 | 29 | ||
| 30 | |||
| 31 | class ProjectFiltersMixin(object): | 30 | class ProjectFiltersMixin(object): | 
| 32 | """Common mixin for recipe, machine in project filters""" | 31 | """Common mixin for recipe, machine in project filters""" | 
| 33 | 32 | ||
| @@ -633,3 +632,224 @@ class SelectPackagesTable(ToasterTable): | |||
| 633 | "the package content of you custom image", | 632 | "the package content of you custom image", | 
| 634 | static_data_name="add_rm_pkg_btn", | 633 | static_data_name="add_rm_pkg_btn", | 
| 635 | static_data_template='{% include "pkg_add_rm_btn.html" %}') | 634 | static_data_template='{% include "pkg_add_rm_btn.html" %}') | 
| 635 | |||
| 636 | class ProjectsTable(ToasterTable): | ||
| 637 | """Table of projects in Toaster""" | ||
| 638 | |||
| 639 | def __init__(self, *args, **kwargs): | ||
| 640 | super(ProjectsTable, self).__init__(*args, **kwargs) | ||
| 641 | self.default_orderby = 'updated' | ||
| 642 | self.title = 'All projects' | ||
| 643 | self.static_context_extra['Build'] = Build | ||
| 644 | |||
| 645 | def get_context_data(self, **kwargs): | ||
| 646 | return super(ProjectsTable, self).get_context_data(**kwargs) | ||
| 647 | |||
| 648 | def setup_queryset(self, *args, **kwargs): | ||
| 649 | queryset = Project.objects.all() | ||
| 650 | |||
| 651 | # annotate each project with its number of builds | ||
| 652 | queryset = queryset.annotate(num_builds=Count('build')) | ||
| 653 | |||
| 654 | # exclude the command line builds project if it has no builds | ||
| 655 | q_default_with_builds = Q(is_default=True) & Q(num_builds__gt=0) | ||
| 656 | queryset = queryset.filter(Q(is_default=False) | | ||
| 657 | q_default_with_builds) | ||
| 658 | |||
| 659 | # order rows | ||
| 660 | queryset = queryset.order_by(self.default_orderby) | ||
| 661 | |||
| 662 | self.queryset = queryset | ||
| 663 | |||
| 664 | # columns: last activity on (updated) - DEFAULT, project (name), release, machine, number of builds, last build outcome, recipe (name), errors, warnings, image files | ||
| 665 | def setup_columns(self, *args, **kwargs): | ||
| 666 | name_template = ''' | ||
| 667 | {% load project_url_tag %} | ||
| 668 | <span data-project-field="name"> | ||
| 669 | <a href="{% project_url data %}"> | ||
| 670 | {{data.name}} | ||
| 671 | </a> | ||
| 672 | </span> | ||
| 673 | ''' | ||
| 674 | |||
| 675 | last_activity_on_template = ''' | ||
| 676 | {% load project_url_tag %} | ||
| 677 | <span data-project-field="updated"> | ||
| 678 | <a href="{% project_url data %}"> | ||
| 679 | {{data.updated | date:"d/m/y H:i"}} | ||
| 680 | </a> | ||
| 681 | </span> | ||
| 682 | ''' | ||
| 683 | |||
| 684 | release_template = ''' | ||
| 685 | <span data-project-field="release"> | ||
| 686 | {% if data.release %} | ||
| 687 | <a href="{% url 'project' data.id %}#project-details"> | ||
| 688 | {{data.release.name}} | ||
| 689 | </a> | ||
| 690 | {% elif data.is_default %} | ||
| 691 | <span class="muted">Not applicable</span> | ||
| 692 | <i class="icon-question-sign get-help hover-help" | ||
| 693 | data-original-title="This project does not have a release set. | ||
| 694 | It simply collects information about the builds you start from | ||
| 695 | the command line while Toaster is running" | ||
| 696 | style="visibility: hidden;"> | ||
| 697 | </i> | ||
| 698 | {% else %} | ||
| 699 | No release available | ||
| 700 | {% endif %} | ||
| 701 | </span> | ||
| 702 | ''' | ||
| 703 | |||
| 704 | machine_template = ''' | ||
| 705 | <span data-project-field="machine"> | ||
| 706 | {% if data.is_default %} | ||
| 707 | <span class="muted">Not applicable</span> | ||
| 708 | <i class="icon-question-sign get-help hover-help" | ||
| 709 | data-original-title="This project does not have a machine | ||
| 710 | set. It simply collects information about the builds you | ||
| 711 | start from the command line while Toaster is running" | ||
| 712 | style="visibility: hidden;"></i> | ||
| 713 | {% else %} | ||
| 714 | <a href="{% url 'project' data.id %}#machine-distro"> | ||
| 715 | {{data.get_current_machine_name}} | ||
| 716 | </a> | ||
| 717 | {% endif %} | ||
| 718 | </span> | ||
| 719 | ''' | ||
| 720 | |||
| 721 | number_of_builds_template = ''' | ||
| 722 | {% if data.get_number_of_builds > 0 %} | ||
| 723 | <a href="{% url 'projectbuilds' data.id %}"> | ||
| 724 | {{data.get_number_of_builds}} | ||
| 725 | </a> | ||
| 726 | {% else %} | ||
| 727 | <span class="muted">0</span> | ||
| 728 | {% endif %} | ||
| 729 | ''' | ||
| 730 | |||
| 731 | last_build_outcome_template = ''' | ||
| 732 | {% if data.get_number_of_builds > 0 %} | ||
| 733 | <a href="{% url 'builddashboard' data.get_last_build_id %}"> | ||
| 734 | {% if data.get_last_outcome == extra.Build.SUCCEEDED %} | ||
| 735 | <i class="icon-ok-sign success"></i> | ||
| 736 | {% elif data.get_last_outcome == extra.Build.FAILED %} | ||
| 737 | <i class="icon-minus-sign error"></i> | ||
| 738 | {% endif %} | ||
| 739 | </a> | ||
| 740 | {% endif %} | ||
| 741 | ''' | ||
| 742 | |||
| 743 | recipe_template = ''' | ||
| 744 | {% if data.get_number_of_builds > 0 %} | ||
| 745 | <a href="{% url "builddashboard" data.get_last_build_id %}"> | ||
| 746 | {{data.get_last_target}} | ||
| 747 | </a> | ||
| 748 | {% endif %} | ||
| 749 | ''' | ||
| 750 | |||
| 751 | errors_template = ''' | ||
| 752 | {% if data.get_number_of_builds > 0 %} | ||
| 753 | <a class="errors.count error" | ||
| 754 | href="{% url "builddashboard" data.get_last_build_id %}#errors"> | ||
| 755 | {{data.get_last_errors}} error{{data.get_last_errors | pluralize}} | ||
| 756 | </a> | ||
| 757 | {% endif %} | ||
| 758 | ''' | ||
| 759 | |||
| 760 | warnings_template = ''' | ||
| 761 | {% if data.get_number_of_builds > 0 %} | ||
| 762 | <a class="warnings.count warning" | ||
| 763 | href="{% url "builddashboard" data.get_last_build_id %}#warnings"> | ||
| 764 | {{data.get_last_warnings}} warning{{data.get_last_warnings | pluralize}} | ||
| 765 | </a> | ||
| 766 | {% endif %} | ||
| 767 | ''' | ||
| 768 | |||
| 769 | image_files_template = ''' | ||
| 770 | {% load projecttags %} | ||
| 771 | {% if data.get_number_of_builds > 0 and data.get_last_outcome == extra.Build.SUCCEEDED %} | ||
| 772 | <a href="{% url "builddashboard" data.get_last_build_id %}#images"> | ||
| 773 | {{fstypes | get_dict_value:data.id}} | ||
| 774 | </a> | ||
| 775 | {% endif %} | ||
| 776 | ''' | ||
| 777 | |||
| 778 | self.add_column(title='Project', | ||
| 779 | hideable=False, | ||
| 780 | orderable=True, | ||
| 781 | static_data_name='name', | ||
| 782 | static_data_template=name_template) | ||
| 783 | |||
| 784 | self.add_column(title='Last activity on', | ||
| 785 | help_text='Starting date and time of the \ | ||
| 786 | last project build. If the project has no \ | ||
| 787 | builds, this shows the date the project was \ | ||
| 788 | created.', | ||
| 789 | hideable=True, | ||
| 790 | orderable=True, | ||
| 791 | static_data_name='updated', | ||
| 792 | static_data_template=last_activity_on_template) | ||
| 793 | |||
| 794 | self.add_column(title='Release', | ||
| 795 | help_text='The version of the build system used by \ | ||
| 796 | the project', | ||
| 797 | hideable=False, | ||
| 798 | orderable=True, | ||
| 799 | static_data_name='release', | ||
| 800 | static_data_template=release_template) | ||
| 801 | |||
| 802 | self.add_column(title='Machine', | ||
| 803 | help_text='The hardware currently selected for the \ | ||
| 804 | project', | ||
| 805 | hideable=False, | ||
| 806 | orderable=False, | ||
| 807 | static_data_name='machine', | ||
| 808 | static_data_template=machine_template) | ||
| 809 | |||
| 810 | self.add_column(title='Number of builds', | ||
| 811 | help_text='The number of builds which have been run \ | ||
| 812 | for the project', | ||
| 813 | hideable=True, | ||
| 814 | orderable=False, | ||
| 815 | static_data_name='number_of_builds', | ||
| 816 | static_data_template=number_of_builds_template) | ||
| 817 | |||
| 818 | self.add_column(title='Last build outcome', | ||
| 819 | help_text='Indicates whether the last project build \ | ||
| 820 | completed successfully or failed', | ||
| 821 | hideable=True, | ||
| 822 | orderable=False, | ||
| 823 | static_data_name='last_build_outcome', | ||
| 824 | static_data_template=last_build_outcome_template) | ||
| 825 | |||
| 826 | self.add_column(title='Recipe', | ||
| 827 | help_text='The last recipe which was built in this \ | ||
| 828 | project', | ||
| 829 | hideable=True, | ||
| 830 | orderable=False, | ||
| 831 | static_data_name='recipe_name', | ||
| 832 | static_data_template=recipe_template) | ||
| 833 | |||
| 834 | self.add_column(title='Errors', | ||
| 835 | help_text='The number of errors encountered during \ | ||
| 836 | the last project build (if any)', | ||
| 837 | hideable=True, | ||
| 838 | orderable=False, | ||
| 839 | static_data_name='errors', | ||
| 840 | static_data_template=errors_template) | ||
| 841 | |||
| 842 | self.add_column(title='Warnings', | ||
| 843 | help_text='The number of warnings encountered during \ | ||
| 844 | the last project build (if any)', | ||
| 845 | hideable=True, | ||
| 846 | orderable=False, | ||
| 847 | static_data_name='warnings', | ||
| 848 | static_data_template=warnings_template) | ||
| 849 | |||
| 850 | self.add_column(title='Image files', | ||
| 851 | help_text='', | ||
| 852 | hideable=True, | ||
| 853 | orderable=False, | ||
| 854 | static_data_name='image_files', | ||
| 855 | static_data_template=image_files_template) | ||
| diff --git a/bitbake/lib/toaster/toastergui/templates/projects-toastertable.html b/bitbake/lib/toaster/toastergui/templates/projects-toastertable.html new file mode 100644 index 0000000000..5814f32d06 --- /dev/null +++ b/bitbake/lib/toaster/toastergui/templates/projects-toastertable.html | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | {% extends 'base.html' %} | ||
| 2 | |||
| 3 | {% block title %} All projects - Toaster {% endblock %} | ||
| 4 | |||
| 5 | {% block pagecontent %} | ||
| 6 | |||
| 7 | <div class="page-header top-air"> | ||
| 8 | <h1 data-role="page-title"></h1> | ||
| 9 | </div> | ||
| 10 | |||
| 11 | {% url 'projects' as xhr_table_url %} | ||
| 12 | {% include 'toastertable.html' %} | ||
| 13 | |||
| 14 | <script> | ||
| 15 | $(document).ready(function () { | ||
| 16 | var tableElt = $("#{{table_name}}"); | ||
| 17 | var titleElt = $("[data-role='page-title']"); | ||
| 18 | |||
| 19 | tableElt.on("table-done", function (e, total, tableParams) { | ||
| 20 | var title = "All projects"; | ||
| 21 | |||
| 22 | if (tableParams.search || tableParams.filter) { | ||
| 23 | if (total === 0) { | ||
| 24 | title = "No projects found"; | ||
| 25 | } | ||
| 26 | else if (total > 0) { | ||
| 27 | title = total + " project" + (total > 1 ? 's' : '') + " found"; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | titleElt.text(title); | ||
| 32 | }); | ||
| 33 | }); | ||
| 34 | </script> | ||
| 35 | |||
| 36 | {% endblock %} | ||
| diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py index 2bf2d99ae7..da97a31133 100644 --- a/bitbake/lib/toaster/toastergui/urls.py +++ b/bitbake/lib/toaster/toastergui/urls.py | |||
| @@ -75,8 +75,14 @@ urlpatterns = patterns('toastergui.views', | |||
| 75 | url(r'^newproject/$', 'newproject', name='newproject'), | 75 | url(r'^newproject/$', 'newproject', name='newproject'), | 
| 76 | 76 | ||
| 77 | 77 | ||
| 78 | # TODO remove when new toaster table is ready | ||
| 78 | url(r'^projects/$', 'projects', name='all-projects'), | 79 | url(r'^projects/$', 'projects', name='all-projects'), | 
| 79 | 80 | ||
| 81 | # TODO move to /projects/ when new toaster table is ready | ||
| 82 | url(r'^projects-new/$', | ||
| 83 | tables.ProjectsTable.as_view(template_name="projects-toastertable.html"), | ||
| 84 | name='all-projects-new'), | ||
| 85 | |||
| 80 | url(r'^project/(?P<pid>\d+)/$', 'project', name='project'), | 86 | url(r'^project/(?P<pid>\d+)/$', 'project', name='project'), | 
| 81 | url(r'^project/(?P<pid>\d+)/configuration$', 'projectconf', name='projectconf'), | 87 | url(r'^project/(?P<pid>\d+)/configuration$', 'projectconf', name='projectconf'), | 
| 82 | url(r'^project/(?P<pid>\d+)/builds/$', 'projectbuilds', name='projectbuilds'), | 88 | url(r'^project/(?P<pid>\d+)/builds/$', 'projectbuilds', name='projectbuilds'), | 
