We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
names of CSV files should be internationalized

### Observed behavior

The names of files when exporting and downloading from the facility data page are all in English:

> ![image](https://user-images.githubusercontent.com/2367265/81123607-52be9500-8ee8-11ea-907b-ab8300d24f20.png)

![image](https://user-images.githubusercontent.com/2367265/81123553-302c7c00-8ee8-11ea-8bb6-ea54ed2e376d.png)

ref: https://github.com/learningequality/kolibri/pull/6835

### Expected behavior

Names of files should be in the user's currently selected language. This is the same behavior as currently exists in Coach CSV export.

### User-facing consequences

file name meaning is not understandable in the user's preferred language

### Context

0.13.2

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kolibri/plugins/facility/views.py`
Content:
```
1 from __future__ import absolute_import
2 from __future__ import print_function
3 from __future__ import unicode_literals
4 
5 import io
6 import os
7 from datetime import datetime
8 
9 from django.http import Http404
10 from django.http.response import FileResponse
11 from django.utils.decorators import method_decorator
12 from django.views.generic.base import TemplateView
13 
14 from kolibri.core.decorators import cache_no_user_data
15 from kolibri.utils import conf
16 
17 
18 @method_decorator(cache_no_user_data, name="dispatch")
19 class FacilityManagementView(TemplateView):
20     template_name = "facility_management.html"
21 
22 
23 def download_csv_file(request, filename):
24     filepath = os.path.join(conf.KOLIBRI_HOME, "temp", filename)
25 
26     # if the file does not exist on disk, return a 404
27     if filepath is None or not os.path.exists(filepath):
28         raise Http404("Creation of users export file has failed")
29 
30     # generate a file response
31     response = FileResponse(io.open(filepath, "rb"))
32     # set the content-type by guessing from the filename
33     response["Content-Type"] = "text/csv"
34 
35     # set the content-disposition as attachment to force download
36     response["Content-Disposition"] = "attachment; filename=users_{}.csv".format(
37         datetime.now().strftime("%Y%m%d_%H%M%S")
38     )
39 
40     # set the content-length to the file size
41     response["Content-Length"] = os.path.getsize(filepath)
42 
43     return response
44
```

Path: `kolibri/core/logger/csv_export.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import csv
4 import io
5 import json
6 import logging
7 import math
8 import os
9 import sys
10 from collections import OrderedDict
11 
12 from django.core.cache import cache
13 from django.http import Http404
14 from django.http import HttpResponse
15 from django.http.response import FileResponse
16 
17 from .models import ContentSessionLog
18 from .models import ContentSummaryLog
19 from kolibri.core.content.models import ChannelMetadata
20 from kolibri.core.content.models import ContentNode
21 from kolibri.utils import conf
22 
23 
24 logger = logging.getLogger(__name__)
25 
26 
27 def cache_channel_name(obj):
28     channel_id = obj["channel_id"]
29     key = "{id}_ChannelMetadata_name".format(id=channel_id)
30     channel_name = cache.get(key)
31     if channel_name is None:
32         try:
33             channel_name = ChannelMetadata.objects.get(id=channel_id)
34         except ChannelMetadata.DoesNotExist:
35             channel_name = ""
36         cache.set(key, channel_name, 60 * 10)
37     return channel_name
38 
39 
40 def cache_content_title(obj):
41     content_id = obj["content_id"]
42     key = "{id}_ContentNode_title".format(id=content_id)
43     title = cache.get(key)
44     if title is None:
45         node = ContentNode.objects.filter(content_id=content_id).first()
46         if node:
47             title = node.title
48         else:
49             title = ""
50         cache.set(key, title, 60 * 10)
51     return title
52 
53 
54 mappings = {
55     "channel_name": cache_channel_name,
56     "content_title": cache_content_title,
57     "time_spent": lambda x: "{:.1f}".format(round(x["time_spent"], 1)),
58     "progress": lambda x: "{:.4f}".format(math.floor(x["progress"] * 10000.0) / 10000),
59 }
60 
61 labels = OrderedDict(
62     (
63         ("user__facility__name", "Facility name"),
64         ("user__username", "Username"),
65         ("channel_id", "Channel id"),
66         ("channel_name", "Channel name"),
67         ("content_id", "Content id"),
68         ("content_title", "Content title"),
69         ("start_timestamp", "Time of first interaction"),
70         ("end_timestamp", "Time of last interaction"),
71         ("completion_timestamp", "Time of completion"),
72         ("time_spent", "Time Spent (sec)"),
73         ("progress", "Progress (0-1)"),
74         ("kind", "Content kind"),
75     )
76 )
77 
78 
79 def map_object(obj):
80     mapped_obj = {}
81     for header, label in labels.items():
82         if header in mappings:
83             mapped_obj[label] = mappings[header](obj)
84         elif header in obj:
85             mapped_obj[label] = obj[header]
86     return mapped_obj
87 
88 
89 classes_info = {
90     "session": {
91         "queryset": ContentSessionLog.objects.all(),
92         "filename": "content_session_logs.csv",
93         "db_columns": (
94             "user__username",
95             "user__facility__name",
96             "channel_id",
97             "content_id",
98             "start_timestamp",
99             "end_timestamp",
100             "time_spent",
101             "progress",
102             "kind",
103         ),
104     },
105     "summary": {
106         "queryset": ContentSummaryLog.objects.all(),
107         "filename": "content_summary_logs.csv",
108         "db_columns": (
109             "user__username",
110             "user__facility__name",
111             "content_id",
112             "channel_id",
113             "start_timestamp",
114             "end_timestamp",
115             "completion_timestamp",
116             "time_spent",
117             "progress",
118             "kind",
119         ),
120     },
121 }
122 
123 
124 def csv_file_generator(log_type, filepath, overwrite=False):
125     if log_type not in ("summary", "session"):
126         raise ValueError(
127             "Impossible to create a csv export file for {}".format(log_type)
128         )
129 
130     log_info = classes_info[log_type]
131 
132     if not overwrite and os.path.exists(filepath):
133         raise ValueError("{} already exists".format(filepath))
134     queryset = log_info["queryset"]
135 
136     # Exclude completion timestamp for the sessionlog CSV
137     header_labels = tuple(
138         label
139         for label in labels.values()
140         if log_type == "summary" or label != "completion_timestamp"
141     )
142 
143     if sys.version_info[0] < 3:
144         csv_file = io.open(filepath, "wb")
145     else:
146         csv_file = io.open(filepath, "w", newline="")
147 
148     with csv_file as f:
149         writer = csv.DictWriter(f, header_labels)
150         logger.info("Creating csv file {filename}".format(filename=filepath))
151         writer.writeheader()
152         for item in queryset.select_related("user", "user__facility").values(
153             *log_info["db_columns"]
154         ):
155             writer.writerow(map_object(item))
156             yield
157 
158 
159 def exported_logs_info(request):
160     """
161     Get the last modification timestamp of the summary logs exported
162 
163     :returns: An object with the files informatin
164     """
165 
166     logs_dir = os.path.join(conf.KOLIBRI_HOME, "log_export")
167     csv_statuses = {}
168     csv_export_filenames = {
169         "session": "content_session_logs.csv",
170         "summary": "content_summary_logs.csv",
171     }
172     for log_type in csv_export_filenames.keys():
173         log_path = os.path.join(logs_dir, csv_export_filenames[log_type])
174         if os.path.exists(log_path):
175             csv_statuses[log_type] = os.path.getmtime(log_path)
176         else:
177             csv_statuses[log_type] = None
178 
179     return HttpResponse(json.dumps(csv_statuses), content_type="application/json")
180 
181 
182 def download_csv_file(request, log_type):
183     csv_export_filenames = {
184         "session": "content_session_logs.csv",
185         "summary": "content_summary_logs.csv",
186     }
187     if log_type in csv_export_filenames.keys():
188         filepath = os.path.join(
189             conf.KOLIBRI_HOME, "log_export", csv_export_filenames[log_type]
190         )
191     else:
192         filepath = None
193 
194     # if the file does not exist on disk, return a 404
195     if filepath is None or not os.path.exists(filepath):
196         raise Http404("There is no csv export file for {} available".format(log_type))
197 
198     # generate a file response
199     response = FileResponse(io.open(filepath, "rb"))
200     # set the content-type by guessing from the filename
201     response["Content-Type"] = "text/csv"
202 
203     # set the content-disposition as attachment to force download
204     response["Content-Disposition"] = "attachment; filename={}".format(
205         csv_export_filenames[log_type]
206     )
207 
208     # set the content-length to the file size
209     response["Content-Length"] = os.path.getsize(filepath)
210 
211     return response
212
```
--- 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/kolibri/core/logger/csv_export.py b/kolibri/core/logger/csv_export.py
--- a/kolibri/core/logger/csv_export.py
+++ b/kolibri/core/logger/csv_export.py
@@ -13,6 +13,10 @@
 from django.http import Http404
 from django.http import HttpResponse
 from django.http.response import FileResponse
+from django.template.defaultfilters import slugify
+from django.utils import translation
+from django.utils.translation import get_language_from_request
+from django.utils.translation import pgettext
 
 from .models import ContentSessionLog
 from .models import ContentSummaryLog
@@ -180,6 +184,29 @@
 
 
 def download_csv_file(request, log_type):
+    locale = get_language_from_request(request)
+    translation.activate(locale)
+
+    csv_translated_filenames = {
+        "session": (
+            slugify(
+                pgettext(
+                    "Default name for the exported CSV file with content session logs. Please keep the underscores between words in the translation",
+                    "content_session_logs",
+                )
+            )
+            + ".csv"
+        ).replace("-", "_"),
+        "summary": (
+            slugify(
+                pgettext(
+                    "Default name for the exported CSV file with content summary logs. Please keep the underscores between words in the translation",
+                    "content_summary_logs",
+                )
+            )
+            + ".csv"
+        ).replace("-", "_"),
+    }
     csv_export_filenames = {
         "session": "content_session_logs.csv",
         "summary": "content_summary_logs.csv",
@@ -202,8 +229,9 @@
 
     # set the content-disposition as attachment to force download
     response["Content-Disposition"] = "attachment; filename={}".format(
-        csv_export_filenames[log_type]
+        str(csv_translated_filenames[log_type])
     )
+    translation.deactivate()
 
     # set the content-length to the file size
     response["Content-Length"] = os.path.getsize(filepath)
diff --git a/kolibri/plugins/facility/views.py b/kolibri/plugins/facility/views.py
--- a/kolibri/plugins/facility/views.py
+++ b/kolibri/plugins/facility/views.py
@@ -8,7 +8,11 @@
 
 from django.http import Http404
 from django.http.response import FileResponse
+from django.template.defaultfilters import slugify
+from django.utils import translation
 from django.utils.decorators import method_decorator
+from django.utils.translation import get_language_from_request
+from django.utils.translation import pgettext
 from django.views.generic.base import TemplateView
 
 from kolibri.core.decorators import cache_no_user_data
@@ -21,6 +25,8 @@
 
 
 def download_csv_file(request, filename):
+    locale = get_language_from_request(request)
+    translation.activate(locale)
     filepath = os.path.join(conf.KOLIBRI_HOME, "temp", filename)
 
     # if the file does not exist on disk, return a 404
@@ -33,11 +39,21 @@
     response["Content-Type"] = "text/csv"
 
     # set the content-disposition as attachment to force download
-    response["Content-Disposition"] = "attachment; filename=users_{}.csv".format(
-        datetime.now().strftime("%Y%m%d_%H%M%S")
+    exported_filename = (
+        slugify(
+            pgettext(
+                "Default name for the exported CSV file of facility user data. Please keep the underscore between words in the translation",
+                "users_{}",
+            ).format(datetime.now().strftime("%Y%m%d_%H%M%S"))
+        ).replace("-", "_")
+        + ".csv"
+    )
+    response["Content-Disposition"] = "attachment; filename={}".format(
+        str(exported_filename)
     )
 
     # set the content-length to the file size
     response["Content-Length"] = os.path.getsize(filepath)
+    translation.deactivate()
 
     return response

{"golden_diff": "diff --git a/kolibri/core/logger/csv_export.py b/kolibri/core/logger/csv_export.py\n--- a/kolibri/core/logger/csv_export.py\n+++ b/kolibri/core/logger/csv_export.py\n@@ -13,6 +13,10 @@\n from django.http import Http404\n from django.http import HttpResponse\n from django.http.response import FileResponse\n+from django.template.defaultfilters import slugify\n+from django.utils import translation\n+from django.utils.translation import get_language_from_request\n+from django.utils.translation import pgettext\n \n from .models import ContentSessionLog\n from .models import ContentSummaryLog\n@@ -180,6 +184,29 @@\n \n \n def download_csv_file(request, log_type):\n+    locale = get_language_from_request(request)\n+    translation.activate(locale)\n+\n+    csv_translated_filenames = {\n+        \"session\": (\n+            slugify(\n+                pgettext(\n+                    \"Default name for the exported CSV file with content session logs. Please keep the underscores between words in the translation\",\n+                    \"content_session_logs\",\n+                )\n+            )\n+            + \".csv\"\n+        ).replace(\"-\", \"_\"),\n+        \"summary\": (\n+            slugify(\n+                pgettext(\n+                    \"Default name for the exported CSV file with content summary logs. Please keep the underscores between words in the translation\",\n+                    \"content_summary_logs\",\n+                )\n+            )\n+            + \".csv\"\n+        ).replace(\"-\", \"_\"),\n+    }\n     csv_export_filenames = {\n         \"session\": \"content_session_logs.csv\",\n         \"summary\": \"content_summary_logs.csv\",\n@@ -202,8 +229,9 @@\n \n     # set the content-disposition as attachment to force download\n     response[\"Content-Disposition\"] = \"attachment; filename={}\".format(\n-        csv_export_filenames[log_type]\n+        str(csv_translated_filenames[log_type])\n     )\n+    translation.deactivate()\n \n     # set the content-length to the file size\n     response[\"Content-Length\"] = os.path.getsize(filepath)\ndiff --git a/kolibri/plugins/facility/views.py b/kolibri/plugins/facility/views.py\n--- a/kolibri/plugins/facility/views.py\n+++ b/kolibri/plugins/facility/views.py\n@@ -8,7 +8,11 @@\n \n from django.http import Http404\n from django.http.response import FileResponse\n+from django.template.defaultfilters import slugify\n+from django.utils import translation\n from django.utils.decorators import method_decorator\n+from django.utils.translation import get_language_from_request\n+from django.utils.translation import pgettext\n from django.views.generic.base import TemplateView\n \n from kolibri.core.decorators import cache_no_user_data\n@@ -21,6 +25,8 @@\n \n \n def download_csv_file(request, filename):\n+    locale = get_language_from_request(request)\n+    translation.activate(locale)\n     filepath = os.path.join(conf.KOLIBRI_HOME, \"temp\", filename)\n \n     # if the file does not exist on disk, return a 404\n@@ -33,11 +39,21 @@\n     response[\"Content-Type\"] = \"text/csv\"\n \n     # set the content-disposition as attachment to force download\n-    response[\"Content-Disposition\"] = \"attachment; filename=users_{}.csv\".format(\n-        datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n+    exported_filename = (\n+        slugify(\n+            pgettext(\n+                \"Default name for the exported CSV file of facility user data. Please keep the underscore between words in the translation\",\n+                \"users_{}\",\n+            ).format(datetime.now().strftime(\"%Y%m%d_%H%M%S\"))\n+        ).replace(\"-\", \"_\")\n+        + \".csv\"\n+    )\n+    response[\"Content-Disposition\"] = \"attachment; filename={}\".format(\n+        str(exported_filename)\n     )\n \n     # set the content-length to the file size\n     response[\"Content-Length\"] = os.path.getsize(filepath)\n+    translation.deactivate()\n \n     return response\n", "issue": "names of CSV files should be internationalized\n\r\n### Observed behavior\r\n\r\nThe names of files when exporting and downloading from the facility data page are all in English:\r\n\r\n> ![image](https://user-images.githubusercontent.com/2367265/81123607-52be9500-8ee8-11ea-907b-ab8300d24f20.png)\r\n\r\n![image](https://user-images.githubusercontent.com/2367265/81123553-302c7c00-8ee8-11ea-8bb6-ea54ed2e376d.png)\r\n\r\nref: https://github.com/learningequality/kolibri/pull/6835\r\n\r\n### Expected behavior\r\n\r\nNames of files should be in the user's currently selected language. This is the same behavior as currently exists in Coach CSV export.\r\n\r\n### User-facing consequences\r\n\r\nfile name meaning is not understandable in the user's preferred language\r\n\r\n### Context\r\n\r\n0.13.2\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport io\nimport os\nfrom datetime import datetime\n\nfrom django.http import Http404\nfrom django.http.response import FileResponse\nfrom django.utils.decorators import method_decorator\nfrom django.views.generic.base import TemplateView\n\nfrom kolibri.core.decorators import cache_no_user_data\nfrom kolibri.utils import conf\n\n\n@method_decorator(cache_no_user_data, name=\"dispatch\")\nclass FacilityManagementView(TemplateView):\n    template_name = \"facility_management.html\"\n\n\ndef download_csv_file(request, filename):\n    filepath = os.path.join(conf.KOLIBRI_HOME, \"temp\", filename)\n\n    # if the file does not exist on disk, return a 404\n    if filepath is None or not os.path.exists(filepath):\n        raise Http404(\"Creation of users export file has failed\")\n\n    # generate a file response\n    response = FileResponse(io.open(filepath, \"rb\"))\n    # set the content-type by guessing from the filename\n    response[\"Content-Type\"] = \"text/csv\"\n\n    # set the content-disposition as attachment to force download\n    response[\"Content-Disposition\"] = \"attachment; filename=users_{}.csv\".format(\n        datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n    )\n\n    # set the content-length to the file size\n    response[\"Content-Length\"] = os.path.getsize(filepath)\n\n    return response\n", "path": "kolibri/plugins/facility/views.py"}, {"content": "from __future__ import unicode_literals\n\nimport csv\nimport io\nimport json\nimport logging\nimport math\nimport os\nimport sys\nfrom collections import OrderedDict\n\nfrom django.core.cache import cache\nfrom django.http import Http404\nfrom django.http import HttpResponse\nfrom django.http.response import FileResponse\n\nfrom .models import ContentSessionLog\nfrom .models import ContentSummaryLog\nfrom kolibri.core.content.models import ChannelMetadata\nfrom kolibri.core.content.models import ContentNode\nfrom kolibri.utils import conf\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef cache_channel_name(obj):\n    channel_id = obj[\"channel_id\"]\n    key = \"{id}_ChannelMetadata_name\".format(id=channel_id)\n    channel_name = cache.get(key)\n    if channel_name is None:\n        try:\n            channel_name = ChannelMetadata.objects.get(id=channel_id)\n        except ChannelMetadata.DoesNotExist:\n            channel_name = \"\"\n        cache.set(key, channel_name, 60 * 10)\n    return channel_name\n\n\ndef cache_content_title(obj):\n    content_id = obj[\"content_id\"]\n    key = \"{id}_ContentNode_title\".format(id=content_id)\n    title = cache.get(key)\n    if title is None:\n        node = ContentNode.objects.filter(content_id=content_id).first()\n        if node:\n            title = node.title\n        else:\n            title = \"\"\n        cache.set(key, title, 60 * 10)\n    return title\n\n\nmappings = {\n    \"channel_name\": cache_channel_name,\n    \"content_title\": cache_content_title,\n    \"time_spent\": lambda x: \"{:.1f}\".format(round(x[\"time_spent\"], 1)),\n    \"progress\": lambda x: \"{:.4f}\".format(math.floor(x[\"progress\"] * 10000.0) / 10000),\n}\n\nlabels = OrderedDict(\n    (\n        (\"user__facility__name\", \"Facility name\"),\n        (\"user__username\", \"Username\"),\n        (\"channel_id\", \"Channel id\"),\n        (\"channel_name\", \"Channel name\"),\n        (\"content_id\", \"Content id\"),\n        (\"content_title\", \"Content title\"),\n        (\"start_timestamp\", \"Time of first interaction\"),\n        (\"end_timestamp\", \"Time of last interaction\"),\n        (\"completion_timestamp\", \"Time of completion\"),\n        (\"time_spent\", \"Time Spent (sec)\"),\n        (\"progress\", \"Progress (0-1)\"),\n        (\"kind\", \"Content kind\"),\n    )\n)\n\n\ndef map_object(obj):\n    mapped_obj = {}\n    for header, label in labels.items():\n        if header in mappings:\n            mapped_obj[label] = mappings[header](obj)\n        elif header in obj:\n            mapped_obj[label] = obj[header]\n    return mapped_obj\n\n\nclasses_info = {\n    \"session\": {\n        \"queryset\": ContentSessionLog.objects.all(),\n        \"filename\": \"content_session_logs.csv\",\n        \"db_columns\": (\n            \"user__username\",\n            \"user__facility__name\",\n            \"channel_id\",\n            \"content_id\",\n            \"start_timestamp\",\n            \"end_timestamp\",\n            \"time_spent\",\n            \"progress\",\n            \"kind\",\n        ),\n    },\n    \"summary\": {\n        \"queryset\": ContentSummaryLog.objects.all(),\n        \"filename\": \"content_summary_logs.csv\",\n        \"db_columns\": (\n            \"user__username\",\n            \"user__facility__name\",\n            \"content_id\",\n            \"channel_id\",\n            \"start_timestamp\",\n            \"end_timestamp\",\n            \"completion_timestamp\",\n            \"time_spent\",\n            \"progress\",\n            \"kind\",\n        ),\n    },\n}\n\n\ndef csv_file_generator(log_type, filepath, overwrite=False):\n    if log_type not in (\"summary\", \"session\"):\n        raise ValueError(\n            \"Impossible to create a csv export file for {}\".format(log_type)\n        )\n\n    log_info = classes_info[log_type]\n\n    if not overwrite and os.path.exists(filepath):\n        raise ValueError(\"{} already exists\".format(filepath))\n    queryset = log_info[\"queryset\"]\n\n    # Exclude completion timestamp for the sessionlog CSV\n    header_labels = tuple(\n        label\n        for label in labels.values()\n        if log_type == \"summary\" or label != \"completion_timestamp\"\n    )\n\n    if sys.version_info[0] < 3:\n        csv_file = io.open(filepath, \"wb\")\n    else:\n        csv_file = io.open(filepath, \"w\", newline=\"\")\n\n    with csv_file as f:\n        writer = csv.DictWriter(f, header_labels)\n        logger.info(\"Creating csv file {filename}\".format(filename=filepath))\n        writer.writeheader()\n        for item in queryset.select_related(\"user\", \"user__facility\").values(\n            *log_info[\"db_columns\"]\n        ):\n            writer.writerow(map_object(item))\n            yield\n\n\ndef exported_logs_info(request):\n    \"\"\"\n    Get the last modification timestamp of the summary logs exported\n\n    :returns: An object with the files informatin\n    \"\"\"\n\n    logs_dir = os.path.join(conf.KOLIBRI_HOME, \"log_export\")\n    csv_statuses = {}\n    csv_export_filenames = {\n        \"session\": \"content_session_logs.csv\",\n        \"summary\": \"content_summary_logs.csv\",\n    }\n    for log_type in csv_export_filenames.keys():\n        log_path = os.path.join(logs_dir, csv_export_filenames[log_type])\n        if os.path.exists(log_path):\n            csv_statuses[log_type] = os.path.getmtime(log_path)\n        else:\n            csv_statuses[log_type] = None\n\n    return HttpResponse(json.dumps(csv_statuses), content_type=\"application/json\")\n\n\ndef download_csv_file(request, log_type):\n    csv_export_filenames = {\n        \"session\": \"content_session_logs.csv\",\n        \"summary\": \"content_summary_logs.csv\",\n    }\n    if log_type in csv_export_filenames.keys():\n        filepath = os.path.join(\n            conf.KOLIBRI_HOME, \"log_export\", csv_export_filenames[log_type]\n        )\n    else:\n        filepath = None\n\n    # if the file does not exist on disk, return a 404\n    if filepath is None or not os.path.exists(filepath):\n        raise Http404(\"There is no csv export file for {} available\".format(log_type))\n\n    # generate a file response\n    response = FileResponse(io.open(filepath, \"rb\"))\n    # set the content-type by guessing from the filename\n    response[\"Content-Type\"] = \"text/csv\"\n\n    # set the content-disposition as attachment to force download\n    response[\"Content-Disposition\"] = \"attachment; filename={}\".format(\n        csv_export_filenames[log_type]\n    )\n\n    # set the content-length to the file size\n    response[\"Content-Length\"] = os.path.getsize(filepath)\n\n    return response\n", "path": "kolibri/core/logger/csv_export.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport io\nimport os\nfrom datetime import datetime\n\nfrom django.http import Http404\nfrom django.http.response import FileResponse\nfrom django.template.defaultfilters import slugify\nfrom django.utils import translation\nfrom django.utils.decorators import method_decorator\nfrom django.utils.translation import get_language_from_request\nfrom django.utils.translation import pgettext\nfrom django.views.generic.base import TemplateView\n\nfrom kolibri.core.decorators import cache_no_user_data\nfrom kolibri.utils import conf\n\n\n@method_decorator(cache_no_user_data, name=\"dispatch\")\nclass FacilityManagementView(TemplateView):\n    template_name = \"facility_management.html\"\n\n\ndef download_csv_file(request, filename):\n    locale = get_language_from_request(request)\n    translation.activate(locale)\n    filepath = os.path.join(conf.KOLIBRI_HOME, \"temp\", filename)\n\n    # if the file does not exist on disk, return a 404\n    if filepath is None or not os.path.exists(filepath):\n        raise Http404(\"Creation of users export file has failed\")\n\n    # generate a file response\n    response = FileResponse(io.open(filepath, \"rb\"))\n    # set the content-type by guessing from the filename\n    response[\"Content-Type\"] = \"text/csv\"\n\n    # set the content-disposition as attachment to force download\n    exported_filename = (\n        slugify(\n            pgettext(\n                \"Default name for the exported CSV file of facility user data. Please keep the underscore between words in the translation\",\n                \"users_{}\",\n            ).format(datetime.now().strftime(\"%Y%m%d_%H%M%S\"))\n        ).replace(\"-\", \"_\")\n        + \".csv\"\n    )\n    response[\"Content-Disposition\"] = \"attachment; filename={}\".format(\n        str(exported_filename)\n    )\n\n    # set the content-length to the file size\n    response[\"Content-Length\"] = os.path.getsize(filepath)\n    translation.deactivate()\n\n    return response\n", "path": "kolibri/plugins/facility/views.py"}, {"content": "from __future__ import unicode_literals\n\nimport csv\nimport io\nimport json\nimport logging\nimport math\nimport os\nimport sys\nfrom collections import OrderedDict\n\nfrom django.core.cache import cache\nfrom django.http import Http404\nfrom django.http import HttpResponse\nfrom django.http.response import FileResponse\nfrom django.template.defaultfilters import slugify\nfrom django.utils import translation\nfrom django.utils.translation import get_language_from_request\nfrom django.utils.translation import pgettext\n\nfrom .models import ContentSessionLog\nfrom .models import ContentSummaryLog\nfrom kolibri.core.content.models import ChannelMetadata\nfrom kolibri.core.content.models import ContentNode\nfrom kolibri.utils import conf\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef cache_channel_name(obj):\n    channel_id = obj[\"channel_id\"]\n    key = \"{id}_ChannelMetadata_name\".format(id=channel_id)\n    channel_name = cache.get(key)\n    if channel_name is None:\n        try:\n            channel_name = ChannelMetadata.objects.get(id=channel_id)\n        except ChannelMetadata.DoesNotExist:\n            channel_name = \"\"\n        cache.set(key, channel_name, 60 * 10)\n    return channel_name\n\n\ndef cache_content_title(obj):\n    content_id = obj[\"content_id\"]\n    key = \"{id}_ContentNode_title\".format(id=content_id)\n    title = cache.get(key)\n    if title is None:\n        node = ContentNode.objects.filter(content_id=content_id).first()\n        if node:\n            title = node.title\n        else:\n            title = \"\"\n        cache.set(key, title, 60 * 10)\n    return title\n\n\nmappings = {\n    \"channel_name\": cache_channel_name,\n    \"content_title\": cache_content_title,\n    \"time_spent\": lambda x: \"{:.1f}\".format(round(x[\"time_spent\"], 1)),\n    \"progress\": lambda x: \"{:.4f}\".format(math.floor(x[\"progress\"] * 10000.0) / 10000),\n}\n\nlabels = OrderedDict(\n    (\n        (\"user__facility__name\", \"Facility name\"),\n        (\"user__username\", \"Username\"),\n        (\"channel_id\", \"Channel id\"),\n        (\"channel_name\", \"Channel name\"),\n        (\"content_id\", \"Content id\"),\n        (\"content_title\", \"Content title\"),\n        (\"start_timestamp\", \"Time of first interaction\"),\n        (\"end_timestamp\", \"Time of last interaction\"),\n        (\"completion_timestamp\", \"Time of completion\"),\n        (\"time_spent\", \"Time Spent (sec)\"),\n        (\"progress\", \"Progress (0-1)\"),\n        (\"kind\", \"Content kind\"),\n    )\n)\n\n\ndef map_object(obj):\n    mapped_obj = {}\n    for header, label in labels.items():\n        if header in mappings:\n            mapped_obj[label] = mappings[header](obj)\n        elif header in obj:\n            mapped_obj[label] = obj[header]\n    return mapped_obj\n\n\nclasses_info = {\n    \"session\": {\n        \"queryset\": ContentSessionLog.objects.all(),\n        \"filename\": \"content_session_logs.csv\",\n        \"db_columns\": (\n            \"user__username\",\n            \"user__facility__name\",\n            \"channel_id\",\n            \"content_id\",\n            \"start_timestamp\",\n            \"end_timestamp\",\n            \"time_spent\",\n            \"progress\",\n            \"kind\",\n        ),\n    },\n    \"summary\": {\n        \"queryset\": ContentSummaryLog.objects.all(),\n        \"filename\": \"content_summary_logs.csv\",\n        \"db_columns\": (\n            \"user__username\",\n            \"user__facility__name\",\n            \"content_id\",\n            \"channel_id\",\n            \"start_timestamp\",\n            \"end_timestamp\",\n            \"completion_timestamp\",\n            \"time_spent\",\n            \"progress\",\n            \"kind\",\n        ),\n    },\n}\n\n\ndef csv_file_generator(log_type, filepath, overwrite=False):\n    if log_type not in (\"summary\", \"session\"):\n        raise ValueError(\n            \"Impossible to create a csv export file for {}\".format(log_type)\n        )\n\n    log_info = classes_info[log_type]\n\n    if not overwrite and os.path.exists(filepath):\n        raise ValueError(\"{} already exists\".format(filepath))\n    queryset = log_info[\"queryset\"]\n\n    # Exclude completion timestamp for the sessionlog CSV\n    header_labels = tuple(\n        label\n        for label in labels.values()\n        if log_type == \"summary\" or label != \"completion_timestamp\"\n    )\n\n    if sys.version_info[0] < 3:\n        csv_file = io.open(filepath, \"wb\")\n    else:\n        csv_file = io.open(filepath, \"w\", newline=\"\")\n\n    with csv_file as f:\n        writer = csv.DictWriter(f, header_labels)\n        logger.info(\"Creating csv file {filename}\".format(filename=filepath))\n        writer.writeheader()\n        for item in queryset.select_related(\"user\", \"user__facility\").values(\n            *log_info[\"db_columns\"]\n        ):\n            writer.writerow(map_object(item))\n            yield\n\n\ndef exported_logs_info(request):\n    \"\"\"\n    Get the last modification timestamp of the summary logs exported\n\n    :returns: An object with the files informatin\n    \"\"\"\n\n    logs_dir = os.path.join(conf.KOLIBRI_HOME, \"log_export\")\n    csv_statuses = {}\n    csv_export_filenames = {\n        \"session\": \"content_session_logs.csv\",\n        \"summary\": \"content_summary_logs.csv\",\n    }\n    for log_type in csv_export_filenames.keys():\n        log_path = os.path.join(logs_dir, csv_export_filenames[log_type])\n        if os.path.exists(log_path):\n            csv_statuses[log_type] = os.path.getmtime(log_path)\n        else:\n            csv_statuses[log_type] = None\n\n    return HttpResponse(json.dumps(csv_statuses), content_type=\"application/json\")\n\n\ndef download_csv_file(request, log_type):\n    locale = get_language_from_request(request)\n    translation.activate(locale)\n\n    csv_translated_filenames = {\n        \"session\": (\n            slugify(\n                pgettext(\n                    \"Default name for the exported CSV file with content session logs. Please keep the underscores between words in the translation\",\n                    \"content_session_logs\",\n                )\n            )\n            + \".csv\"\n        ).replace(\"-\", \"_\"),\n        \"summary\": (\n            slugify(\n                pgettext(\n                    \"Default name for the exported CSV file with content summary logs. Please keep the underscores between words in the translation\",\n                    \"content_summary_logs\",\n                )\n            )\n            + \".csv\"\n        ).replace(\"-\", \"_\"),\n    }\n    csv_export_filenames = {\n        \"session\": \"content_session_logs.csv\",\n        \"summary\": \"content_summary_logs.csv\",\n    }\n    if log_type in csv_export_filenames.keys():\n        filepath = os.path.join(\n            conf.KOLIBRI_HOME, \"log_export\", csv_export_filenames[log_type]\n        )\n    else:\n        filepath = None\n\n    # if the file does not exist on disk, return a 404\n    if filepath is None or not os.path.exists(filepath):\n        raise Http404(\"There is no csv export file for {} available\".format(log_type))\n\n    # generate a file response\n    response = FileResponse(io.open(filepath, \"rb\"))\n    # set the content-type by guessing from the filename\n    response[\"Content-Type\"] = \"text/csv\"\n\n    # set the content-disposition as attachment to force download\n    response[\"Content-Disposition\"] = \"attachment; filename={}\".format(\n        str(csv_translated_filenames[log_type])\n    )\n    translation.deactivate()\n\n    # set the content-length to the file size\n    response[\"Content-Length\"] = os.path.getsize(filepath)\n\n    return response\n", "path": "kolibri/core/logger/csv_export.py"}]}