diff --git a/common/settings.py b/common/settings.py index c854cf9..ec5c2a0 100644 --- a/common/settings.py +++ b/common/settings.py @@ -118,7 +118,7 @@ ALLOWED_HOSTS = ['localhost', 'stages.pierre-coullery.ch'] # Mapping between column names of a tabular file and Student field names STUDENT_IMPORT_MAPPING = { - 'NO_CLOEE': 'ext_id', + 'NOCLOEE': 'ext_id', 'NOM': 'last_name', 'PRENOM': 'first_name', 'RUE': 'street', @@ -129,8 +129,6 @@ STUDENT_IMPORT_MAPPING = { 'DATENAI': 'birth_date', 'NAVS13': 'avs', 'SEXE': 'gender', - 'NO_EMPLOYEUR' : 'corporation', - 'NO_FORMATEUR' : 'instructor', 'CLASSE_ACTUELLE': 'klass', } diff --git a/stages/models.py b/stages/models.py index 9744aca..932ab42 100644 --- a/stages/models.py +++ b/stages/models.py @@ -6,14 +6,6 @@ from django.db import models from . import utils -def is_int(s): - try: - int(s) - return True - except ValueError: - return False - - class Section(models.Model): """ Filières """ name = models.CharField(max_length=20, verbose_name='Nom') @@ -156,7 +148,7 @@ class Student(models.Model): return '%d ans%s' % (age_y, ' %d m.' % age_m if age_m > 0 else '') @classmethod - def prepare_import(cls, student_values, corp_values, inst_values): + def prepare_import(cls, student_values): ''' Hook for tabimport, before new object get created ''' if 'klass' in student_values: try: @@ -165,30 +157,8 @@ class Student(models.Model): 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 student_values and is_int(student_values['city'][:4]): + if 'city' in student_values and utils.is_int(student_values['city'][:4]): student_values['pcode'], _, student_values['city'] = student_values['city'].partition(' ') student_values['archived'] = False return student_values diff --git a/stages/test_files/EXPORT_GAN.xlsx b/stages/test_files/EXPORT_GAN.xlsx index 9dbc23e..9e22173 100644 Binary files a/stages/test_files/EXPORT_GAN.xlsx and b/stages/test_files/EXPORT_GAN.xlsx differ diff --git a/stages/tests.py b/stages/tests.py index 1c03dee..d7ddb0e 100644 --- a/stages/tests.py +++ b/stages/tests.py @@ -3,7 +3,7 @@ import os from datetime import date from django.contrib.auth.models import User -from django.test import TestCase +from django.test import TestCase, override_settings from django.urls import reverse from django.utils.html import escape @@ -180,7 +180,8 @@ class ImportTests(TestCase): self.client.login(username='me', password='mepassword') with open(path, 'rb') as fh: response = self.client.post(reverse('import-students'), {'upload': fh}, follow=True) - self.assertContains(response, escape("La classe '1ASEFEa' n'existe pas encore")) + msg = "\n".join(str(m) for m in response.context['messages']) + self.assertIn("La classe '1ASEFEa' n'existe pas encore", msg) lev1 = Level.objects.create(name='1') Klass.objects.create( @@ -193,9 +194,10 @@ class ImportTests(TestCase): section=Section.objects.create(name='EDE'), level=lev1, ) - with open(path, 'rb') as fh: + with open(path, 'rb') as fh: # , override_settings(DEBUG=True): response = self.client.post(reverse('import-students'), {'upload': fh}, follow=True) - self.assertContains(response, "Created objects: 2") + msg = "\n".join(str(m) for m in response.context['messages']) + self.assertIn("Created objects: 2", msg) def test_import_hp(self): teacher = Teacher.objects.create( diff --git a/stages/utils.py b/stages/utils.py index 7e39332..01fb4b2 100644 --- a/stages/utils.py +++ b/stages/utils.py @@ -12,3 +12,11 @@ def school_year(date, as_tuple=False): return (start_year, start_year + 1) else: return "%d — %d" % (start_year, start_year + 1) + + +def is_int(s): + try: + int(s) + return True + except ValueError: + return False diff --git a/stages/views.py b/stages/views.py index 9f495d6..082023e 100644 --- a/stages/views.py +++ b/stages/views.py @@ -19,6 +19,7 @@ from .models import ( Klass, Section, Student, Teacher, Corporation, CorpContact, Course, Period, Training, Availability, ) +from .utils import is_int def school_year_start(): @@ -319,33 +320,77 @@ class StudentImportView(ImportViewBase): corporation_mapping = settings.CORPORATION_IMPORT_MAPPING instructor_mapping = settings.INSTRUCTOR_IMPORT_MAPPING + def strip(val): + return val.strip() if isinstance(val, str) else val + obj_created = obj_modified = 0 + seen_students_ids = set() for line in up_file: 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() + val: strip(line[key]) for key, val in student_mapping.items() } + if student_defaults['ext_id'] in seen_students_ids: + # Second line for student, ignore it + continue + seen_students_ids.add(student_defaults['ext_id']) + if isinstance(student_defaults['birth_date'], str): + student_defaults['birth_date'] = datetime.strptime(student_defaults['birth_date'], '%d.%m.%Y').date() - defaults = Student.prepare_import( - student_defaults, corporation_defaults, instructor_defaults - ) - obj, created = Student.objects.get_or_create( - ext_id=line[student_rev_mapping['ext_id']], defaults=defaults) - if not created: + corporation_defaults = { + val: strip(line[key]) for key, val in corporation_mapping.items() + } + student_defaults['corporation'] = self.get_corporation(corporation_defaults) + + instructor_defaults = { + val: strip(line[key]) for key, val in instructor_mapping.items() + } + student_defaults['instructor'] = self.get_instructor(student_defaults['corporation'], instructor_defaults) + + defaults = Student.prepare_import(student_defaults) + try: + student = Student.objects.get(ext_id=student_defaults['ext_id']) + modified = False for key, val in defaults.items(): - setattr(obj, key, val) - obj.save() - obj_modified += 1 - else: + if getattr(student, key) != val: + setattr(student, key, val) + modified = True + if modified: + student.save() + obj_modified += 1 + except Student.DoesNotExist: + student = Student.objects.create(**defaults) obj_created += 1 #FIXME: implement arch_staled return obj_created, obj_modified + def get_corporation(self, corp_values): + if corp_values['ext_id'] == '': + return None + if 'city' in corp_values and is_int(corp_values['city'][:4]): + corp_values['pcode'], _, corp_values['city'] = corp_values['city'].partition(' ') + corp, created = Corporation.objects.get_or_create( + ext_id=corp_values['ext_id'], + defaults=corp_values + ) + return corp + + def get_instructor(self, corp, inst_values): + if inst_values['ext_id'] == '': + return None + try: + inst = CorpContact.objects.get(ext_id=inst_values['ext_id']) + except CorpContact.DoesNotExist: + try: + inst = corp.corpcontact_set.get( + first_name__iexact=inst_values['first_name'], last_name__iexact=inst_values['last_name'] + ) + inst.ext_id = inst_values['ext_id'] + inst.save() + except CorpContact.DoesNotExist: + inst_values['corporation'] = corp + inst = CorpContact.objects.create(**inst_values) + return inst + class HPImportView(ImportViewBase): """