diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py index efe742136..e2456c703 100644 --- a/rdmo/projects/progress.py +++ b/rdmo/projects/progress.py @@ -47,6 +47,47 @@ def compute_sets(values): return sets +def compute_next_relevant_page(current_page, direction, catalog, resolved_conditions): + """ + Determine the next relevant page based on precomputed values and conditions. + """ + while True: + # Determine the next page based on the specified direction + next_page = (catalog.get_prev_page(current_page) if direction == 'prev' + else catalog.get_next_page(current_page)) + + # If no further pages are available, return None + if not next_page: + return None + + # Use compute_show_page with precomputed resolved_conditions to check if the next page meets conditions + if compute_show_page(next_page, resolved_conditions): + return next_page # Found the next relevant page + + # Move to the next page in sequence if conditions are not met + current_page = next_page + + +def compute_show_page(page, conditions): + """Determine if a page should be shown based on resolved conditions.""" + # show only pages with resolved conditions, but show all pages without conditions + pages_conditions = {page.id for page in page.conditions.all()} + + if pages_conditions: + # check if any valuesets for set_prefix = '' resolved + # for non collection pages restrict further to set_index = 0 + return any( + ( + (set_prefix == '') and + ((page.is_collection or set_index == 0) or (not page.is_collection and set_index)) + ) + for page_condition in pages_conditions + for set_prefix, set_index in conditions[page_condition] + ) + else: + return True + + 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') @@ -74,19 +115,8 @@ def compute_navigation(section, project, snapshot=None): navigation_section['pages'] = [] for page in catalog_section.elements: - pages_conditions = {page.id for page in page.conditions.all()} - - # show only pages with resolved conditions, but show all pages without conditions - if pages_conditions: - # check if any valuesets for set_prefix = '' resolved - # for non collection pages restrict further to set_index = 0 - show = any( - (set_prefix == '') and (page.is_collection or set_index == 0) - for page_condition in pages_conditions - for set_prefix, set_index in conditions[page_condition] - ) - else: - show = True + + show = compute_show_page(page, conditions) # count the total number of questions, taking sets and conditions into account counts = count_questions(page, sets, conditions) diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index 3ca669e76..725920c6b 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -42,7 +42,14 @@ HasProjectProgressObjectPermission, HasProjectsPermission, ) -from .progress import compute_navigation, compute_progress +from .progress import ( + compute_navigation, + compute_next_relevant_page, + compute_progress, + compute_sets, + compute_show_page, + resolve_conditions, +) from .serializers.v1 import ( IntegrationSerializer, InviteSerializer, @@ -521,29 +528,34 @@ def dispatch(self, *args, **kwargs): def retrieve(self, request, *args, **kwargs): page = self.get_object() - conditions = page.conditions.select_related('source', 'target_option') - + catalog = self.project.catalog + # conditions = page.conditions.select_related('source', 'target_option') values = self.project.values.filter(snapshot=None).select_related('attribute', 'option') - if check_conditions(conditions, values): + sets = compute_sets(values) + resolved_conditions = resolve_conditions(catalog, values, sets) + + # Check if the current page meets conditions + if compute_show_page(page, resolved_conditions): serializer = self.get_serializer(page) return Response(serializer.data) else: - if request.GET.get('back') == 'true': - prev_page = self.project.catalog.get_prev_page(page) - if prev_page is not None: - url = reverse('v1-projects:project-page-detail', - args=[self.project.id, prev_page.id]) + '?back=true' - return HttpResponseRedirect(url, status=303) - else: - next_page = self.project.catalog.get_next_page(page) - if next_page is not None: - url = reverse('v1-projects:project-page-detail', args=[self.project.id, next_page.id]) - return HttpResponseRedirect(url, status=303) - - # indicate end of catalog + # Determine the direction of navigation (back or forward) + direction = 'prev' if request.GET.get('back') == 'true' else 'next' + + # Find the next relevant page with precomputed values and conditions + next_page = compute_next_relevant_page(page, direction, catalog, resolved_conditions) + + if next_page: + url = reverse('v1-projects:project-page-detail', args=[self.project.id, next_page.id]) + if direction == 'prev': + url += '?back=true' + return HttpResponseRedirect(url, status=303) + + # If no next relevant page is found, end of catalog return Response(status=204) + @action(detail=False, url_path='continue', permission_classes=(HasModelPermission | HasProjectPagePermission, )) def get_continue(self, request, pk=None, parent_lookup_project=None): try: