From: Guillaume Pellerin Date: Fri, 4 Nov 2016 15:13:15 +0000 (+0100) Subject: Finally fix various M2M values and related import rules, minor network.models update X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=e309c4679d30ed2c9a32a867e783cc220c476be3;p=mezzo.git Finally fix various M2M values and related import rules, minor network.models update --- diff --git a/app/organization/network/admin.py b/app/organization/network/admin.py index 7e29ea4c..e4485ca5 100644 --- a/app/organization/network/admin.py +++ b/app/organization/network/admin.py @@ -78,6 +78,7 @@ class PersonActivityInline(StackedDynamicInlineAdmin): model = PersonActivity fk_name = 'person' + filter_horizontal = ['organizations', 'employers', 'teams', 'projects', 'supervisors', 'phd_directors', ] class PersonPlaylistInline(TabularDynamicInlineAdmin): @@ -108,24 +109,32 @@ class PersonBlockInline(StackedDynamicInlineAdmin): class PersonAdmin(BaseTranslationOrderedModelAdmin): model = Person - inlines = [PersonActivityInline, - PersonImageInline, + inlines = [PersonImageInline, PersonBlockInline, PersonPlaylistInline, PersonLinkInline, - PersonFileInline ] + PersonFileInline, + PersonActivityInline,] first_fields = ['last_name', 'first_name', 'title', 'gender', 'user'] search_fields = ['last_name', 'first_name'] list_display = ['last_name', 'first_name', 'description', 'email', 'gender'] list_filter = ['person_title', 'activities__date_from', 'activities__date_to', 'activities__is_permanent', 'activities__framework', 'activities__grade', - 'activities__function', 'activities__team',] + 'activities__function', 'activities__teams',] -class PersonActivityAdmin(admin.ModelAdmin): +class PersonActivityAdmin(BaseTranslationModelAdmin): model = PersonActivity - list_display = ['person', 'team', 'status', 'date_from', 'date_to'] + list_display = ['person', 'get_teams', 'status', 'date_from', 'date_to'] + filter_horizontal = ['organizations', 'employers', 'teams', 'projects', 'supervisors', 'phd_directors', ] + + def get_teams(self, instance): + values = [] + for team in instance.teams.all(): + print(team.code) + values.append(team.code) + return ' - '.join(values) class PersonListBlockInlineAdmin(TabularDynamicInlineAdmin): diff --git a/app/organization/network/management/commands/import-ircam-person-xls.py b/app/organization/network/management/commands/import-ircam-person-xls.py index 3efe10fc..05476864 100644 --- a/app/organization/network/management/commands/import-ircam-person-xls.py +++ b/app/organization/network/management/commands/import-ircam-person-xls.py @@ -77,7 +77,7 @@ class IrcamPerson(object): self.person, c = Person.objects.get_or_create(title=title, first_name=first_name, last_name=last_name) self.activity = PersonActivity(person=self.person) - def get_identity(self, ): + def set_identity(self, ): gender = self.row[2].value if gender == 'H': self.person.gender = 'male' @@ -92,19 +92,6 @@ class IrcamPerson(object): self.person.save() - def get_or_create_name(self, model, column_id): - return model.objects.get_or_create(name=self.row[column_id].value)[0] if self.row[column_id].value else None - - def split_names(self, names): - name_list = [] - for spliter in self.spliters: - if spliter in names: - name_list = names.split(spliter) - if name_list: - for map(str.strip, name_list) - return name_list - return names - def get_person(self, value): if value: name_exceptions = ['de', 'von'] @@ -133,21 +120,54 @@ class IrcamPerson(object): return person return None - def get_team(self, code): - code = str(code) - qs = Q(code=code) | Q(code=code.lower()) | Q(code=code.upper()) | Q(code=code.capitalize()) - teams = Team.objects.filter(qs) - if teams: - return teams[0] - - qs = Q(title=code) | Q(title=code.lower()) | Q(title=code.upper()) | Q(title=code.capitalize()) - projects = Project.objects.filter(qs) - if projects: - self.activity.project = projects[0] - return None - return None + def get_or_create_name(self, model, column_id): + value = self.row[column_id].value if self.row[column_id].value else None + obj = None + if value: + try: + obj, c = model.objects.get_or_create(name=value) + except: + obj = model(name=value) + obj.save() + return obj - def get_activity(self): + def split_names(self, names): + name_list = [] + for spliter in self.spliters: + if spliter in names: + name_list = names.split(spliter) + if name_list: + name_list = map(str.strip, name_list) + return name_list + return [names,] + + def add_many(self, field, model, column_id, type='name'): + values = self.row[column_id].value if self.row[column_id].value else None + if values: + for value in self.split_names(values): + if type == 'name': + qs = Q(name=value) | Q(name=value.lower()) | Q(name=value.upper()) | Q(name=value.capitalize()) + obj = model(name=value) + elif type == 'code': + qs = Q(code=value) | Q(code=value.lower()) | Q(code=value.upper()) | Q(code=value.capitalize()) + obj = model(code=value) + elif type == 'title': + qs = Q(title=value) | Q(title=value.lower()) | Q(title=value.upper()) | Q(title=value.capitalize()) + obj = model(title=value) + elif type == 'person': + obj = self.get_person(value) + qs = None + if qs: + objects = model.objects.filter(qs) + if objects: + obj = objects[0] + else: + obj.save() + else: + obj.save() + field.add(obj) + + def set_activity(self): self.activity.date_from = datetime.datetime(*xlrd.xldate_as_tuple(self.row[4].value, self.datemode)) if self.row[4].value else None self.activity.date_to = datetime.datetime(*xlrd.xldate_as_tuple(self.row[5].value, self.datemode)) if self.row[5].value else None try: @@ -159,18 +179,16 @@ class IrcamPerson(object): self.activity.is_permanent = True if self.row[11].value else False self.activity.framework = self.get_or_create_name(ActivityFramework, 12) self.activity.grade = self.get_or_create_name(ActivityGrade, 13) + self.activity.save() - self.activity.employer = self.get_or_create_name(Organization, 14) - self.activity.attachment_organization = self.get_or_create_name(Organization, 15) - self.activity.second_employer = self.get_or_create_name(Organization, 16) + self.add_many(self.activity.employers, Organization, 14) + self.add_many(self.activity.organizations, Organization, 15) + self.add_many(self.activity.employers, Organization, 16) self.activity.umr = self.get_or_create_name(UMR, 17) - self.activity.team = self.get_team(self.row[19].value) - try: - self.activity.second_team = self.get_team(self.row[21].value) - except: - self.activity.second_team_text = self.row[21].value - self.activity.project, c = Project.objects.get_or_create(title=self.row[22].value) if self.row[22].value else (None, False) + self.add_many(self.activity.teams, Team, 19, type='code') + self.add_many(self.activity.teams, Team, 21, type='code') + self.add_many(self.activity.projects, Project, 22, type='title') quota = self.row[23].value try: @@ -179,9 +197,12 @@ class IrcamPerson(object): self.activity.rd_quota_text = str(quota) self.activity.phd_doctoral_school = self.get_or_create_name(Organization, 25) - self.activity.phd_director = self.get_person(self.row[26].value) - self.activity.phd_officer_1 = self.get_person(self.row[27].value) - self.activity.phd_officer_2 = self.get_person(self.row[28].value) + if self.activity.phd_doctoral_school: + self.activity.phd_doctoral_school.type, c = OrganizationType.objects.get_or_create(name='Ecole doctorale') + self.activity.phd_doctoral_school.save() + self.add_many(self.activity.phd_directors, Person, 26, type='person') + self.add_many(self.activity.supervisors, Person, 27, type='person') + self.add_many(self.activity.supervisors, Person, 28, type='person') self.activity.training_type = self.get_or_create_name(TrainingType, 29) self.activity.training_level = self.get_or_create_name(TrainingLevel, 30) @@ -189,13 +210,13 @@ class IrcamPerson(object): self.activity.training_speciality = self.get_or_create_name(TrainingSpeciality, 32) self.activity.function = self.get_or_create_name(ActivityFunction, 34) - if self.activity.phd_director: + if self.activity.phd_directors: self.activity.phd_title = self.row[35].value try: self.activity.phd_defense_date = datetime.datetime(*xlrd.xldate_as_tuple(self.row[38].value, self.datemode)) if self.row[38].value else None except: pass - self.activity.phd_postdoctoralsituation = self.row[39].value + self.activity.phd_post_doctoral_situation = self.row[39].value elif self.activity.training_type: self.activity.training_title = self.row[35].value @@ -213,7 +234,9 @@ class IrcamPerson(object): class Command(BaseCommand): - help = """Import Person data from IRCAM's legacy XLS management file""" + help = """Import Person data from IRCAM's legacy XLS management file. + python manage.py import-ircam-person-xls -s /srv/backup/TB_personnel_GP-GM_4.xls + """ option_list = BaseCommand.option_list + ( make_option('-d', '--dry-run', @@ -242,5 +265,5 @@ class Command(BaseCommand): xls = IrcamXLS(self.source_file) for i in range(xls.first_row, xls.size): p = IrcamPerson(xls.sheet.row(i), xls.book.datemode) - p.get_identity() - p.get_activity() + p.set_identity() + p.set_activity() diff --git a/app/organization/network/migrations/0047_auto_20161104_1424.py b/app/organization/network/migrations/0047_auto_20161104_1424.py new file mode 100644 index 00000000..7301dfbf --- /dev/null +++ b/app/organization/network/migrations/0047_auto_20161104_1424.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-11-04 13:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-network', '0046_auto_20161026_1025'), + ] + + operations = [ + migrations.RenameField( + model_name='personactivity', + old_name='phd_postdoctoralsituation', + new_name='phd_post_doctoral_situation', + ), + migrations.RemoveField( + model_name='personactivity', + name='attachment_organization', + ), + migrations.RemoveField( + model_name='personactivity', + name='employer', + ), + migrations.RemoveField( + model_name='personactivity', + name='phd_officers', + ), + migrations.RemoveField( + model_name='personactivity', + name='second_employer', + ), + migrations.RemoveField( + model_name='personactivity', + name='second_team', + ), + migrations.RemoveField( + model_name='personactivity', + name='second_team_text', + ), + migrations.RemoveField( + model_name='personactivity', + name='team', + ), + migrations.RemoveField( + model_name='personactivity', + name='third_employer', + ), + migrations.AddField( + model_name='personactivity', + name='employers', + field=models.ManyToManyField(blank=True, related_name='employer_project_activities', to='organization-network.Organization', verbose_name='employers'), + ), + migrations.AddField( + model_name='personactivity', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_activities', to='organization-network.Organization', verbose_name='organization (attachment or subscribed)'), + ), + migrations.AddField( + model_name='personactivity', + name='supervisors', + field=models.ManyToManyField(blank=True, related_name='supervisor_activities', to='organization-network.Person', verbose_name='supervisors'), + ), + migrations.AddField( + model_name='personactivity', + name='team_text', + field=models.CharField(blank=True, max_length=256, null=True, verbose_name='other team text'), + ), + migrations.AddField( + model_name='personactivity', + name='teams', + field=models.ManyToManyField(blank=True, related_name='team_activities', to='organization-network.Team', verbose_name='teams'), + ), + migrations.AlterField( + model_name='personactivity', + name='rd_quota_float', + field=models.FloatField(blank=True, null=True, verbose_name='R&D quota (float)'), + ), + ] diff --git a/app/organization/network/migrations/0048_auto_20161104_1445.py b/app/organization/network/migrations/0048_auto_20161104_1445.py new file mode 100644 index 00000000..460c8f16 --- /dev/null +++ b/app/organization/network/migrations/0048_auto_20161104_1445.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-11-04 13:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-network', '0047_auto_20161104_1424'), + ] + + operations = [ + migrations.AlterField( + model_name='organization', + name='mappable_location', + field=models.CharField(blank=True, help_text='This address will be used to calculate latitude and longitude. Leave blank and set Latitude and Longitude to specify the location yourself, or leave all three blank to auto-fill from the Location field.', max_length=128, null=True), + ), + ] diff --git a/app/organization/network/migrations/0049_auto_20161104_1453.py b/app/organization/network/migrations/0049_auto_20161104_1453.py new file mode 100644 index 00000000..7d6e42d4 --- /dev/null +++ b/app/organization/network/migrations/0049_auto_20161104_1453.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-11-04 13:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-network', '0048_auto_20161104_1445'), + ] + + operations = [ + migrations.RemoveField( + model_name='personactivity', + name='organization', + ), + migrations.AddField( + model_name='personactivity', + name='organizations', + field=models.ManyToManyField(blank=True, related_name='project_activities', to='organization-network.Organization', verbose_name='organizations (attachment or subscribed)'), + ), + ] diff --git a/app/organization/network/models.py b/app/organization/network/models.py index 490a7416..5232ea05 100644 --- a/app/organization/network/models.py +++ b/app/organization/network/models.py @@ -63,12 +63,12 @@ ALIGNMENT_CHOICES = (('left', _('left')), ('left', _('left')), ('right', _('righ class Organization(Named, Address, URL, AdminThumbRelatedMixin): """(Organization description)""" - mappable_location = models.CharField(max_length=128, blank=True, help_text="This address will be used to calculate latitude and longitude. Leave blank and set Latitude and Longitude to specify the location yourself, or leave all three blank to auto-fill from the Location field.") + mappable_location = models.CharField(max_length=128, blank=True, null=True, help_text="This address will be used to calculate latitude and longitude. Leave blank and set Latitude and Longitude to specify the location yourself, or leave all three blank to auto-fill from the Location field.") lat = models.DecimalField(max_digits=10, decimal_places=7, blank=True, null=True, verbose_name="Latitude", help_text="Calculated automatically if mappable location is set.") lon = models.DecimalField(max_digits=10, decimal_places=7, blank=True, null=True, verbose_name="Longitude", help_text="Calculated automatically if mappable location is set.") type = models.ForeignKey('OrganizationType', verbose_name=_('organization type'), blank=True, null=True, on_delete=models.SET_NULL) initials = models.CharField(_('initials'), max_length=128, blank=True, null=True) - is_on_map = models.BooleanField(_('is on map'), default=False) + is_on_map = models.BooleanField(_('is on map'), default=False, blank=True) admin_thumb_type = 'logo' @@ -89,7 +89,8 @@ class Organization(Named, Address, URL, AdminThumbRelatedMixin): raise ValidationError("Latitude required if specifying longitude.") if not (self.lat and self.lon) and not self.mappable_location: - self.mappable_location = self.address.replace("\n"," ").replace('\r', ' ') + ", " + self.postal_code + " " + self.city + if self.address: + self.mappable_location = self.address.replace("\n"," ").replace('\r', ' ') + ", " + self.postal_code + " " + self.city if self.mappable_location and not (self.lat and self.lon): #location should always override lat/long if set g = GoogleMaps(domain=settings.EVENT_GOOGLE_MAPS_DOMAIN) @@ -388,27 +389,25 @@ class PersonActivity(Period): grade = models.ForeignKey(ActivityGrade, verbose_name=_('grade'), blank=True, null=True, on_delete=models.SET_NULL) function = models.ForeignKey(ActivityFunction, verbose_name=_('function'), blank=True, null=True, on_delete=models.SET_NULL) - employer = models.ForeignKey(Organization, verbose_name=_('employer'), related_name='employer_activities', blank=True, null=True, on_delete=models.SET_NULL) - attachment_organization = models.ForeignKey(Organization, verbose_name=_('attachment organization'), related_name='attachment_activities', blank=True, null=True, on_delete=models.SET_NULL) - second_employer = models.ForeignKey(Organization, verbose_name=_('second employer'), related_name='second_employer_activities', blank=True, null=True, on_delete=models.SET_NULL) - third_employer = models.ForeignKey(Organization, verbose_name=_('third employer'), related_name='third_employer_activities', blank=True, null=True, on_delete=models.SET_NULL) + organizations = models.ManyToManyField(Organization, verbose_name=_('organizations (attachment or subscribed)'), related_name='project_activities', blank=True) + employers = models.ManyToManyField(Organization, verbose_name=_('employers'), related_name='employer_project_activities', blank=True) umr = models.ForeignKey(UMR, verbose_name=_('UMR'), blank=True, null=True, on_delete=models.SET_NULL) - team = models.ForeignKey('Team', verbose_name=_('team'), related_name='team_activities', blank=True, null=True, on_delete=models.SET_NULL) - second_team = models.ForeignKey('Team', verbose_name=_('second team'), related_name='second_team_activities', blank=True, null=True, on_delete=models.SET_NULL) - second_team_text = models.CharField(_('second team text'), blank=True, null=True, max_length=256) + teams = models.ManyToManyField('Team', verbose_name=_('teams'), related_name='team_activities', blank=True) + team_text = models.CharField(_('other team text'), blank=True, null=True, max_length=256) projects = models.ManyToManyField('organization-projects.Project', verbose_name=_('projects'), related_name='activities', blank=True) - rd_quota_float = models.IntegerField(_('R&D quota (float)'), blank=True, null=True) + rd_quota_float = models.FloatField(_('R&D quota (float)'), blank=True, null=True) rd_quota_text = models.CharField(_('R&D quota (text)'), blank=True, null=True, max_length=128) rd_program = models.TextField(_('R&D program'), blank=True) budget_code = models.ForeignKey(BudgetCode, blank=True, null=True, on_delete=models.SET_NULL) + supervisors = models.ManyToManyField('Person', verbose_name=_('supervisors'), related_name='supervisor_activities', blank=True) + phd_doctoral_school = models.ForeignKey(Organization, verbose_name=_('doctoral school'), blank=True, null=True, on_delete=models.SET_NULL) phd_directors = models.ManyToManyField('Person', verbose_name=_('PhD directors'), related_name='phd_director_activities', blank=True) - phd_officers = models.ManyToManyField('Person', verbose_name=_('PhD officers'), related_name='phd_officer_activities', blank=True) phd_defense_date = models.DateField(_('PhD defense date'), blank=True, null=True) phd_title = models.TextField(_('PhD title'), blank=True) - phd_postdoctoralsituation = models.CharField(_('post-doctoral situation'), blank=True, max_length=256) + phd_post_doctoral_situation = models.CharField(_('post-doctoral situation'), blank=True, max_length=256) hdr = models.BooleanField(_('HDR'), default=False) training_type = models.ForeignKey(TrainingType, verbose_name=_('training type'), blank=True, null=True, on_delete=models.SET_NULL) @@ -430,6 +429,7 @@ class PersonActivity(Period): class Meta: verbose_name = _('activity') verbose_name_plural = _('activities') + ordering = ['person__last_name',] def __str__(self): if self.status: