diff --git a/kolibri/core/logger/csv_export.py b/kolibri/core/logger/csv_export.py index 61d6c122e00..7a612b98374 100644 --- a/kolibri/core/logger/csv_export.py +++ b/kolibri/core/logger/csv_export.py @@ -2,14 +2,15 @@ import csv import datetime +import io import logging import math -import os from collections import OrderedDict from functools import partial from dateutil import parser from django.core.cache import cache +from django.core.files.storage import DefaultStorage from django.utils.translation import gettext_lazy as _ from django.utils.translation import pgettext_lazy from le_utils.constants import content_kinds @@ -18,7 +19,6 @@ from .models import ContentSummaryLog from kolibri.core.content.models import ChannelMetadata from kolibri.core.content.models import ContentNode -from kolibri.core.utils.csv import open_csv_for_writing from kolibri.core.utils.csv import output_mapper @@ -146,6 +146,7 @@ def cache_content_title(obj): def csv_file_generator( facility, log_type, filepath, start_date, end_date, overwrite=False ): + file_storage = DefaultStorage() if log_type not in ("summary", "session"): raise ValueError( @@ -160,8 +161,9 @@ def csv_file_generator( else parser.parse(end_date) + datetime.timedelta(days=1) ) - if not overwrite and os.path.exists(filepath): - raise ValueError("{} already exists".format(filepath)) + filename = file_storage.generate_filename(filepath.split("/")[-1]) + if not overwrite and file_storage.exists(filename): + raise ValueError("{} already exists".format(filename)) queryset = log_info["queryset"].filter( dataset_id=facility.dataset_id, ) @@ -179,14 +181,28 @@ def csv_file_generator( if log_type == "summary" or label != labels["completion_timestamp"] ) - csv_file = open_csv_for_writing(filepath) + csv_file = io.BytesIO() with csv_file as f: - writer = csv.DictWriter(f, header_labels) - logger.info("Creating csv file {filename}".format(filename=filepath)) + writer = csv.DictWriter(io.TextIOWrapper(f, encoding="utf-8"), header_labels) + logger.info( + "Creating {logtype} csv file {filename}".format( + logtype=log_type, filename=filename + ) + ) writer.writeheader() for item in queryset.select_related("user", "user__facility").values( *log_info["db_columns"] ): writer.writerow(map_object(item)) yield + + f.seek(0) + file = file_storage.save(filename, f) + + try: + # If the file is local, we can get the path + logger.info("File saved - Path: {}".format(file_storage.path(file))) + except NotImplementedError: + # But if path is not implemented, we assume we can get the URL + logger.info("File saved - Path: {}".format(file_storage.url(file)))