Repository: apluslms/a-plus

Issue:
Add the learning object's hierarchical name to CachedContent and remove the regex hack in CourseExercisesViewSet
From the comment https://github.com/apluslms/a-plus/pull/1090#issuecomment-1615351930

* Could `hierarchical_name` be added to `CachedContent`? `__recurse_exercises` now uses an awful regex hack to deal with hierarchical names. If the cache contained the hierarchical name directly in a new field, we could remove the regex hack.
  https://github.com/apluslms/a-plus/blob/ebf3bc012f61f219893d1e00a86acb00f08aa84e/course/api/views.py#L212-L219
* `hierarchical_name` is formed incorrectly for exercises when the course uses Roman content numbering. `__recurse_exercises` uses `f"{child['number']} {child['name']}"` but in this case, the number is Roman while the hierarchical name should always use Arabic numbers "1.2.3".
  https://github.com/apluslms/a-plus/blob/ebf3bc012f61f219893d1e00a86acb00f08aa84e/course/api/views.py#L218
  This is easy to fix if the correct hierarchical name can be read directly from the cache. Then,  `CourseExercisesViewSet` does not need to concatenate any string components together.


Discussion:

Resolving patch:
diff --git a/course/api/views.py b/course/api/views.py
--- a/course/api/views.py
+++ b/course/api/views.py
@@ -1,5 +1,3 @@
-import re
-
 from rest_framework import filters, viewsets, status, mixins
 from rest_framework.decorators import action
 from rest_framework.exceptions import ParseError
@@ -206,17 +204,6 @@ class CourseExercisesViewSet(NestedViewSetMixin,
     def __recurse_exercises(self, module, exercises):
         for child in filter(lambda ex: ex['type'] == 'exercise', module['children']):
             if child['submittable']:
-                # check if there is a xx.yy pattern at the start of the name (e.g. 6.2 Hello Python). Also matches 1.
-                # Also matches roman numerals
-                # https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-                hierarchical_name = (
-                    child['name']
-                    if re.match(
-                        r"^([0-9]+\.[0-9.]* )|(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})) ",
-                        child['name'],
-                    )
-                    else f"{child['number']} {child['name']}"
-                )
                 exercise_dictionary = {
                     'id': child['id'],
                     'url': build_aplus_url(
@@ -227,7 +214,7 @@ def __recurse_exercises(self, module, exercises):
                     'display_name': child['name'],
                     'max_points': child['max_points'],
                     'max_submissions': child['max_submissions'],
-                    'hierarchical_name': hierarchical_name,
+                    'hierarchical_name': child['hierarchical_name'],
                     'difficulty': child['difficulty'],
                 }
                 exercises.append(exercise_dictionary)
diff --git a/exercise/cache/content.py b/exercise/cache/content.py
--- a/exercise/cache/content.py
+++ b/exercise/cache/content.py
@@ -59,6 +59,7 @@ def recursion(
                     'order': o.order,
                     'status': o.status,
                     'name': str(o),
+                    'hierarchical_name': o.hierarchical_name(),
                     'number': module['number'] + '.' + o.number(),
                     'link': o.get_display_url(),
                     'submittable': False,


Test patch: