From 8d30c7f76b0cda7333fe69ea31adb986719aafee Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 12 Jul 2017 09:53:25 +0200 Subject: [PATCH] Adapted student import to new file format --- common/settings.py | 41 +++++++++++++++++++++++-------- stages/models.py | 41 ++++++++++++++++++++++++------- stages/test_files/EXPORT_GAN.xls | Bin 0 -> 9216 bytes stages/tests.py | 29 ++++++++++++++++++++++ stages/views.py | 24 ++++++++++++++---- 5 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 stages/test_files/EXPORT_GAN.xls diff --git a/common/settings.py b/common/settings.py index fc94cf3..7d69991 100644 --- a/common/settings.py +++ b/common/settings.py @@ -118,16 +118,37 @@ ALLOWED_HOSTS = ['localhost', 'stages.pierre-coullery.ch'] # Mapping between column names of a tabular file and Student field names STUDENT_IMPORT_MAPPING = { - 'Num élève': 'ext_id', - 'Nom élève': 'last_name', - 'Prénom élève': 'first_name', - 'Rue élève': 'street', - 'Localité élève': 'city', # pcode is separated from city in prepare_import - 'Tél. élève': 'tel', - 'Natel élève': 'mobile', - 'Email RPN élève': 'email', - 'Date nais. élève': 'birth_date', - 'Classe': 'klass', + 'NO_CLOEE': 'ext_id', + 'NOM': 'last_name', + 'PRENOM': 'first_name', + 'RUE': 'street', + 'LOCALITE': 'city', # pcode is separated from city in prepare_import + 'TEL_PRIVE': 'tel', + 'TEL_MOBILE': 'mobile', + 'EMAIL_RPN': 'email', + 'DATENAI': 'birth_date', + 'NAVS13': 'avs', + 'SEXE': 'gender', + 'NO_EMPLOYEUR' : 'corporation', + 'NO_FORMATEUR' : 'instructor', + 'CLASSE_ACTUELLE': 'klass', +} + +CORPORATION_IMPORT_MAPPING = { + 'NO_EMPLOYEUR' : 'ext_id', + 'EMPLOYEUR' : 'name', + 'RUE_EMPLOYEUR': 'street', + 'LOCALITE_EMPLOYEUR': 'city', + 'TEL_EMPLOYEUR': 'tel', + 'CANTON_EMPLOYEUR' : 'district', +} + +INSTRUCTOR_IMPORT_MAPPING = { + 'NO_FORMATEUR': 'ext_id', + 'NOM_FORMATEUR': 'last_name', + 'PRENOM_FORMATEUR': 'first_name', + 'TEL_FORMATEUR': 'tel', + 'MAIL_FORMATEUR': 'email', } from .local_settings import * diff --git a/stages/models.py b/stages/models.py index 2fcb0a6..c893cdc 100644 --- a/stages/models.py +++ b/stages/models.py @@ -132,19 +132,42 @@ class Student(models.Model): return '%d ans%s' % (age_y, ' %d m.' % age_m if age_m > 0 else '') @classmethod - def prepare_import(cls, values): + def prepare_import(cls, student_values, corp_values, inst_values): ''' Hook for tabimport, before new object get created ''' - if 'klass' in values: + if 'klass' in student_values: try: - k = Klass.objects.get(name=values['klass']) + k = Klass.objects.get(name=student_values['klass']) except Klass.DoesNotExist: - raise Exception("La classe '%s' n'existe pas encore" % values['klass']) - values['klass'] = k + raise Exception("La classe '%s' n'existe pas encore" % student_values['klass']) + student_values['klass'] = k + + if 'corporation' in student_values: + if 'city' in corp_values and is_int(corp_values['city'][:4]): + corp_values['pcode'], _, corp_values['city'] = corp_values['city'].partition(' ') + if student_values['corporation'] != '': + obj, created = Corporation.objects.get_or_create( + ext_id=student_values['corporation'], + defaults = corp_values + ) + inst_values['corporation'] = obj + student_values['corporation'] = obj + else: + student_values['corporation'] = None + + if 'instructor' in student_values: + if student_values['instructor'] != '': + obj, created = CorpContact.objects.get_or_create( + ext_id=student_values['instructor'], + defaults = inst_values + ) + student_values['instructor'] = obj + else: + student_values['instructor'] = None # See if postal code included in city, and split them - if 'city' in values and is_int(values['city'][:4]): - values['pcode'], _, values['city'] = values['city'].partition(' ') - values['archived'] = False - return values + if 'city' in student_values and is_int(student_values['city'][:4]): + student_values['pcode'], _, student_values['city'] = student_values['city'].partition(' ') + student_values['archived'] = False + return student_values class Referent(models.Model): diff --git a/stages/test_files/EXPORT_GAN.xls b/stages/test_files/EXPORT_GAN.xls new file mode 100644 index 0000000000000000000000000000000000000000..6b08ac69180ff38ebf68d50156f8bc029285442d GIT binary patch literal 9216 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 Mk_TI$!zUU4KhJ%aLjV8( literal 0 HcmV?d00001 diff --git a/stages/tests.py b/stages/tests.py index 7e2198c..f663232 100644 --- a/stages/tests.py +++ b/stages/tests.py @@ -1,9 +1,11 @@ import json +import os from datetime import date from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse +from django.utils.html import escape from .models import ( Level, Domain, Section, Klass, Period, Student, Corporation, Availability, @@ -145,3 +147,30 @@ class PeriodTest(TestCase): per = Period.objects.create(title="Week test", section=self.section, level=self.level1, start_date=date(2013, 9, 12), end_date=date(2013, 9, 26)) self.assertEqual(per.weeks, 2) + + +class ImportTests(TestCase): + def setUp(self): + User.objects.create_user('me', 'me@example.org', 'mepassword') + + def test_import_gan(self): + path = os.path.join(os.path.dirname(__file__), 'test_files', 'EXPORT_GAN.xls') + self.client.login(username='me', password='mepassword') + with open(path, 'rb') as fh: + response = self.client.post(reverse('tabimport'), {'upload': fh}, follow=True) + self.assertContains(response, escape("La classe '1ASEFEa' n'existe pas encore")) + + lev1 = Level.objects.create(name='1') + Klass.objects.create( + name='1ASEFEa', + section=Section.objects.create(name='ASE'), + level=lev1, + ) + Klass.objects.create( + name='1EDS', + section=Section.objects.create(name='EDE'), + level=lev1, + ) + with open(path, 'rb') as fh: + response = self.client.post(reverse('tabimport'), {'upload': fh}, follow=True) + self.assertContains(response, "Created objects: 2") diff --git a/stages/views.py b/stages/views.py index 9f81750..fee6e1f 100644 --- a/stages/views.py +++ b/stages/views.py @@ -301,14 +301,28 @@ class StudentImportView(FormView): def import_data(self, up_file): """ Import Student data from uploaded file. """ - mapping = settings.STUDENT_IMPORT_MAPPING - rev_mapping = {v: k for k, v in mapping.items()} + student_mapping = settings.STUDENT_IMPORT_MAPPING + student_rev_mapping = {v: k for k, v in student_mapping.items()} + corporation_mapping = settings.CORPORATION_IMPORT_MAPPING + instructor_mapping = settings.INSTRUCTOR_IMPORT_MAPPING + obj_created = obj_modified = 0 for line in up_file: - defaults = dict((val, line[key]) for key, val in mapping.items() if val != 'ext_id') - defaults = Student.prepare_import(defaults) + student_defaults = { + val: line[key] for key, val in student_mapping.items() if val != 'ext_id' + } + corporation_defaults = { + val: line[key] for key, val in corporation_mapping.items() + } + instructor_defaults = { + val: line[key] for key, val in instructor_mapping.items() + } + + defaults = Student.prepare_import( + student_defaults, corporation_defaults, instructor_defaults + ) obj, created = Student.objects.get_or_create( - ext_id=line[rev_mapping['ext_id']], defaults=defaults) + ext_id=line[student_rev_mapping['ext_id']], defaults=defaults) if not created: for key, val in defaults.items(): setattr(obj, key, val)