From 41c92fae147c14e863b24e8ee05a0c8930ae6217 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 22 Aug 2023 17:53:24 +0200 Subject: [PATCH 01/12] Refactor progress bar and take sets and conditions into account (#596) --- rdmo/projects/models/project.py | 22 ++------ rdmo/projects/progress.py | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 rdmo/projects/progress.py diff --git a/rdmo/projects/models/project.py b/rdmo/projects/models/project.py index eae2128fe2..48d2acc477 100644 --- a/rdmo/projects/models/project.py +++ b/rdmo/projects/models/project.py @@ -2,7 +2,6 @@ from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.db import models -from django.db.models import Exists, OuterRef from django.db.models.signals import pre_delete from django.dispatch import receiver from django.urls import reverse @@ -12,12 +11,12 @@ from mptt.models import MPTTModel, TreeForeignKey from rdmo.core.models import Model -from rdmo.domain.models import Attribute -from rdmo.questions.models import Catalog, Question +from rdmo.questions.models import Catalog from rdmo.tasks.models import Task from rdmo.views.models import View from ..managers import ProjectManager +from ..progress import get_progress class Project(MPTTModel, Model): @@ -88,22 +87,7 @@ def clean(self): @property def progress(self): - # create a queryset for the attributes of the catalog for this project - # the subquery is used to query only attributes which have a question in the catalog, which is not optional - questions = Question.objects.filter_by_catalog(self.catalog) \ - .filter(attribute_id=OuterRef('pk')).exclude(is_optional=True) - attributes = Attribute.objects.annotate(active=Exists(questions)).filter(active=True).distinct() - - # query the total number of attributes from the qs above - total = attributes.count() - - # query all current values with attributes from the qs above, but where the text, option, or file field is set, - # and count only one value per attribute - values = self.values.filter(snapshot=None) \ - .filter(attribute__in=attributes) \ - .exclude((models.Q(text='') | models.Q(text=None)) & models.Q(option=None) & - (models.Q(file='') | models.Q(file=None))) \ - .distinct().values('attribute').count() + values, total = get_progress(self) try: ratio = values / total diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py new file mode 100644 index 0000000000..b058879a95 --- /dev/null +++ b/rdmo/projects/progress.py @@ -0,0 +1,90 @@ +from collections import defaultdict + +from django.db.models import Exists, OuterRef, Q + +from rdmo.conditions.models import Condition +from rdmo.questions.models import Catalog, Section, Page, QuestionSet, Question + + +def get_progress(project, snapshot=None): + # get all values for this project and snapshot + project_values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') + + # get all conditions for this catalog + pages_conditions_subquery = Page.objects.filter_by_catalog(project.catalog).filter(conditions=OuterRef('pk')) + questionsets_conditions_subquery = QuestionSet.objects.filter_by_catalog(project.catalog).filter(conditions=OuterRef('pk')) + questions_conditions_subquery = Question.objects.filter_by_catalog(project.catalog).filter(conditions=OuterRef('pk')) + + catalog_conditions = Condition.objects.annotate(has_page=Exists(pages_conditions_subquery)) \ + .annotate(has_questionset=Exists(questionsets_conditions_subquery)) \ + .annotate(has_question=Exists(questions_conditions_subquery)) \ + .filter(Q(has_page=True) | Q(has_questionset=True) | Q(has_question=True)) \ + .distinct().select_related('source', 'target_option') + + # evaluate conditions + conditions = set() + for condition in catalog_conditions: + if condition.resolve(project_values): + conditions.add(condition.id) + + # compute sets from values + sets = defaultdict(list) + for attribute, set_index in project_values.values_list('attribute', 'set_index').distinct(): + sets[attribute].append(set_index) + + # count the total number of questions, taking sets and conditions into account + total_count, attributes = count_questions(project.catalog, sets, conditions) + + # filter the project values for the counted questions and exclude empty values + values_count = project_values.filter(attribute__in=attributes) \ + .exclude((Q(text='') | Q(text=None)) & Q(option=None) & + (Q(file='') | Q(file=None))) \ + .count() + + return values_count, total_count + + +def count_questions(parent_element, sets, conditions): + count = 0 + attributes = [] + + for element in parent_element.elements: + if isinstance(element, (Catalog, Section)): + element_count, element_attributes = count_questions(element, sets, conditions) + attributes += element_attributes + count += element_count + else: + element_conditions = set(condition.id for condition in element.conditions.all()) + if not element_conditions or element_conditions.intersection(conditions): + if isinstance(element, Question): + attributes.append(element.attribute) + count += 1 + else: + if element.attribute: + attributes.append(element.attribute) + + element_count, element_attributes = count_questions(element, sets, conditions) + set_count = count_sets(element, sets) + if set_count > 0: + count += element_count * set_count + attributes += element_attributes + + return count, attributes + + +def count_sets(parent_element, sets): + if parent_element.is_collection: + if parent_element.attribute: + count = len(sets[parent_element.attribute_id]) + else: + count = 0 + else: + count = 1 + + for element in parent_element.elements: + if isinstance(element, Question): + element_count = len(sets[element.attribute_id]) + if element_count > count: + count = element_count + + return count From e8bb565b1fd187fe13314eb6bbef2a7480e2157e Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 22 Aug 2023 18:01:13 +0200 Subject: [PATCH 02/12] Check is_optional for progress bar (again) --- rdmo/projects/progress.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py index b058879a95..8e0693771a 100644 --- a/rdmo/projects/progress.py +++ b/rdmo/projects/progress.py @@ -57,8 +57,9 @@ def count_questions(parent_element, sets, conditions): element_conditions = set(condition.id for condition in element.conditions.all()) if not element_conditions or element_conditions.intersection(conditions): if isinstance(element, Question): - attributes.append(element.attribute) - count += 1 + if not element.is_optional: + attributes.append(element.attribute) + count += 1 else: if element.attribute: attributes.append(element.attribute) From 39965a2076adf278131eaa3b483cd808ffc39b54 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 24 Aug 2023 16:51:07 +0200 Subject: [PATCH 03/12] Add progress_count and progress_total to Project model, compute on POST when using the API and add progress to the project overview (#488) --- .eslintrc.js | 2 +- rdmo/core/static/core/js/core.js | 7 +++++ .../migrations/0059_project_progress.py | 23 ++++++++++++++++ rdmo/projects/models/project.py | 26 +++++++------------ rdmo/projects/progress.py | 2 +- .../projects/js/project_questions/services.js | 6 ++--- .../projects/project_questions_progress.html | 4 +-- .../projects/templates/projects/projects.html | 4 +++ .../templates/projects/site_projects.html | 6 ++++- rdmo/projects/templatetags/projects_tags.py | 13 ++++++++++ rdmo/projects/viewsets.py | 22 +++++++++++++--- 11 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 rdmo/projects/migrations/0059_project_progress.py diff --git a/.eslintrc.js b/.eslintrc.js index 1419f87dfe..5d67f9ab04 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,5 +44,5 @@ module.exports = { 'react': { 'version': 'detect' } - } + }, } diff --git a/rdmo/core/static/core/js/core.js b/rdmo/core/static/core/js/core.js index 39f154fc2c..ee790f74fa 100644 --- a/rdmo/core/static/core/js/core.js +++ b/rdmo/core/static/core/js/core.js @@ -16,6 +16,13 @@ angular.module('core', ['ngResource']) method: 'PUT', params: {} }; + $resourceProvider.defaults.actions.postAction = { + method: 'POST', + params: { + id: '@id', + detail_action: '@detail_action' + } + }; }]) .filter('capitalize', function() { diff --git a/rdmo/projects/migrations/0059_project_progress.py b/rdmo/projects/migrations/0059_project_progress.py new file mode 100644 index 0000000000..abce63ea50 --- /dev/null +++ b/rdmo/projects/migrations/0059_project_progress.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-08-24 09:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0058_meta'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='progress_count', + field=models.IntegerField(help_text='The number of values for the progress bar.', null=True, verbose_name='Progress count'), + ), + migrations.AddField( + model_name='project', + name='progress_total', + field=models.IntegerField(help_text='The total number of expected values for the progress bar.', null=True, verbose_name='Progress total'), + ), + ] diff --git a/rdmo/projects/models/project.py b/rdmo/projects/models/project.py index 48d2acc477..d13b33a60a 100644 --- a/rdmo/projects/models/project.py +++ b/rdmo/projects/models/project.py @@ -16,7 +16,6 @@ from rdmo.views.models import View from ..managers import ProjectManager -from ..progress import get_progress class Project(MPTTModel, Model): @@ -64,6 +63,16 @@ class Project(MPTTModel, Model): verbose_name=_('Views'), help_text=_('The views that will be used for this project.') ) + progress_total = models.IntegerField( + null=True, + verbose_name=_('Progress total'), + help_text=_('The total number of expected values for the progress bar.') + ) + progress_count = models.IntegerField( + null=True, + verbose_name=_('Progress count'), + help_text=_('The number of values for the progress bar.') + ) class Meta: ordering = ('tree_id', 'level', 'title') @@ -85,21 +94,6 @@ def clean(self): 'parent': [_('A project may not be moved to be a child of itself or one of its descendants.')] }) - @property - def progress(self): - values, total = get_progress(self) - - try: - ratio = values / total - except ZeroDivisionError: - ratio = 0 - - return { - 'total': total, - 'values': values, - 'ratio': ratio - } - @property def catalog_uri(self): if self.catalog is not None: diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py index 8e0693771a..6271c11bbd 100644 --- a/rdmo/projects/progress.py +++ b/rdmo/projects/progress.py @@ -6,7 +6,7 @@ from rdmo.questions.models import Catalog, Section, Page, QuestionSet, Question -def get_progress(project, snapshot=None): +def compute_progress(project, snapshot=None): # get all values for this project and snapshot project_values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') diff --git a/rdmo/projects/static/projects/js/project_questions/services.js b/rdmo/projects/static/projects/js/project_questions/services.js index 92b09fc744..9d511bb26b 100644 --- a/rdmo/projects/static/projects/js/project_questions/services.js +++ b/rdmo/projects/static/projects/js/project_questions/services.js @@ -955,13 +955,11 @@ angular.module('project_questions') } } else { // update progress - resources.projects.get({ + resources.projects.postAction({ id: service.project.id, detail_action: 'progress' }, function(response) { - if (service.progress.values != response.values) { - service.progress = response - } + service.progress = response }); // check if we need to refresh the site diff --git a/rdmo/projects/templates/projects/project_questions_progress.html b/rdmo/projects/templates/projects/project_questions_progress.html index 9b66e61bb9..1ae56dad41 100644 --- a/rdmo/projects/templates/projects/project_questions_progress.html +++ b/rdmo/projects/templates/projects/project_questions_progress.html @@ -3,8 +3,8 @@
- {% blocktrans trimmed with values='{$ service.progress.values $}' total='{$ service.progress.total $}' %} - {{ values }} of {{ total }} + {% blocktrans trimmed with count='{$ service.progress.count $}' total='{$ service.progress.total $}' %} + {{ count }} of {{ total }} {% endblocktrans %}
diff --git a/rdmo/projects/templates/projects/projects.html b/rdmo/projects/templates/projects/projects.html index 0edfaace36..b2aed864a1 100644 --- a/rdmo/projects/templates/projects/projects.html +++ b/rdmo/projects/templates/projects/projects.html @@ -124,6 +124,7 @@

{% trans 'My Projects' %}

{% trans 'Name' %} + {% trans 'Progress' %} {% trans 'Role' %} {% trans 'Last changed' %} @@ -138,6 +139,9 @@

{% trans 'My Projects' %}

{{ project.title }} + + {% project_progress project %} + {{ project.role|projects_role }} diff --git a/rdmo/projects/templates/projects/site_projects.html b/rdmo/projects/templates/projects/site_projects.html index f2a8f49c23..5caba675ed 100644 --- a/rdmo/projects/templates/projects/site_projects.html +++ b/rdmo/projects/templates/projects/site_projects.html @@ -61,7 +61,8 @@

{% blocktrans trimmed with site=request.site %}All projects on {{ site }}{% - + + @@ -76,6 +77,9 @@

{% blocktrans trimmed with site=request.site %}All projects on {{ site }}{% {{ project.title }} +

diff --git a/rdmo/projects/templatetags/projects_tags.py b/rdmo/projects/templatetags/projects_tags.py index 390a79e4f8..9517c7cda0 100644 --- a/rdmo/projects/templatetags/projects_tags.py +++ b/rdmo/projects/templatetags/projects_tags.py @@ -18,6 +18,19 @@ def projects_indent(level): return mark_safe('' + string + '') +@register.simple_tag() +def project_progress(project): + if project.progress_count is None or project.progress_total is None: + return '' + + try: + ratio = project.progress_count / project.progress_total + except ZeroDivisionError: + ratio = 0 + + return f'{ratio:.0%}' + + @register.filter() @stringfilter def projects_role(role): diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index 257c510dc1..fba1c5e402 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -27,6 +27,7 @@ from .filters import SnapshotFilterBackend, ValueFilterBackend from .models import Continuation, Integration, Invite, Issue, Membership, Project, Snapshot, Value from .permissions import HasProjectPagePermission, HasProjectPermission, HasProjectsPermission +from .progress import compute_progress from .serializers.v1 import ( IntegrationSerializer, InviteSerializer, @@ -158,11 +159,26 @@ def options(self, request, pk=None): # if it didn't work return 404 raise NotFound() - @action(detail=True, permission_classes=(IsAuthenticated, )) + @action(detail=True, methods=['get', 'post'], permission_classes=(HasModelPermission | HasProjectPermission, )) def progress(self, request, pk=None): project = self.get_object() - project.catalog.prefetch_elements() - return Response(project.progress) + + if request.method == 'POST' or project.progress_count is None or project.progress_total is None: + # compute the progress and store + project.catalog.prefetch_elements() + project.progress_count, project.progress_total = compute_progress(project) + project.save() + + try: + ratio = project.progress_count / project.progress_total + except ZeroDivisionError: + ratio = 0 + + return Response({ + 'count': project.progress_count, + 'total': project.progress_total, + 'ratio': ratio + }) def perform_create(self, serializer): project = serializer.save(site=get_current_site(self.request)) From e1e2fc503334cbd2901f04daf299dfdcedebb954 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 24 Aug 2023 21:17:13 +0200 Subject: [PATCH 04/12] Add navigation action to ProjectViewSet and refactor navigation (#299, #424) --- rdmo/projects/progress.py | 95 +++++++++++++++---- rdmo/projects/serializers/v1/overview.py | 37 +------- rdmo/projects/serializers/v1/page.py | 1 + .../projects/js/project_questions/services.js | 37 +++----- .../project_questions_navigation.html | 15 ++- .../project_questions_navigation_help.html | 2 +- rdmo/projects/viewsets.py | 27 ++++-- 7 files changed, 125 insertions(+), 89 deletions(-) diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py index 6271c11bbd..1afe1049f4 100644 --- a/rdmo/projects/progress.py +++ b/rdmo/projects/progress.py @@ -6,10 +6,7 @@ from rdmo.questions.models import Catalog, Section, Page, QuestionSet, Question -def compute_progress(project, snapshot=None): - # get all values for this project and snapshot - project_values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') - +def resolve_conditions(project, values): # get all conditions for this catalog pages_conditions_subquery = Page.objects.filter_by_catalog(project.catalog).filter(conditions=OuterRef('pk')) questionsets_conditions_subquery = QuestionSet.objects.filter_by_catalog(project.catalog).filter(conditions=OuterRef('pk')) @@ -24,24 +21,88 @@ def compute_progress(project, snapshot=None): # evaluate conditions conditions = set() for condition in catalog_conditions: - if condition.resolve(project_values): + if condition.resolve(values): conditions.add(condition.id) - # compute sets from values + # return all true conditions for this project + return conditions + + +def compute_sets(values): sets = defaultdict(list) - for attribute, set_index in project_values.values_list('attribute', 'set_index').distinct(): + for attribute, set_index in values.values_list('attribute', 'set_index').distinct(): sets[attribute].append(set_index) + return sets + + +def compute_navigation(section, project, snapshot=None): + # get all values for this project and snapshot + values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') + + # get true conditions + conditions = resolve_conditions(project, values) + + # compute sets from values + sets = compute_sets(values) + + # query non empty values + values_list = values.exclude((Q(text='') | Q(text=None)) & Q(option=None) & + (Q(file='') | Q(file=None))) \ + .values_list('attribute', 'set_index').distinct() \ + .values_list('attribute', flat=True) + + navigation = [] + for catalog_section in project.catalog.elements: + navigation_section = { + 'id': catalog_section.id, + 'title': catalog_section.title, + 'first': catalog_section.elements[0].id if section.elements else None + } + if catalog_section.id == section.id: + navigation_section['pages'] = [] + for page in catalog_section.elements: + pages_conditions = set(page.id for page in page.conditions.all()) + show = bool(not pages_conditions or pages_conditions.intersection(conditions)) + + # count the total number of questions, taking sets and conditions into account + total, attributes = count_questions(page, sets, conditions) + + # filter the project values for the counted questions and exclude empty values + count = len(tuple(filter(lambda attribute: attribute in attributes, values_list))) + + navigation_section['pages'].append({ + 'id': page.id, + 'title': page.title, + 'show': show, + 'count': count, + 'total': total + }) + + navigation.append(navigation_section) + + return navigation + + +def compute_progress(project, snapshot=None): + # get all values for this project and snapshot + values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') + + # get true conditions + conditions = resolve_conditions(project, values) + + # compute sets from values + sets = compute_sets(values) # count the total number of questions, taking sets and conditions into account - total_count, attributes = count_questions(project.catalog, sets, conditions) + total, attributes = count_questions(project.catalog, sets, conditions) # filter the project values for the counted questions and exclude empty values - values_count = project_values.filter(attribute__in=attributes) \ - .exclude((Q(text='') | Q(text=None)) & Q(option=None) & - (Q(file='') | Q(file=None))) \ - .count() + count = values.filter(attribute_id__in=attributes) \ + .exclude((Q(text='') | Q(text=None)) & Q(option=None) & + (Q(file='') | Q(file=None))) \ + .values_list('attribute', 'set_index').distinct().count() - return values_count, total_count + return count, total def count_questions(parent_element, sets, conditions): @@ -58,11 +119,11 @@ def count_questions(parent_element, sets, conditions): if not element_conditions or element_conditions.intersection(conditions): if isinstance(element, Question): if not element.is_optional: - attributes.append(element.attribute) + attributes.append(element.attribute_id) count += 1 else: - if element.attribute: - attributes.append(element.attribute) + if element.attribute_id: + attributes.append(element.attribute_id) element_count, element_attributes = count_questions(element, sets, conditions) set_count = count_sets(element, sets) @@ -75,7 +136,7 @@ def count_questions(parent_element, sets, conditions): def count_sets(parent_element, sets): if parent_element.is_collection: - if parent_element.attribute: + if parent_element.attribute_id: count = len(sets[parent_element.attribute_id]) else: count = 0 diff --git a/rdmo/projects/serializers/v1/overview.py b/rdmo/projects/serializers/v1/overview.py index a0428b9836..cc5cfdf3e2 100644 --- a/rdmo/projects/serializers/v1/overview.py +++ b/rdmo/projects/serializers/v1/overview.py @@ -1,51 +1,18 @@ from rest_framework import serializers from rdmo.projects.models import Project -from rdmo.questions.models import Catalog, Page, Section - - -class PageSerializer(serializers.ModelSerializer): - - class Meta: - model = Page - fields = ( - 'id', - 'title', - 'has_conditions' - ) - - -class SectionSerializer(serializers.ModelSerializer): - - pages = serializers.SerializerMethodField() - - class Meta: - model = Section - fields = ( - 'id', - 'title', - 'pages' - ) - - def get_pages(self, obj): - return PageSerializer(obj.elements, many=True, read_only=True).data +from rdmo.questions.models import Catalog class CatalogSerializer(serializers.ModelSerializer): - sections = serializers.SerializerMethodField() - class Meta: model = Catalog fields = ( 'id', - 'title', - 'sections' + 'title' ) - def get_sections(self, obj): - return SectionSerializer(obj.elements, many=True, read_only=True).data - class ProjectOverviewSerializer(serializers.ModelSerializer): diff --git a/rdmo/projects/serializers/v1/page.py b/rdmo/projects/serializers/v1/page.py index 49fe49fd03..de51adeaa9 100644 --- a/rdmo/projects/serializers/v1/page.py +++ b/rdmo/projects/serializers/v1/page.py @@ -181,6 +181,7 @@ def get_section(self, obj): return { 'id': section.id, 'title': section.title, + 'first': section.elements[0].id if section.elements else None } if section else {} def get_prev_page(self, obj): diff --git a/rdmo/projects/static/projects/js/project_questions/services.js b/rdmo/projects/static/projects/js/project_questions/services.js index 9d511bb26b..4ed6ba7688 100644 --- a/rdmo/projects/static/projects/js/project_questions/services.js +++ b/rdmo/projects/static/projects/js/project_questions/services.js @@ -9,7 +9,7 @@ angular.module('project_questions') /* configure resources */ var resources = { - projects: $resource(baseurl + 'api/v1/projects/projects/:id/:detail_action/'), + projects: $resource(baseurl + 'api/v1/projects/projects/:id/:detail_action/:detail_id/'), values: $resource(baseurl + 'api/v1/projects/projects/:project/values/:id/:detail_action/'), pages: $resource(baseurl + 'api/v1/projects/projects/:project/pages/:list_action/:id/'), settings: $resource(baseurl + 'api/v1/core/settings/') @@ -147,13 +147,14 @@ angular.module('project_questions') } return service.fetchPage(page_id) + .then(service.fetchNavigation) .then(service.fetchOptions) .then(service.fetchValues) .then(service.fetchConditions) .then(function () { // copy future objects angular.forEach([ - 'page', 'progress', 'attributes', 'questionsets', 'questions', 'valuesets', 'values' + 'page', 'progress', 'attributes', 'questionsets', 'questions', 'valuesets', 'values', 'navigation' ], function (key) { service[key] = angular.copy(future[key]); }); @@ -276,6 +277,16 @@ angular.module('project_questions') }); }; + service.fetchNavigation = function() { + future.navigation = resources.projects.query({ + id: service.project.id, + detail_id: future.page.section.id, + detail_action: 'navigation' + }); + + return future.navigation.$promise + }; + service.initPage = function(page) { // store attributes in a separate array if (page.attribute !== null) future.attributes.push(page.attribute); @@ -891,16 +902,7 @@ angular.module('project_questions') } else if (angular.isDefined(page)) { service.initView(page.id); } else if (angular.isDefined(section)) { - if (angular.isDefined(section.pages)) { - service.initView(section.pages[0].id); - } else { - // jump to first page of the section in breadcrumb - // let section_from_service = service.project.catalog.sections.find(x => x.id === section.id) - var section_from_service = $filter('filter')(service.project.catalog.sections, { - id: section.id - })[0] - service.initView(section_from_service.pages[0].id); - } + service.initView(section.first); } else { service.initView(null); } @@ -909,16 +911,7 @@ angular.module('project_questions') if (angular.isDefined(page)) { service.initView(page.id); } else if (angular.isDefined(section)) { - if (angular.isDefined(section.pages)) { - service.initView(section.pages[0].id); - } else { - // jump to first page of the section in breadcrumb - // let section_from_service = service.project.catalog.sections.find(x => x.id === section.id) - var section_from_service = $filter('filter')(service.project.catalog.sections, { - id: section.id - })[0] - service.initView(section_from_service.pages[0].id); - } + service.initView(section.first); } else { service.initView(null); } diff --git a/rdmo/projects/templates/projects/project_questions_navigation.html b/rdmo/projects/templates/projects/project_questions_navigation.html index f5945d8db2..6f9a1f2eb9 100644 --- a/rdmo/projects/templates/projects/project_questions_navigation.html +++ b/rdmo/projects/templates/projects/project_questions_navigation.html @@ -3,22 +3,27 @@ {% include 'projects/project_questions_navigation_help.html' %}
{% trans 'Name' %}{% trans 'Name' %}{% trans 'Progress' %} {% trans 'Created' %} {% trans 'Last changed' %} + {% project_progress project %} + {{ project.created }}