Skip to content

Commit 1e7287d

Browse files
committed
Updates to data model #3168
1 parent 358c7c0 commit 1e7287d

File tree

7 files changed

+111
-19
lines changed

7 files changed

+111
-19
lines changed

src/core/admin.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,11 +400,11 @@ class AccessRequestAdmin(admin.ModelAdmin):
400400

401401
class OrganizationAdmin(admin.ModelAdmin):
402402
list_display = ('pk', 'ror', '_ror_display', '_custom_label',
403-
'_locations', 'ror_status')
403+
'website', '_locations', 'ror_status')
404404
list_display_links = ('pk', 'ror')
405405
list_filter = ('ror_status', 'locations__country')
406406
search_fields = ('pk', 'ror_display__value', 'custom_label__value', 'labels__value',
407-
'aliases__value', 'acronyms__value')
407+
'aliases__value', 'acronyms__value', 'website', 'ror')
408408
raw_id_fields = ('locations', )
409409

410410
def _ror_display(self, obj):
@@ -442,6 +442,40 @@ class LocationAdmin(admin.ModelAdmin):
442442
'geonames_id')
443443

444444

445+
class AffiliationAdmin(admin.ModelAdmin):
446+
list_display = ('pk', '_person', 'organization',
447+
'title', 'department', 'start', 'end')
448+
list_display_links = ('pk', '_person')
449+
list_filter = ('start', 'end', 'organization__locations__country')
450+
search_fields = (
451+
'pk',
452+
'title',
453+
'department',
454+
'organization__ror_display_for',
455+
'organization__custom_label_for',
456+
'organization__label_for',
457+
'organization__alias_for',
458+
'organization__acronym_for'
459+
'account__first_name',
460+
'account__last_name',
461+
'account__email',
462+
'frozen_author__first_name',
463+
'frozen_author__last_name',
464+
'frozen_author__frozen_email',
465+
'preprint_author__account__first_name',
466+
'preprint_author__account__last_name',
467+
'preprint_author__account__email',
468+
)
469+
raw_id_fields = ('account', 'frozen_author',
470+
'preprint_author', 'organization')
471+
472+
def _person(self, obj):
473+
if obj:
474+
return obj.account or obj.frozen_author or obj.preprint_author
475+
else:
476+
return ''
477+
478+
445479
admin_list = [
446480
(models.AccountRole, AccountRoleAdmin),
447481
(models.Account, AccountAdmin),
@@ -474,6 +508,7 @@ class LocationAdmin(admin.ModelAdmin):
474508
(models.Organization, OrganizationAdmin),
475509
(models.OrganizationName, OrganizationNameAdmin),
476510
(models.Location, LocationAdmin),
511+
(models.Affiliation, AffiliationAdmin),
477512
]
478513

479514
[admin.site.register(*t) for t in admin_list]

src/core/migrations/0097_location_organization_remove_account_country_and_more.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
class Migration(migrations.Migration):
1010

1111
dependencies = [
12-
('submission', '0080_remove_frozenauthor_country_and_more'),
1312
('repository', '0044_remove_preprintauthor_affiliation_and_more'),
14-
('core', '0096_update_review_ack_email'),
13+
('submission', '0080_remove_frozenauthor_country_and_more'),
14+
('core', '0099_alter_accountrole_options'),
1515
]
1616

1717
operations = [
@@ -32,6 +32,7 @@ class Migration(migrations.Migration):
3232
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
3333
('ror', models.URLField(blank=True, help_text='Research Organization Registry identifier (URL)', validators=[core.models.validate_ror], verbose_name='ROR')),
3434
('ror_status', models.CharField(blank=True, choices=[('active', 'Active'), ('inactive', 'Inactive'), ('withdrawn', 'Withdrawn'), ('unknown', 'Unknown')], default='unknown', max_length=10)),
35+
('website', models.CharField(blank=True, max_length=500)),
3536
('locations', models.ManyToManyField(blank=True, null=True, to='core.location')),
3637
],
3738
),
@@ -78,4 +79,8 @@ class Migration(migrations.Migration):
7879
'ordering': ['-pk'],
7980
},
8081
),
82+
migrations.AddConstraint(
83+
model_name='organization',
84+
constraint=models.UniqueConstraint(condition=models.Q(('ror__exact', ''), _negated=True), fields=('ror',), name='filled_unique'),
85+
),
8186
]

src/core/models.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,10 @@ def initials(self):
402402
def affiliation(self, obj=False, date=None):
403403
return Affiliation.get_primary(account=self, obj=obj, date=date)
404404

405+
@property
406+
def affiliations(self):
407+
return Affiliation.objects.filter(account=self).order_by('-is_primary')
408+
405409
@property
406410
def institution(self):
407411
affil = self.affiliation(obj=True)
@@ -1975,7 +1979,6 @@ def validate_ror(url):
19751979
raise ValidationError(f'{ror} is not a valid ROR identifier')
19761980

19771981

1978-
19791982
class Organization(models.Model):
19801983

19811984
class RORStatus(models.TextChoices):
@@ -1996,12 +1999,32 @@ class RORStatus(models.TextChoices):
19961999
choices=RORStatus.choices,
19972000
default=RORStatus.UNKNOWN,
19982001
)
2002+
website = models.CharField(
2003+
blank=True,
2004+
max_length=500,
2005+
)
19992006
locations = models.ManyToManyField(
20002007
'Location',
20012008
blank=True,
20022009
null=True,
20032010
)
20042011

