From 66d890f1cf02f48d5425bb031ac0d61580d17dd1 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Wed, 18 Nov 2020 12:30:18 +0100 Subject: [PATCH] SubjectImportReader requires SubjectImportData parameter --- .../web/importer/csv_subject_import_reader.py | 5 +- .../importer/csv_tns_subject_import_reader.py | 9 ++- smash/web/importer/importer.py | 4 +- smash/web/importer/importer_cron_job.py | 73 ++++++++++--------- smash/web/importer/subject_import_reader.py | 6 ++ .../0180_visitimportdata_migration.py | 29 +++++++- .../test_csv_subject_import_reader.py | 15 ++-- .../tests/importer/test_importer_cron_job.py | 18 ++--- .../test_tns_csv_subject_import_reader.py | 10 ++- 9 files changed, 107 insertions(+), 62 deletions(-) diff --git a/smash/web/importer/csv_subject_import_reader.py b/smash/web/importer/csv_subject_import_reader.py index 9f95f0c3..77c71ab6 100644 --- a/smash/web/importer/csv_subject_import_reader.py +++ b/smash/web/importer/csv_subject_import_reader.py @@ -3,7 +3,7 @@ import datetime import logging from .subject_import_reader import SubjectImportReader -from web.models import StudySubject, Subject, Study +from web.models import StudySubject, Subject, Study, SubjectImportData from web.models.constants import GLOBAL_STUDY_ID CSV_DATE_FORMAT = "%d-%m-%Y" @@ -12,7 +12,8 @@ logger = logging.getLogger(__name__) class CsvSubjectImportReader(SubjectImportReader): - def __init__(self): + def __init__(self, import_data: SubjectImportData): + super().__init__(import_data) self.study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0] def load_data(self, filename): diff --git a/smash/web/importer/csv_tns_subject_import_reader.py b/smash/web/importer/csv_tns_subject_import_reader.py index c20862be..38c539a9 100644 --- a/smash/web/importer/csv_tns_subject_import_reader.py +++ b/smash/web/importer/csv_tns_subject_import_reader.py @@ -1,11 +1,11 @@ +import codecs import csv import datetime import logging -import codecs +from web.models import StudySubject, Subject, Study, SubjectImportData +from web.models.constants import GLOBAL_STUDY_ID from .subject_import_reader import SubjectImportReader -from ..models import StudySubject, Subject, Study -from ..models.constants import GLOBAL_STUDY_ID CSV_DATE_FORMAT = "%d/%m/%Y" @@ -13,7 +13,8 @@ logger = logging.getLogger(__name__) class TnsCsvSubjectImportReader(SubjectImportReader): - def __init__(self): + def __init__(self, import_data: SubjectImportData): + super().__init__(import_data) self.study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0] def load_data(self, filename): diff --git a/smash/web/importer/importer.py b/smash/web/importer/importer.py index 00d2bf1d..95b42414 100644 --- a/smash/web/importer/importer.py +++ b/smash/web/importer/importer.py @@ -14,8 +14,7 @@ logger = logging.getLogger(__name__) class Importer(object): - def __init__(self, filename, reader): - # type: (str, SubjectImportReader) -> None + def __init__(self, filename, reader: SubjectImportReader): self.filename = filename self.reader = reader self.added_count = 0 @@ -34,7 +33,6 @@ class Importer(object): else: self.importer_user = Worker.objects.filter(user=user) - def execute(self): self.added_count = 0 self.problematic_count = 0 diff --git a/smash/web/importer/importer_cron_job.py b/smash/web/importer/importer_cron_job.py index ce0bdfa8..9eadfafb 100644 --- a/smash/web/importer/importer_cron_job.py +++ b/smash/web/importer/importer_cron_job.py @@ -9,9 +9,8 @@ import timeout_decorator from django.db import OperationalError, ProgrammingError from django_cron import CronJobBase, Schedule -from web.models import ConfigurationItem, Study, VisitImportData -from web.models.constants import CRON_JOB_TIMEOUT, DEFAULT_FROM_EMAIL, DAILY_SUBJECT_IMPORT_FILE, \ - SUBJECT_IMPORT_RUN_AT, GLOBAL_STUDY_ID +from web.models import ConfigurationItem, Study, VisitImportData, SubjectImportData +from web.models.constants import CRON_JOB_TIMEOUT, DEFAULT_FROM_EMAIL, GLOBAL_STUDY_ID from web.smash_email import EmailSender from .csv_tns_subject_import_reader import TnsCsvSubjectImportReader from .csv_tns_visit_import_reader import TnsCsvVisitImportReader @@ -23,46 +22,55 @@ logger = logging.getLogger(__name__) class SubjectImporterCronJob(CronJobBase): RUN_AT_TIMES = [] try: - item = ConfigurationItem.objects.filter(type=SUBJECT_IMPORT_RUN_AT).first() + item = SubjectImportData.objects.filter(study_id=GLOBAL_STUDY_ID).first() if item is not None: - RUN_AT_TIMES = item.value.split(';') + RUN_AT_TIMES = item.run_at_times.split(';') except (OperationalError, ProgrammingError): # sqlite and postgres throw different errors here logger.debug('Looks like db is not initialized') schedule = Schedule(run_at_times=RUN_AT_TIMES) code = 'web.import_subjects_daily_job' # a unique code + def __init__(self, study_id=GLOBAL_STUDY_ID): + super().__init__() + self.study = Study.objects.get(pk=study_id) + @timeout_decorator.timeout(CRON_JOB_TIMEOUT) def do(self): email_title = "Subjects daily import" email_recipients = ConfigurationItem.objects.get(type=DEFAULT_FROM_EMAIL).value - filename = ConfigurationItem.objects.get(type=DAILY_SUBJECT_IMPORT_FILE).value - - if filename is None or filename == '': - logger.info("Importing subjects skipped. File not defined ") - return "import file not defined" - logger.info("Importing subjects from file: " + filename) - if not os.path.isfile(filename): - EmailSender().send_email(email_title, - "<h3><font color='red'>File with imported data is not available in the system: " + filename + "</font></h3>", - email_recipients) - return "import file not found" - try: - importer = Importer(filename, TnsCsvSubjectImportReader()) - importer.execute() - email_body = importer.get_summary() - EmailSender().send_email(email_title, - "<h3>Data was successfully imported from file: " + filename + "</h3>" + email_body, - email_recipients) - backup_file(filename) - return "import is successful" - - except: - tb = traceback.format_exc() - EmailSender().send_email(email_title, - "<h3><font color='red'>There was a problem with importing data from file: " + filename + "</font></h3><pre>" + tb + "</pre>", - email_recipients) - return "import crashed" + for import_data in SubjectImportData.objects.filter(study=self.study).all(): + if import_data.filename is None or import_data.filename == '': + logger.info("Importing subjects skipped. File not defined ") + return "import file not defined" + filename = import_data.get_absolute_file_path() + logger.info("Importing subjects from file: " + filename) + if not os.path.isfile(filename): + content = "<h3><font color='red'>File with imported data is not available in the system: " + \ + filename + "</font></h3>" + EmailSender().send_email(email_title, + content, + email_recipients) + return "import file not found" + # noinspection PyBroadException + try: + importer = Importer(filename, TnsCsvSubjectImportReader(import_data)) + importer.execute() + email_body = importer.get_summary() + EmailSender().send_email(email_title, + "<h3>Data was successfully imported from file: " + filename + "</h3>" + + email_body, + email_recipients) + backup_file(filename) + return "import is successful" + + except BaseException: + tb = traceback.format_exc() + EmailSender().send_email(email_title, + "<h3><font color='red'>There was a problem with importing data from file: " + + filename + "</font></h3><pre>" + tb + "</pre>", + email_recipients) + return "import crashed" class VisitImporterCronJob(CronJobBase): @@ -116,7 +124,6 @@ class VisitImporterCronJob(CronJobBase): "<h3><font color='red'>There was a problem with importing data from file: " + filename + "</font></h3><pre>" + tb + "</pre>", email_recipients) - print(tb) return "import crashed" diff --git a/smash/web/importer/subject_import_reader.py b/smash/web/importer/subject_import_reader.py index 0bc7f898..187c3d03 100644 --- a/smash/web/importer/subject_import_reader.py +++ b/smash/web/importer/subject_import_reader.py @@ -1,7 +1,13 @@ +from typing import List + +from web.models import SubjectImportData from web.models.study_subject import StudySubject class SubjectImportReader: + def __init__(self, import_data: SubjectImportData): + self.import_data = import_data + def load_data(self, filename): # type: (str) -> List [StudySubject] pass diff --git a/smash/web/migrations/0180_visitimportdata_migration.py b/smash/web/migrations/0180_visitimportdata_migration.py index 364f5f73..8e13d955 100644 --- a/smash/web/migrations/0180_visitimportdata_migration.py +++ b/smash/web/migrations/0180_visitimportdata_migration.py @@ -1,7 +1,7 @@ from django.db import migrations from web.models.constants import IMPORTER_USER, GLOBAL_STUDY_ID, IMPORT_APPOINTMENT_TYPE, DAILY_VISIT_IMPORT_FILE, \ - VISIT_IMPORT_RUN_AT + VISIT_IMPORT_RUN_AT, DAILY_SUBJECT_IMPORT_FILE, SUBJECT_IMPORT_RUN_AT def get_val(apps, item_type: str): @@ -47,6 +47,32 @@ def create_visit_import_data(apps, schema_editor): entry.save() +# noinspection PyUnusedLocal +def create_subject_import_data(apps, schema_editor): + # noinspection PyPep8Naming + SubjectImportData = apps.get_model("web", "SubjectImportData") + # noinspection PyPep8Naming + Study = apps.get_model("web", "Study") + # noinspection PyPep8Naming + Worker = apps.get_model("web", "Worker") + + entry = SubjectImportData() + entry.study = Study.objects.get(pk=GLOBAL_STUDY_ID) + importer_user_name = get_val(apps, IMPORTER_USER) + import_file = get_val(apps, DAILY_SUBJECT_IMPORT_FILE) + import_file_run_at = get_val(apps, SUBJECT_IMPORT_RUN_AT) + + if importer_user_name is not None: + workers = Worker.objects.filter(user__username=importer_user_name) + + if len(workers) > 0: + entry.worker = workers[0] + + entry.filename = import_file + entry.run_at_times = import_file_run_at + entry.save() + + class Migration(migrations.Migration): dependencies = [ ('web', '0179_visitimportdata'), @@ -54,4 +80,5 @@ class Migration(migrations.Migration): operations = [ migrations.RunPython(create_visit_import_data), + migrations.RunPython(create_subject_import_data), ] diff --git a/smash/web/tests/importer/test_csv_subject_import_reader.py b/smash/web/tests/importer/test_csv_subject_import_reader.py index a80f3234..3f11f773 100644 --- a/smash/web/tests/importer/test_csv_subject_import_reader.py +++ b/smash/web/tests/importer/test_csv_subject_import_reader.py @@ -4,17 +4,21 @@ import logging from django.test import TestCase +from web.models import SubjectImportData from web.importer import CsvSubjectImportReader -from web.tests.functions import get_resource_path +from web.tests.functions import get_resource_path, get_test_study logger = logging.getLogger(__name__) class TestCsvReader(TestCase): + def setUp(self): + self.study_import_data = SubjectImportData.objects.create(study=get_test_study()) def test_load_data(self): - filename = get_resource_path('import.csv') - study_subjects = CsvSubjectImportReader().load_data(filename) + self.study_import_data.filename = get_resource_path('import.csv') + study_subjects = CsvSubjectImportReader(self.study_import_data).load_data(self.study_import_data.filename) + self.assertEqual(1, len(study_subjects)) study_subject = study_subjects[0] self.assertEqual("Piotr", study_subject.subject.first_name) @@ -29,8 +33,9 @@ class TestCsvReader(TestCase): self.assertIsNotNone(study_subject.study) def test_load_problematic_dates(self): - filename = get_resource_path('import_date_of_birth.csv') - study_subjects = CsvSubjectImportReader().load_data(filename) + self.study_import_data.filename = get_resource_path('import_date_of_birth.csv') + study_subjects = CsvSubjectImportReader(self.study_import_data).load_data(self.study_import_data.filename) + self.assertEqual(3, len(study_subjects)) self.assertIsNone(study_subjects[0].subject.date_born) self.assertIsNone(study_subjects[1].subject.date_born) diff --git a/smash/web/tests/importer/test_importer_cron_job.py b/smash/web/tests/importer/test_importer_cron_job.py index e1b95470..55ae4b09 100644 --- a/smash/web/tests/importer/test_importer_cron_job.py +++ b/smash/web/tests/importer/test_importer_cron_job.py @@ -11,8 +11,7 @@ from django.test import TestCase from django_cron.models import CronJobLog from web.importer import SubjectImporterCronJob, VisitImporterCronJob -from web.models import ConfigurationItem, Visit, VisitImportData -from web.models.constants import DAILY_SUBJECT_IMPORT_FILE +from web.models import Visit, VisitImportData, SubjectImportData from web.tests.functions import get_resource_path, get_test_study, create_appointment_type, create_worker logger = logging.getLogger(__name__) @@ -27,9 +26,11 @@ class TestCronJobImporter(TestCase): self.visit_import_data = VisitImportData.objects.create(study=self.study, appointment_type=create_appointment_type(), import_worker=create_worker()) + self.subject_import_data = SubjectImportData.objects.create(study=self.study, + import_worker=create_worker()) + CronJobLog.objects.all().delete() def test_import_without_configuration(self): - CronJobLog.objects.all().delete() job = SubjectImporterCronJob() @@ -43,14 +44,12 @@ class TestCronJobImporter(TestCase): new_file, tmp = tempfile.mkstemp() copyfile(filename, tmp) - conf = ConfigurationItem.objects.get(type=DAILY_SUBJECT_IMPORT_FILE) - conf.value = tmp - conf.save() - CronJobLog.objects.all().delete() + settings.ETL_ROOT = os.path.dirname(tmp) - job = SubjectImporterCronJob() + self.subject_import_data.filename = os.path.basename(tmp) + self.subject_import_data.save() - status = job.do() + status = SubjectImporterCronJob(self.study.id).do() self.assertEqual("import is successful", status) self.assertEqual(1, len(mail.outbox)) @@ -64,7 +63,6 @@ class TestCronJobImporter(TestCase): self.visit_import_data.filename = os.path.basename(tmp) self.visit_import_data.save() - CronJobLog.objects.all().delete() job = VisitImporterCronJob(study_id=self.study.id) diff --git a/smash/web/tests/importer/test_tns_csv_subject_import_reader.py b/smash/web/tests/importer/test_tns_csv_subject_import_reader.py index e9d46d49..28e5bad5 100644 --- a/smash/web/tests/importer/test_tns_csv_subject_import_reader.py +++ b/smash/web/tests/importer/test_tns_csv_subject_import_reader.py @@ -4,9 +4,10 @@ import logging from django.test import TestCase -from web.importer import TnsCsvSubjectImportReader -from web.tests.functions import get_resource_path from web.importer import MsgCounterHandler +from web.importer import TnsCsvSubjectImportReader +from web.models import SubjectImportData +from web.tests.functions import get_resource_path, get_test_study logger = logging.getLogger(__name__) @@ -14,6 +15,7 @@ logger = logging.getLogger(__name__) class TestTnsCsvSubjectReader(TestCase): def setUp(self): + self.study_import_data = SubjectImportData.objects.create(study=get_test_study()) self.warning_counter = MsgCounterHandler() logging.getLogger('').addHandler(self.warning_counter) @@ -21,8 +23,8 @@ class TestTnsCsvSubjectReader(TestCase): logging.getLogger('').removeHandler(self.warning_counter) def test_load_data(self): - filename = get_resource_path('tns_subjects_import.csv') - study_subjects = TnsCsvSubjectImportReader().load_data(filename) + self.study_import_data.filename = get_resource_path('tns_subjects_import.csv') + study_subjects = TnsCsvSubjectImportReader(self.study_import_data).load_data(self.study_import_data.filename) self.assertEqual(3, len(study_subjects)) study_subject = study_subjects[1] self.assertEqual("John2", study_subject.subject.first_name) -- GitLab