Skip to content

Commit d314d51

Browse files
committed
WIP
1 parent 358c7c0 commit d314d51

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1194
-64
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', 'title', 'department', 'start', 'end')
447+
list_display_links = ('pk', '_person')
448+
list_filter = ('start', 'end', 'organization__locations__country')
449+
search_fields = (
450+
'pk',
451+
'title',
452+
'department',
453+
'organization__ror_display_for',
454+
'organization__custom_label_for',
455+
'organization__label_for',
456+
'organization__alias_for',
457+
'organization__acronym_for'
458+
'account__first_name',
459+
'account__last_name',
460+
'account__email',
461+
'frozen_author__first_name',
462+
'frozen_author__last_name',
463+
'frozen_author__frozen_email',
464+
'preprint_author__account__first_name',
465+
'preprint_author__account__last_name',
466+
'preprint_author__account__email',
467+
)
468+
raw_id_fields = ('account', 'frozen_author',
469+
'preprint_author', 'organization')
470+
471+
def _person(self, obj):
472+
if obj:
473+
return obj.account or obj.frozen_author or obj.preprint_author
474+
else:
475+
return ''
476+
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/forms/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
AccessRequestForm,
33
AccountRoleForm,
44
AdminUserForm,
5+
AffiliationForm,
56
ArticleMetaImageForm,
67
CBVFacetForm,
78
ConfirmableForm,

src/core/forms/forms.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,3 +940,18 @@ class AccountRoleForm(forms.ModelForm):
940940
class Meta:
941941
model = models.AccountRole
942942
fields = '__all__'
943+
944+
945+
class AffiliationForm(forms.ModelForm):
946+
947+
class Meta:
948+
model = models.Affiliation
949+
fields = '__all__'
950+
widgets = {
951+
'account': forms.HiddenInput,
952+
'frozen_author': forms.HiddenInput,
953+
'preprint_author': forms.HiddenInput,
954+
'organization': forms.HiddenInput,
955+
'start': HTMLDateInput,
956+
'end': HTMLDateInput,
957+
}

src/core/include_urls.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,38 @@
118118
re_path(r'^manager/user/(?P<user_id>\d+)/edit/$', core_views.user_edit, name='core_user_edit'),
119119
re_path(r'^manager/user/(?P<user_id>\d+)/history/$', core_views.user_history, name='core_user_history'),
120120

121+
## Affiliations
122+
re_path(
123+
r'^manager/user/(?P<user_id>\d+)/organization/search/$',
124+
core_views.OrganizationListView.as_view(),
125+
name='organization_search'
126+
),
127+
re_path(
128+
r'^manager/user/(?P<user_id>\d+)/organization_name/create/$',
129+
core_views.OrganizationNameCreateView.as_view(),
130+
name='organization_name_create'
131+
),
132+
re_path(
133+
r'^manager/user/(?P<user_id>\d+)/organization_name/(?P<organization_name_id>\d+)/update/$',
134+
core_views.OrganizationNameUpdateView.as_view(),
135+
name='organization_name_update'
136+
),
137+
re_path(
138+
r'^manager/user/(?P<user_id>\d+)/organization/(?P<organization_id>\d+)/affiliation/create/$',
139+
core_views.AffiliationCreateView.as_view(),
140+
name='affiliation_create'
141+
),
142+
re_path(
143+
r'^manager/user/(?P<user_id>\d+)/affiliation/(?P<affiliation_id>\d+)/update/$',
144+
core_views.AffiliationUpdateView.as_view(),
145+
name='affiliation_update'
146+
),
147+
re_path(
148+
r'^manager/user/(?P<user_id>\d+)/affiliation/(?P<affiliation_id>\d+)/delete/$',
149+
core_views.AffiliationDeleteView.as_view(),
150+
name='affiliation_delete'
151+
),
152+
121153
# Templates
122154
re_path(r'^manager/templates/$', core_views.email_templates, name='core_email_templates'),
123155

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: 55 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,14 +2239,18 @@ 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:

0 commit comments

Comments
 (0)