2012+
class Meta:
2013+
constraints = [
2014+
models.UniqueConstraint(
2015+
fields=['ror'],
2016+
condition=~models.Q(ror__exact=''),
2017+
name='filled_unique',
2018+
)
2019+
]
2020+
2021+
def __str__(self):
2022+
elements = [
2023+
str(self.name) if self.name else '',
2024+
str(self.location) if self.location else '',
2025+
]
2026+
return ', '.join([element for element in elements if element])
2027+
20052028
@property
20062029
def name(self):
20072030
"""
@@ -2018,20 +2041,33 @@ def name(self):
20182041
except Organization.custom_label.RelatedObjectDoesNotExist:
20192042
return self.labels.first()
20202043

2021-
def __str__(self):
2022-
elements = [
2023-
str(self.name) if self.name else '',
2024-
str(self.location) if self.location else '',
2025-
]
2026-
return ', '.join([element for element in elements if element])
2027-
20282044
@property
20292045
def location(self):
20302046
"""
20312047
Return the first location.
20322048
"""
20332049
return self.locations.first() if self.locations else None
20342050

2051+
@property
2052+
def names(self):
2053+
"""
2054+
All names.
2055+
"""
2056+
return OrganizationName.objects.filter(
2057+
models.Q(ror_display_for=self) |
2058+
models.Q(custom_label_for=self) |
2059+
models.Q(label_for=self) |
2060+
models.Q(alias_for=self) |
2061+
models.Q(acronym_for=self),
2062+
)
2063+
2064+
@property
2065+
def also_known_as(self):
2066+
"""
2067+
All names excluding the ROR display name.
2068+
"""
2069+
return self.names.exclude(ror_display_for=self)
2070+
20352071
@classmethod
20362072
def naive_get_or_create(
20372073
cls,
@@ -2094,9 +2130,13 @@ def create_from_ror_record(cls, record):
20942130
organization, created = cls.objects.get_or_create(
20952131
ror=record.get('id', ''),
20962132
)
2097-
if record.get('status'):
2098-
organization.ror_status = record.get('status')
2133+
organization.ror_status = record.get('status', cls.RORStatus.UNKNOWN)
2134+
for link in record.get('links', []):
2135+
if link.get('type') == 'website':
2136+
organization.website = link.get('value', '')
2137+
break
20992138
organization.save()
2139+
21002140
for name in record.get('names'):
21012141
kwargs = {}
21022142
kwargs['value'] = name.get('value', '')
@@ -2111,6 +2151,7 @@ def create_from_ror_record(cls, record):
21112151
if 'acronym' in name.get('types'):
21122152
kwargs['acronym_for'] = organization
21132153
OrganizationName.objects.get_or_create(**kwargs)
2154+
21142155
for location in record.get('locations'):
21152156
details = location.get('geonames_details', {})
21162157
country, created = Country.objects.get_or_create(
@@ -2198,19 +2239,30 @@ class Affiliation(models.Model):
21982239
)
21992240
is_primary = models.BooleanField(
22002241
default=False,
2242+
help_text="Each account can have one primary affiliation",
22012243
)
22022244
start = models.DateField(
22032245
blank=True,
22042246
null=True,
2247+
verbose_name="Start date",
22052248
)
22062249
end = models.DateField(
22072250
blank=True,
22082251
null=True,
2252+
verbose_name="End date",
2253+
help_text="Leave empty for a current affiliation",
22092254
)
22102255

22112256
class Meta:
22122257
ordering = ['-pk']
22132258

2259+
def title_department(self):
2260+
elements = [
2261+
self.title,
2262+
self.department,
2263+
]
2264+
return ', '.join([element for element in elements if element])
2265+
22142266
def __str__(self):
22152267
elements = [
22162268
self.title,

src/repository/migrations/0044_remove_preprintauthor_affiliation_and_more.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class Migration(migrations.Migration):
77

88
dependencies = [
9-
('repository', '0043_auto_20240402_1256'),
9+
('repository', '0045_historicalrepository_display_public_metrics_and_more'),
1010
]
1111

1212
operations = [

src/submission/migrations/0080_remove_frozenauthor_country_and_more.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class Migration(migrations.Migration):
77

88
dependencies = [
9-
('submission', '0079_merge_20240602_1739'),
9+
('submission', '0084_remove_article_jats_article_type_and_more'),
1010
]
1111

1212
operations = [

src/utils/migrations/0035_rorimport_rorimporterror.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class Migration(migrations.Migration):
88

99
dependencies = [
10-
('utils', '0034_rename_toaddress_addressee'),
10+
('utils', '0038_upgrade_1_7_2'),
1111
]
1212

1313
operations = [

src/utils/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,12 @@ def get_records(self):
466466

467467
def delete_previous_download(self):
468468
if not self.previous_import:
469-
logger.info('No previous import to remove.')
469+
logger.debug('No previous import to remove.')
470470
return
471471
try:
472472
os.unlink(self.previous_import.zip_path)
473473
except FileNotFoundError:
474-
logger.info('Previous import had no zip file.')
474+
logger.debug('Previous import had no zip file.')
475475

476476
def download_data(self):
477477
"""

0 commit comments

Comments
 (0)