diff --git a/profiles/constants.py b/profiles/constants.py index fb7c120de3db2c927f5fbab193a24b4adffff12c..b9ac6f92693bab316ddad1cc133615f6deb7a857 100644 --- a/profiles/constants.py +++ b/profiles/constants.py @@ -9,3 +9,32 @@ PROFILE_NON_DUPLICATE_REASONS = ( (DIFFERENT_PEOPLE, 'These are different people'), (MULTIPLE_ALLOWED, 'Multiple Profiles allowed for this person'), ) + + +AFFILIATION_CATEGORY_EMPLOYED_PROF_FULL = 'employed_prof_full' +AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSOCIATE = 'employed_prof_associate' +AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSISTANT = 'employed_prof_assistant' +AFFILIATION_CATEGORY_EMPLOYED_PROF_EMERITUS = 'employed_prof_emeritus' +AFFILIATION_CATEGORY_EMPLOYED_PERMANENT_STAFF = 'employed_permanent_staff' +AFFILIATION_CATEGORY_EMPLOYED_FIXED_TERM_STAFF = 'employed_fixed_term_staff' +AFFILIATION_CATEGORY_EMPLOYED_TENURE_TRACK = 'employed_tenure_track' +AFFILIATION_CATEGORY_EMPLOYED_POSTDOC = 'employed_postdoc' +AFFILIATION_CATEGORY_EMPLOYED_PhD = 'employed_phd' +AFFILIATION_CATEGORY_ASSOCIATE_SCIENTIST = 'associate_scientist' +AFFILIATION_CATEGORY_CONSULTANT = 'consultant' +AFFILIATION_CATEGORY_VISITOR = 'visitor' + +AFFILIATION_CATEGORIES = ( + (AFFILIATION_CATEGORY_EMPLOYED_PROF_FULL, 'Full Professor'), + (AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSOCIATE, 'Associate Professor'), + (AFFILIATION_CATEGORY_EMPLOYED_PROF_ASSISTANT, 'Assistant Professor'), + (AFFILIATION_CATEGORY_EMPLOYED_PROF_EMERITUS, 'Emeritus Professor'), + (AFFILIATION_CATEGORY_EMPLOYED_PERMANENT_STAFF, 'Permanent Staff'), + (AFFILIATION_CATEGORY_EMPLOYED_FIXED_TERM_STAFF, 'Fixed Term Staff'), + (AFFILIATION_CATEGORY_EMPLOYED_TENURE_TRACK, 'Tenure Tracker'), + (AFFILIATION_CATEGORY_EMPLOYED_POSTDOC, 'Postdoctoral Researcher'), + (AFFILIATION_CATEGORY_EMPLOYED_PhD, 'PhD candidate'), + (AFFILIATION_CATEGORY_ASSOCIATE_SCIENTIST, 'Associate Scientist'), + (AFFILIATION_CATEGORY_CONSULTANT, 'Consultant'), + (AFFILIATION_CATEGORY_VISITOR, 'Visotor'), +) diff --git a/profiles/migrations/0018_affiliation.py b/profiles/migrations/0018_affiliation.py new file mode 100644 index 0000000000000000000000000000000000000000..83b3d0301775c1b37145ac5e706c477daba79e23 --- /dev/null +++ b/profiles/migrations/0018_affiliation.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2019-03-27 14:14 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organizations', '0010_auto_20190223_1406'), + ('profiles', '0017_auto_20190126_2058'), + ] + + operations = [ + migrations.CreateModel( + name='Affiliation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('category', models.CharField(choices=[('employed_prof_full', 'Full Professor'), ('employed_prof_associate', 'Associate Professor'), ('employed_prof_assistant', 'Assistant Professor'), ('employed_prof_emeritus', 'Emeritus Professor'), ('employed_permanent_staff', 'Permanent Staff'), ('employed_fixed_term_staff', 'Fixed Term Staff'), ('employed_tenure_track', 'Tenure Tracker'), ('employed_postdoc', 'Postdoctoral Researcher'), ('employed_phd', 'PhD candidate'), ('associate_scientist', 'Associate Scientist'), ('consultant', 'Consultant'), ('visitor', 'Visotor')], help_text='Select the most suitable category', max_length=64)), + ('description', models.CharField(max_length=256)), + ('date_from', models.DateField(blank=True, null=True)), + ('date_until', models.DateField(blank=True, null=True)), + ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='affiliations', to='organizations.Organization')), + ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='affiliations', to='profiles.Profile')), + ], + options={ + 'ordering': ['profile__user__last_name', 'profile__user__first_name', 'date_until'], + 'default_related_name': 'affiliations', + }, + ), + ] diff --git a/profiles/models.py b/profiles/models.py index 2a7e9b5af27a07ebd2069a4c7ec0265d2082896c..fa47729d4545f403ca4f2ce84c053022d3419184 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -18,7 +18,7 @@ from journals.models import Publication, PublicationAuthorsTable from ontology.models import Topic from theses.models import ThesisLink -from .constants import PROFILE_NON_DUPLICATE_REASONS +from .constants import PROFILE_NON_DUPLICATE_REASONS, AFFILIATION_CATEGORIES from .managers import ProfileQuerySet @@ -164,3 +164,46 @@ class ProfileNonDuplicates(models.Model): @property def full_name(self): return '%s%s' % (self.profiles.first().last_name, self.profiles.first().first_name) + + + +################ +# Affiliations # +################ + +class Affiliation(models.Model): + """ + Link between a Profile and an Organization, for a specified time interval. + + Fields: + * profile + * organization + * description + * date_from + * date_until + + Affiliations can overlap in time. + + Ideally, each Profile would have at least one valid Affiliation at each moment + of time during the whole duration of that person's career. + """ + profile = models.ForeignKey('profiles.Profile', on_delete=models.CASCADE, + related_name='affiliations') + organization = models.ForeignKey('organizations.Organization', on_delete=models.CASCADE, + related_name='affiliations') + category = models.CharField(max_length=64, choices=AFFILIATION_CATEGORIES, + help_text='Select the most suitable category') + description = models.CharField(max_length=256) + date_from = models.DateField(blank=True, null=True) + date_until = models.DateField(blank=True, null=True) + + class Meta: + default_related_name = 'affiliations' + ordering = ['profile__user__last_name', 'profile__user__first_name', + 'date_until'] + + def __str__(self): + return '{ profile }, { organization } [{ date_from } to { date_until }]'.format( + profile=self.profile, organization=self.organization, + date_from=self.date_from.strftime('Y-m-d'), + date_until=self.date_until.strftime('Y-m-d'))