We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Send web analytics event when user enrolls on a course run
## Feature Request

**Is your feature request related to a problem or unsupported use case? Please describe.**
To better monitor witch marketing campaigns have better performance, we need to generate events when an user enrolls to a course. 

**Describe the solution you'd like**
I would like to the add support to the web analytics module, the some react events, at least when the user enrolls to a course.

**Describe alternatives you've considered**
I don't any.

**Discovery, Documentation, Adoption, Migration Strategy**
It is a new feature and it's compatible with current implementation. 
Add information on web analytics docs on google analytics sub section that richie would send events, and list them.

**Do you want to work on it through a Pull Request?**
Yes. Probably create an util to send web analytics events.

How to configure Google Analytics to receive the Google Tag Manager custom events:
https://www.analyticsmania.com/post/google-tag-manager-custom-event-trigger/

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/richie/apps/core/context_processors.py`
Content:
```
1 """
2 Template context processors
3 """
4 import json
5 from collections import OrderedDict
6 
7 from django.conf import settings
8 from django.contrib.sites.models import Site
9 from django.core.files.storage import get_storage_class
10 from django.http.request import HttpRequest
11 from django.middleware.csrf import get_token
12 from django.utils.translation import get_language_from_request
13 
14 from richie.apps.core.templatetags.joanie import is_joanie_enabled
15 from richie.apps.courses.defaults import RICHIE_MAX_ARCHIVED_COURSE_RUNS
16 from richie.apps.courses.models import Organization
17 
18 from . import defaults
19 
20 
21 def site_metas(request: HttpRequest):
22     """
23     Context processor to add all information required by Richie CMS templates and frontend.
24 
25     If `CDN_DOMAIN` settings is defined we add it in the context. It allows
26     to load statics js on a CDN like cloudfront.
27     """
28     site_current = Site.objects.get_current()
29     protocol = "https" if request.is_secure() else "http"
30 
31     context = {
32         **{
33             f"GLIMPSE_PAGINATION_{k.upper()}": v
34             for k, v in {
35                 **defaults.GLIMPSE_PAGINATION,
36                 **getattr(settings, "RICHIE_GLIMPSE_PAGINATION", {}),
37             }.items()
38         },
39         "SITE": {
40             "name": site_current.name,
41             "domain": site_current.domain,
42             "web_url": f"{protocol:s}://{site_current.domain:s}",
43         },
44         "FRONTEND_CONTEXT": {
45             "context": {
46                 "csrftoken": get_token(request),
47                 "environment": getattr(settings, "ENVIRONMENT", ""),
48                 "release": getattr(settings, "RELEASE", ""),
49                 "sentry_dsn": getattr(settings, "SENTRY_DSN", ""),
50             }
51         },
52         **WebAnalyticsContextProcessor().context_processor(request),
53     }
54 
55     if getattr(settings, "CDN_DOMAIN", None):
56         context["CDN_DOMAIN"] = settings.CDN_DOMAIN
57 
58     storage_url = get_storage_class()().url("any-page")
59     # Add a MEDIA_URL_PREFIX to context to prefix the media url files to have an absolute URL
60     if storage_url.startswith("//"):
61         # Eg. //my-cdn-user.cdn-provider.com/media/
62         context["MEDIA_URL_PREFIX"] = f"{request.scheme:s}:"
63     elif storage_url.startswith("/"):
64         # Eg. /media/
65         context["MEDIA_URL_PREFIX"] = f"{protocol:s}://{site_current.domain:s}"
66     else:
67         # Eg. https://my-cdn-user.cdn-provider.com/media/
68         context["MEDIA_URL_PREFIX"] = ""
69 
70     authentication_delegation = getattr(
71         settings, "RICHIE_AUTHENTICATION_DELEGATION", None
72     )
73     if authentication_delegation:
74 
75         context["AUTHENTICATION"] = {
76             "profile_urls": json.dumps(
77                 {
78                     key: {
79                         "label": str(url["label"]),
80                         "action": str(
81                             url["href"].format(
82                                 base_url=authentication_delegation["BASE_URL"]
83                             )
84                         ),
85                     }
86                     for key, url in authentication_delegation.get(
87                         "PROFILE_URLS", {}
88                     ).items()
89                 }
90             ),
91         }
92 
93         context["FRONTEND_CONTEXT"]["context"]["authentication"] = {
94             "endpoint": authentication_delegation["BASE_URL"],
95             "backend": authentication_delegation["BACKEND"],
96         }
97 
98     if is_joanie_enabled():
99         context["FRONTEND_CONTEXT"]["context"]["joanie_backend"] = {
100             "endpoint": settings.JOANIE["BASE_URL"],
101         }
102 
103     if getattr(settings, "RICHIE_LMS_BACKENDS", None):
104         context["FRONTEND_CONTEXT"]["context"]["lms_backends"] = [
105             {
106                 "endpoint": lms["BASE_URL"],
107                 "backend": lms["JS_BACKEND"],
108                 "course_regexp": lms["JS_COURSE_REGEX"],
109             }
110             for lms in getattr(settings, "RICHIE_LMS_BACKENDS", [])
111         ]
112 
113     context["FRONTEND_CONTEXT"] = json.dumps(context["FRONTEND_CONTEXT"])
114 
115     if getattr(settings, "RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT", None):
116         context[
117             "RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT"
118         ] = settings.RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT
119 
120     context["RICHIE_MAX_ARCHIVED_COURSE_RUNS"] = getattr(
121         settings, "RICHIE_MAX_ARCHIVED_COURSE_RUNS", RICHIE_MAX_ARCHIVED_COURSE_RUNS
122     )
123 
124     return context
125 
126 
127 class WebAnalyticsContextProcessor:
128     """
129     Context processor to add Web Analytics tracking information to Richie CMS templates and
130     frontend.
131     """
132 
133     def context_processor(self, request: HttpRequest) -> dict:
134         """
135         Real implementation of the context processor for the Web Analytics core app sub-module
136         """
137         context = {}
138         if hasattr(request, "current_page"):
139             # load web analytics settings to the context
140             if getattr(settings, "WEB_ANALYTICS_ID", None):
141                 context["WEB_ANALYTICS_ID"] = settings.WEB_ANALYTICS_ID
142                 context["WEB_ANALYTICS_DIMENSIONS"] = self.get_dimensions(request)
143 
144             context["WEB_ANALYTICS_LOCATION"] = getattr(
145                 settings, "WEB_ANALYTICS_LOCATION", "head"
146             )
147 
148             context["WEB_ANALYTICS_PROVIDER"] = getattr(
149                 settings, "WEB_ANALYTICS_PROVIDER", "google_analytics"
150             )
151         return context
152 
153     # pylint: disable=no-self-use
154     def get_dimensions(self, request: HttpRequest) -> dict:
155         """
156         Compute the web analytics dimensions (dict) that would be added to the Django context
157         They are a dictionary like:
158         ```
159         {
160             "organizations_codes": ["UNIV_LISBON", "UNIV_PORTO"],
161             "course_code": ["COURSE_XPTO"],
162             "course_runs_titles": [
163                 "Summer edition",
164                 "Winter edition"
165             ],
166             "course_runs_resource_links": [
167                 "http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info",
168                 "http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course_2/info"
169             ],
170             "page_title": ["Introduction to Programming"],
171         }
172         ```
173 
174         Args:
175             request (HttpRequest): The Http request
176             web_analytics_context (dict): the context relevant for the web analytics sub module
177 
178         Returns:
179             dict: a dict with the dimensions, where each value is a list
180         """
181         # Warn do not change the order of evaluation of this methods, because on Google Analytics
182         # the key is a 'dimension1' instead of a more generic key like 'organizations'.
183         # That's why we are using an OrderedDict instead of a normal Python dict (don't support
184         # ordering)
185         dimensions = OrderedDict()
186 
187         page = request.current_page or None
188         language = get_language_from_request(request, check_path=True)
189 
190         organizations_codes = []
191         if page and not page.is_home:
192             organizations_codes = Organization.get_organizations_codes(page, language)
193         dimensions["organizations_codes"] = organizations_codes
194 
195         course = getattr(page, "course", None)
196         dimensions["course_code"] = [getattr(course, "code", "")]
197 
198         course_runs = course.course_runs if course else []
199         dimensions["course_runs_titles"] = [
200             course_run.title
201             for course_run in course_runs
202             if course_run is not None and course_run.safe_title is not None
203         ]
204 
205         dimensions["course_runs_resource_links"] = map(
206             lambda course_run: course_run.resource_link, course_runs
207         )
208 
209         dimensions["page_title"] = [page.get_title() if page else ""]
210         return dimensions
211
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/richie/apps/core/context_processors.py b/src/richie/apps/core/context_processors.py
--- a/src/richie/apps/core/context_processors.py
+++ b/src/richie/apps/core/context_processors.py
@@ -47,6 +47,7 @@
                 "environment": getattr(settings, "ENVIRONMENT", ""),
                 "release": getattr(settings, "RELEASE", ""),
                 "sentry_dsn": getattr(settings, "SENTRY_DSN", ""),
+                **WebAnalyticsContextProcessor().frontend_context_processor(request),
             }
         },
         **WebAnalyticsContextProcessor().context_processor(request),
@@ -130,6 +131,18 @@
     frontend.
     """
 
+    # pylint: disable=no-self-use
+    def frontend_context_processor(self, request: HttpRequest) -> dict:
+        """
+        Additional web analytics information for the frontend react
+        """
+        context = {}
+        if getattr(settings, "WEB_ANALYTICS_ID"):
+            context["web_analytics_provider"] = getattr(
+                settings, "WEB_ANALYTICS_PROVIDER", "google_analytics"
+            )
+        return context
+
     def context_processor(self, request: HttpRequest) -> dict:
         """
         Real implementation of the context processor for the Web Analytics core app sub-module

{"golden_diff": "diff --git a/src/richie/apps/core/context_processors.py b/src/richie/apps/core/context_processors.py\n--- a/src/richie/apps/core/context_processors.py\n+++ b/src/richie/apps/core/context_processors.py\n@@ -47,6 +47,7 @@\n                 \"environment\": getattr(settings, \"ENVIRONMENT\", \"\"),\n                 \"release\": getattr(settings, \"RELEASE\", \"\"),\n                 \"sentry_dsn\": getattr(settings, \"SENTRY_DSN\", \"\"),\n+                **WebAnalyticsContextProcessor().frontend_context_processor(request),\n             }\n         },\n         **WebAnalyticsContextProcessor().context_processor(request),\n@@ -130,6 +131,18 @@\n     frontend.\n     \"\"\"\n \n+    # pylint: disable=no-self-use\n+    def frontend_context_processor(self, request: HttpRequest) -> dict:\n+        \"\"\"\n+        Additional web analytics information for the frontend react\n+        \"\"\"\n+        context = {}\n+        if getattr(settings, \"WEB_ANALYTICS_ID\"):\n+            context[\"web_analytics_provider\"] = getattr(\n+                settings, \"WEB_ANALYTICS_PROVIDER\", \"google_analytics\"\n+            )\n+        return context\n+\n     def context_processor(self, request: HttpRequest) -> dict:\n         \"\"\"\n         Real implementation of the context processor for the Web Analytics core app sub-module\n", "issue": "Send web analytics event when user enrolls on a course run\n## Feature Request\r\n\r\n**Is your feature request related to a problem or unsupported use case? Please describe.**\r\nTo better monitor witch marketing campaigns have better performance, we need to generate events when an user enrolls to a course. \r\n\r\n**Describe the solution you'd like**\r\nI would like to the add support to the web analytics module, the some react events, at least when the user enrolls to a course.\r\n\r\n**Describe alternatives you've considered**\r\nI don't any.\r\n\r\n**Discovery, Documentation, Adoption, Migration Strategy**\r\nIt is a new feature and it's compatible with current implementation. \r\nAdd information on web analytics docs on google analytics sub section that richie would send events, and list them.\r\n\r\n**Do you want to work on it through a Pull Request?**\r\nYes. Probably create an util to send web analytics events.\r\n\r\nHow to configure Google Analytics to receive the Google Tag Manager custom events:\r\nhttps://www.analyticsmania.com/post/google-tag-manager-custom-event-trigger/\n", "before_files": [{"content": "\"\"\"\nTemplate context processors\n\"\"\"\nimport json\nfrom collections import OrderedDict\n\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.core.files.storage import get_storage_class\nfrom django.http.request import HttpRequest\nfrom django.middleware.csrf import get_token\nfrom django.utils.translation import get_language_from_request\n\nfrom richie.apps.core.templatetags.joanie import is_joanie_enabled\nfrom richie.apps.courses.defaults import RICHIE_MAX_ARCHIVED_COURSE_RUNS\nfrom richie.apps.courses.models import Organization\n\nfrom . import defaults\n\n\ndef site_metas(request: HttpRequest):\n    \"\"\"\n    Context processor to add all information required by Richie CMS templates and frontend.\n\n    If `CDN_DOMAIN` settings is defined we add it in the context. It allows\n    to load statics js on a CDN like cloudfront.\n    \"\"\"\n    site_current = Site.objects.get_current()\n    protocol = \"https\" if request.is_secure() else \"http\"\n\n    context = {\n        **{\n            f\"GLIMPSE_PAGINATION_{k.upper()}\": v\n            for k, v in {\n                **defaults.GLIMPSE_PAGINATION,\n                **getattr(settings, \"RICHIE_GLIMPSE_PAGINATION\", {}),\n            }.items()\n        },\n        \"SITE\": {\n            \"name\": site_current.name,\n            \"domain\": site_current.domain,\n            \"web_url\": f\"{protocol:s}://{site_current.domain:s}\",\n        },\n        \"FRONTEND_CONTEXT\": {\n            \"context\": {\n                \"csrftoken\": get_token(request),\n                \"environment\": getattr(settings, \"ENVIRONMENT\", \"\"),\n                \"release\": getattr(settings, \"RELEASE\", \"\"),\n                \"sentry_dsn\": getattr(settings, \"SENTRY_DSN\", \"\"),\n            }\n        },\n        **WebAnalyticsContextProcessor().context_processor(request),\n    }\n\n    if getattr(settings, \"CDN_DOMAIN\", None):\n        context[\"CDN_DOMAIN\"] = settings.CDN_DOMAIN\n\n    storage_url = get_storage_class()().url(\"any-page\")\n    # Add a MEDIA_URL_PREFIX to context to prefix the media url files to have an absolute URL\n    if storage_url.startswith(\"//\"):\n        # Eg. //my-cdn-user.cdn-provider.com/media/\n        context[\"MEDIA_URL_PREFIX\"] = f\"{request.scheme:s}:\"\n    elif storage_url.startswith(\"/\"):\n        # Eg. /media/\n        context[\"MEDIA_URL_PREFIX\"] = f\"{protocol:s}://{site_current.domain:s}\"\n    else:\n        # Eg. https://my-cdn-user.cdn-provider.com/media/\n        context[\"MEDIA_URL_PREFIX\"] = \"\"\n\n    authentication_delegation = getattr(\n        settings, \"RICHIE_AUTHENTICATION_DELEGATION\", None\n    )\n    if authentication_delegation:\n\n        context[\"AUTHENTICATION\"] = {\n            \"profile_urls\": json.dumps(\n                {\n                    key: {\n                        \"label\": str(url[\"label\"]),\n                        \"action\": str(\n                            url[\"href\"].format(\n                                base_url=authentication_delegation[\"BASE_URL\"]\n                            )\n                        ),\n                    }\n                    for key, url in authentication_delegation.get(\n                        \"PROFILE_URLS\", {}\n                    ).items()\n                }\n            ),\n        }\n\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"authentication\"] = {\n            \"endpoint\": authentication_delegation[\"BASE_URL\"],\n            \"backend\": authentication_delegation[\"BACKEND\"],\n        }\n\n    if is_joanie_enabled():\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"joanie_backend\"] = {\n            \"endpoint\": settings.JOANIE[\"BASE_URL\"],\n        }\n\n    if getattr(settings, \"RICHIE_LMS_BACKENDS\", None):\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"lms_backends\"] = [\n            {\n                \"endpoint\": lms[\"BASE_URL\"],\n                \"backend\": lms[\"JS_BACKEND\"],\n                \"course_regexp\": lms[\"JS_COURSE_REGEX\"],\n            }\n            for lms in getattr(settings, \"RICHIE_LMS_BACKENDS\", [])\n        ]\n\n    context[\"FRONTEND_CONTEXT\"] = json.dumps(context[\"FRONTEND_CONTEXT\"])\n\n    if getattr(settings, \"RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\", None):\n        context[\n            \"RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\"\n        ] = settings.RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\n\n    context[\"RICHIE_MAX_ARCHIVED_COURSE_RUNS\"] = getattr(\n        settings, \"RICHIE_MAX_ARCHIVED_COURSE_RUNS\", RICHIE_MAX_ARCHIVED_COURSE_RUNS\n    )\n\n    return context\n\n\nclass WebAnalyticsContextProcessor:\n    \"\"\"\n    Context processor to add Web Analytics tracking information to Richie CMS templates and\n    frontend.\n    \"\"\"\n\n    def context_processor(self, request: HttpRequest) -> dict:\n        \"\"\"\n        Real implementation of the context processor for the Web Analytics core app sub-module\n        \"\"\"\n        context = {}\n        if hasattr(request, \"current_page\"):\n            # load web analytics settings to the context\n            if getattr(settings, \"WEB_ANALYTICS_ID\", None):\n                context[\"WEB_ANALYTICS_ID\"] = settings.WEB_ANALYTICS_ID\n                context[\"WEB_ANALYTICS_DIMENSIONS\"] = self.get_dimensions(request)\n\n            context[\"WEB_ANALYTICS_LOCATION\"] = getattr(\n                settings, \"WEB_ANALYTICS_LOCATION\", \"head\"\n            )\n\n            context[\"WEB_ANALYTICS_PROVIDER\"] = getattr(\n                settings, \"WEB_ANALYTICS_PROVIDER\", \"google_analytics\"\n            )\n        return context\n\n    # pylint: disable=no-self-use\n    def get_dimensions(self, request: HttpRequest) -> dict:\n        \"\"\"\n        Compute the web analytics dimensions (dict) that would be added to the Django context\n        They are a dictionary like:\n        ```\n        {\n            \"organizations_codes\": [\"UNIV_LISBON\", \"UNIV_PORTO\"],\n            \"course_code\": [\"COURSE_XPTO\"],\n            \"course_runs_titles\": [\n                \"Summer edition\",\n                \"Winter edition\"\n            ],\n            \"course_runs_resource_links\": [\n                \"http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info\",\n                \"http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course_2/info\"\n            ],\n            \"page_title\": [\"Introduction to Programming\"],\n        }\n        ```\n\n        Args:\n            request (HttpRequest): The Http request\n            web_analytics_context (dict): the context relevant for the web analytics sub module\n\n        Returns:\n            dict: a dict with the dimensions, where each value is a list\n        \"\"\"\n        # Warn do not change the order of evaluation of this methods, because on Google Analytics\n        # the key is a 'dimension1' instead of a more generic key like 'organizations'.\n        # That's why we are using an OrderedDict instead of a normal Python dict (don't support\n        # ordering)\n        dimensions = OrderedDict()\n\n        page = request.current_page or None\n        language = get_language_from_request(request, check_path=True)\n\n        organizations_codes = []\n        if page and not page.is_home:\n            organizations_codes = Organization.get_organizations_codes(page, language)\n        dimensions[\"organizations_codes\"] = organizations_codes\n\n        course = getattr(page, \"course\", None)\n        dimensions[\"course_code\"] = [getattr(course, \"code\", \"\")]\n\n        course_runs = course.course_runs if course else []\n        dimensions[\"course_runs_titles\"] = [\n            course_run.title\n            for course_run in course_runs\n            if course_run is not None and course_run.safe_title is not None\n        ]\n\n        dimensions[\"course_runs_resource_links\"] = map(\n            lambda course_run: course_run.resource_link, course_runs\n        )\n\n        dimensions[\"page_title\"] = [page.get_title() if page else \"\"]\n        return dimensions\n", "path": "src/richie/apps/core/context_processors.py"}], "after_files": [{"content": "\"\"\"\nTemplate context processors\n\"\"\"\nimport json\nfrom collections import OrderedDict\n\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.core.files.storage import get_storage_class\nfrom django.http.request import HttpRequest\nfrom django.middleware.csrf import get_token\nfrom django.utils.translation import get_language_from_request\n\nfrom richie.apps.core.templatetags.joanie import is_joanie_enabled\nfrom richie.apps.courses.defaults import RICHIE_MAX_ARCHIVED_COURSE_RUNS\nfrom richie.apps.courses.models import Organization\n\nfrom . import defaults\n\n\ndef site_metas(request: HttpRequest):\n    \"\"\"\n    Context processor to add all information required by Richie CMS templates and frontend.\n\n    If `CDN_DOMAIN` settings is defined we add it in the context. It allows\n    to load statics js on a CDN like cloudfront.\n    \"\"\"\n    site_current = Site.objects.get_current()\n    protocol = \"https\" if request.is_secure() else \"http\"\n\n    context = {\n        **{\n            f\"GLIMPSE_PAGINATION_{k.upper()}\": v\n            for k, v in {\n                **defaults.GLIMPSE_PAGINATION,\n                **getattr(settings, \"RICHIE_GLIMPSE_PAGINATION\", {}),\n            }.items()\n        },\n        \"SITE\": {\n            \"name\": site_current.name,\n            \"domain\": site_current.domain,\n            \"web_url\": f\"{protocol:s}://{site_current.domain:s}\",\n        },\n        \"FRONTEND_CONTEXT\": {\n            \"context\": {\n                \"csrftoken\": get_token(request),\n                \"environment\": getattr(settings, \"ENVIRONMENT\", \"\"),\n                \"release\": getattr(settings, \"RELEASE\", \"\"),\n                \"sentry_dsn\": getattr(settings, \"SENTRY_DSN\", \"\"),\n                **WebAnalyticsContextProcessor().frontend_context_processor(request),\n            }\n        },\n        **WebAnalyticsContextProcessor().context_processor(request),\n    }\n\n    if getattr(settings, \"CDN_DOMAIN\", None):\n        context[\"CDN_DOMAIN\"] = settings.CDN_DOMAIN\n\n    storage_url = get_storage_class()().url(\"any-page\")\n    # Add a MEDIA_URL_PREFIX to context to prefix the media url files to have an absolute URL\n    if storage_url.startswith(\"//\"):\n        # Eg. //my-cdn-user.cdn-provider.com/media/\n        context[\"MEDIA_URL_PREFIX\"] = f\"{request.scheme:s}:\"\n    elif storage_url.startswith(\"/\"):\n        # Eg. /media/\n        context[\"MEDIA_URL_PREFIX\"] = f\"{protocol:s}://{site_current.domain:s}\"\n    else:\n        # Eg. https://my-cdn-user.cdn-provider.com/media/\n        context[\"MEDIA_URL_PREFIX\"] = \"\"\n\n    authentication_delegation = getattr(\n        settings, \"RICHIE_AUTHENTICATION_DELEGATION\", None\n    )\n    if authentication_delegation:\n\n        context[\"AUTHENTICATION\"] = {\n            \"profile_urls\": json.dumps(\n                {\n                    key: {\n                        \"label\": str(url[\"label\"]),\n                        \"action\": str(\n                            url[\"href\"].format(\n                                base_url=authentication_delegation[\"BASE_URL\"]\n                            )\n                        ),\n                    }\n                    for key, url in authentication_delegation.get(\n                        \"PROFILE_URLS\", {}\n                    ).items()\n                }\n            ),\n        }\n\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"authentication\"] = {\n            \"endpoint\": authentication_delegation[\"BASE_URL\"],\n            \"backend\": authentication_delegation[\"BACKEND\"],\n        }\n\n    if is_joanie_enabled():\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"joanie_backend\"] = {\n            \"endpoint\": settings.JOANIE[\"BASE_URL\"],\n        }\n\n    if getattr(settings, \"RICHIE_LMS_BACKENDS\", None):\n        context[\"FRONTEND_CONTEXT\"][\"context\"][\"lms_backends\"] = [\n            {\n                \"endpoint\": lms[\"BASE_URL\"],\n                \"backend\": lms[\"JS_BACKEND\"],\n                \"course_regexp\": lms[\"JS_COURSE_REGEX\"],\n            }\n            for lms in getattr(settings, \"RICHIE_LMS_BACKENDS\", [])\n        ]\n\n    context[\"FRONTEND_CONTEXT\"] = json.dumps(context[\"FRONTEND_CONTEXT\"])\n\n    if getattr(settings, \"RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\", None):\n        context[\n            \"RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\"\n        ] = settings.RICHIE_MINIMUM_COURSE_RUNS_ENROLLMENT_COUNT\n\n    context[\"RICHIE_MAX_ARCHIVED_COURSE_RUNS\"] = getattr(\n        settings, \"RICHIE_MAX_ARCHIVED_COURSE_RUNS\", RICHIE_MAX_ARCHIVED_COURSE_RUNS\n    )\n\n    return context\n\n\nclass WebAnalyticsContextProcessor:\n    \"\"\"\n    Context processor to add Web Analytics tracking information to Richie CMS templates and\n    frontend.\n    \"\"\"\n\n    # pylint: disable=no-self-use\n    def frontend_context_processor(self, request: HttpRequest) -> dict:\n        \"\"\"\n        Additional web analytics information for the frontend react\n        \"\"\"\n        context = {}\n        if getattr(settings, \"WEB_ANALYTICS_ID\"):\n            context[\"web_analytics_provider\"] = getattr(\n                settings, \"WEB_ANALYTICS_PROVIDER\", \"google_analytics\"\n            )\n        return context\n\n    def context_processor(self, request: HttpRequest) -> dict:\n        \"\"\"\n        Real implementation of the context processor for the Web Analytics core app sub-module\n        \"\"\"\n        context = {}\n        if hasattr(request, \"current_page\"):\n            # load web analytics settings to the context\n            if getattr(settings, \"WEB_ANALYTICS_ID\", None):\n                context[\"WEB_ANALYTICS_ID\"] = settings.WEB_ANALYTICS_ID\n                context[\"WEB_ANALYTICS_DIMENSIONS\"] = self.get_dimensions(request)\n\n            context[\"WEB_ANALYTICS_LOCATION\"] = getattr(\n                settings, \"WEB_ANALYTICS_LOCATION\", \"head\"\n            )\n\n            context[\"WEB_ANALYTICS_PROVIDER\"] = getattr(\n                settings, \"WEB_ANALYTICS_PROVIDER\", \"google_analytics\"\n            )\n        return context\n\n    # pylint: disable=no-self-use\n    def get_dimensions(self, request: HttpRequest) -> dict:\n        \"\"\"\n        Compute the web analytics dimensions (dict) that would be added to the Django context\n        They are a dictionary like:\n        ```\n        {\n            \"organizations_codes\": [\"UNIV_LISBON\", \"UNIV_PORTO\"],\n            \"course_code\": [\"COURSE_XPTO\"],\n            \"course_runs_titles\": [\n                \"Summer edition\",\n                \"Winter edition\"\n            ],\n            \"course_runs_resource_links\": [\n                \"http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info\",\n                \"http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course_2/info\"\n            ],\n            \"page_title\": [\"Introduction to Programming\"],\n        }\n        ```\n\n        Args:\n            request (HttpRequest): The Http request\n            web_analytics_context (dict): the context relevant for the web analytics sub module\n\n        Returns:\n            dict: a dict with the dimensions, where each value is a list\n        \"\"\"\n        # Warn do not change the order of evaluation of this methods, because on Google Analytics\n        # the key is a 'dimension1' instead of a more generic key like 'organizations'.\n        # That's why we are using an OrderedDict instead of a normal Python dict (don't support\n        # ordering)\n        dimensions = OrderedDict()\n\n        page = request.current_page or None\n        language = get_language_from_request(request, check_path=True)\n\n        organizations_codes = []\n        if page and not page.is_home:\n            organizations_codes = Organization.get_organizations_codes(page, language)\n        dimensions[\"organizations_codes\"] = organizations_codes\n\n        course = getattr(page, \"course\", None)\n        dimensions[\"course_code\"] = [getattr(course, \"code\", \"\")]\n\n        course_runs = course.course_runs if course else []\n        dimensions[\"course_runs_titles\"] = [\n            course_run.title\n            for course_run in course_runs\n            if course_run is not None and course_run.safe_title is not None\n        ]\n\n        dimensions[\"course_runs_resource_links\"] = map(\n            lambda course_run: course_run.resource_link, course_runs\n        )\n\n        dimensions[\"page_title\"] = [page.get_title() if page else \"\"]\n        return dimensions\n", "path": "src/richie/apps/core/context_processors.py"}]}