Skip to content

Commit

Permalink
add collection metadata options for ibdgc
Browse files Browse the repository at this point in the history
  • Loading branch information
John Tordoff committed Dec 7, 2023
1 parent 3d3bfdc commit 7f2ac33
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 29 deletions.
62 changes: 62 additions & 0 deletions admin/collection_providers/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class CollectionProviderForm(forms.ModelForm):
program_area_choices = forms.CharField(widget=forms.HiddenInput(), required=False)
school_type_choices = forms.CharField(widget=forms.HiddenInput(), required=False)
study_design_choices = forms.CharField(widget=forms.HiddenInput(), required=False)
data_type_choices = forms.CharField(widget=forms.HiddenInput(), required=False)
disease_choices = forms.CharField(widget=forms.HiddenInput(), required=False)
_id = forms.SlugField(
required=True,
help_text='URL Slug',
Expand Down Expand Up @@ -268,3 +270,63 @@ def clean_study_design_choices(self):
if choices:
added_choices = json.loads(choices)
return {'added': added_choices, 'removed': removed_choices}

def clean_disease_choices(self):
if not self.data.get('disease_choices'):
return {'added': [], 'removed': []}

collection_provider = self.instance
primary_collection = collection_provider.primary_collection
if primary_collection: # Modifying an existing CollectionProvider
old_choices = {c.strip(' ') for c in primary_collection.disease_choices}
updated_choices = {c.strip(' ') for c in json.loads(self.data.get('disease_choices'))}
added_choices = updated_choices - old_choices
removed_choices = old_choices - updated_choices

active_removed_choices = set(
primary_collection.collectionsubmission_set.filter(
disease_choices__in=removed_choices
).values_list('disease', flat=True)
)
if active_removed_choices:
raise forms.ValidationError(
'Cannot remove the following choices for "study_design", as they are '
f'currently in use: {active_removed_choices}'
)
else: # Creating a new CollectionProvider
added_choices = set()
removed_choices = set()
choices = self.data.get('disease_choices')
if choices:
added_choices = json.loads(choices)
return {'added': added_choices, 'removed': removed_choices}

def clean_data_type_choices(self):
if not self.data.get('data_type_choices'):
return {'added': [], 'removed': []}

collection_provider = self.instance
primary_collection = collection_provider.primary_collection
if primary_collection: # Modifying an existing CollectionProvider
old_choices = {c.strip(' ') for c in primary_collection.data_type_choices}
updated_choices = {c.strip(' ') for c in json.loads(self.data.get('data_type_choices'))}
added_choices = updated_choices - old_choices
removed_choices = old_choices - updated_choices

active_removed_choices = set(
primary_collection.collectionsubmission_set.filter(
disease_choices__in=removed_choices
).values_list('data_type_choices', flat=True)
)
if active_removed_choices:
raise forms.ValidationError(
'Cannot remove the following choices for "data_type_choices", as they are '
f'currently in use: {active_removed_choices}'
)
else: # Creating a new CollectionProvider
added_choices = set()
removed_choices = set()
choices = self.data.get('data_type_choices')
if choices:
added_choices = json.loads(choices)
return {'added': added_choices, 'removed': removed_choices}
61 changes: 33 additions & 28 deletions admin/collection_providers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def form_valid(self, form):
self.object.primary_collection.school_type_choices.append(item)
for item in form.cleaned_data['study_design_choices']['added']:
self.object.primary_collection.study_design_choices.append(item)
for item in form.cleaned_data['data_type_choices']['added']:
self.object.primary_collection.study_design_choices.append(item)
for item in form.cleaned_data['disease_choices']['added']:
self.object.primary_collection.study_design_choices.append(item)
self.object.primary_collection.save()
return super().form_valid(form)

Expand Down Expand Up @@ -162,6 +166,20 @@ def get_context_data(self, *args, **kwargs):
f'<li>{choice}</li>' for choice in primary_collection.study_design_choices
))
kwargs['study_design_choices'] = study_design_choices_html
study_design_choices_html = '<ul>{choices}</ul>'.format(choices=''.join(
f'<li>{choice}</li>' for choice in primary_collection.study_design_choices
))
kwargs['study_design_choices'] = study_design_choices_html

disease_choices_html = '<ul>{choices}</ul>'.format(choices=''.join(
f'<li>{choice}</li>' for choice in primary_collection.disease_choices
))
kwargs['disease_choices'] = disease_choices_html

data_type_choices_html = '<ul>{choices}</ul>'.format(choices=''.join(
f'<li>{choice}</li>' for choice in primary_collection.data_type_choices
))
kwargs['data_type_choices'] = data_type_choices_html

# get a dict of model fields so that we can set the initial value for the update form
fields = model_to_dict(collection_provider)
Expand All @@ -175,6 +193,8 @@ def get_context_data(self, *args, **kwargs):

fields['school_type_choices'] = json.dumps(primary_collection.school_type_choices)
fields['study_design_choices'] = json.dumps(primary_collection.study_design_choices)
fields['data_type_choices'] = json.dumps(primary_collection.data_type_choices)
fields['disease_choices'] = json.dumps(primary_collection.disease_choices)

# compile html list of collected_type_choices
if collection_provider.primary_collection:
Expand Down Expand Up @@ -233,36 +253,19 @@ class CollectionProviderChangeForm(PermissionRequiredMixin, UpdateView):
model = CollectionProvider
form_class = CollectionProviderForm

def form_valid(self, form):
if self.object.primary_collection:
self.object.primary_collection.collected_type_choices.extend(form.cleaned_data['collected_type_choices']['added'])
for item in form.cleaned_data['collected_type_choices']['removed']:
self.object.primary_collection.collected_type_choices.remove(item)

self.object.primary_collection.status_choices.extend(form.cleaned_data['status_choices']['added'])
for item in form.cleaned_data['status_choices']['removed']:
self.object.primary_collection.status_choices.remove(item)

self.object.primary_collection.issue_choices.extend(form.cleaned_data['issue_choices']['added'])
for item in form.cleaned_data['issue_choices']['removed']:
self.object.primary_collection.issue_choices.remove(item)

self.object.primary_collection.volume_choices.extend(form.cleaned_data['volume_choices']['added'])
for item in form.cleaned_data['volume_choices']['removed']:
self.object.primary_collection.volume_choices.remove(item)
def process_choices(self, choices_name, form):
collection = self.object.primary_collection
choices_added = form.cleaned_data[f'{choices_name}_choices']['added']
choices_removed = form.cleaned_data[f'{choices_name}_choices']['removed']

self.object.primary_collection.program_area_choices.extend(form.cleaned_data['program_area_choices']['added'])
for item in form.cleaned_data['program_area_choices']['removed']:
self.object.primary_collection.program_area_choices.remove(item)

self.object.primary_collection.school_type_choices.extend(form.cleaned_data['school_type_choices']['added'])
for item in form.cleaned_data['school_type_choices']['removed']:
self.object.primary_collection.school_type_choices.remove(item)

self.object.primary_collection.study_design_choices.extend(form.cleaned_data['study_design_choices']['added'])
for item in form.cleaned_data['study_design_choices']['removed']:
self.object.primary_collection.study_design_choices.remove(item)
getattr(collection, f'{choices_name}_choices').extend(choices_added)
for item in choices_removed:
getattr(collection, f'{choices_name}_choices').remove(item)

def form_valid(self, form):
if self.object.primary_collection:
for choices_name in ['collected_type', 'status', 'issue', 'volume', 'program_area', 'school_type', 'study_design', 'data_type', 'disease']:
self.process_choices(choices_name, form)
self.object.primary_collection.save()
return super().form_valid(form)

Expand Down Expand Up @@ -399,6 +402,8 @@ def create_or_update_provider(self, provider_data):
provider.primary_collection.program_area_choices = primary_collection['fields']['program_area_choices']
provider.primary_collection.school_type_choices = primary_collection['fields']['school_type_choices']
provider.primary_collection.study_design_choices = primary_collection['fields']['study_design_choices']
provider.primary_collection.disease_choices = primary_collection['fields']['disease_choices']
provider.primary_collection.data_type_choices = primary_collection['fields']['data_type_choices']
provider.primary_collection.save()
if licenses:
provider.licenses_acceptable.set(licenses)
Expand Down
26 changes: 26 additions & 0 deletions admin/static/js/pages/collection-provider-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ $('#tags-input-study-design').on('itemRemoved', function(event) {
$('#id_study_design_choices').val(JSON.stringify($('#tags-input-study-design').tagsinput('items')));
});

$('#tags-input-data-type').on('itemAdded', function(event) {
$('#id_data_type_choices').val(JSON.stringify($('#tags-input-data-type').tagsinput('items')));
});

$('#tags-input-data-type').on('itemRemoved', function(event) {
$('#id_data_type_choices').val(JSON.stringify($('#tags-input-data-type').tagsinput('items')));
});

$('#tags-input-disease').on('itemAdded', function(event) {
$('#id_disease_choices').val(JSON.stringify($('#tags-input-disease').tagsinput('items')));
});

$('#tags-input-disease').on('itemRemoved', function(event) {
$('#id_disease_choices').val(JSON.stringify($('#tags-input-disease').tagsinput('items')));
});


$(document).ready(function() {
var collectedTypeItems = JSON.parse($('#id_collected_type_choices').val());
Expand Down Expand Up @@ -93,4 +109,14 @@ $(document).ready(function() {
studyDesignItems.forEach(function(element){
$('#tags-input-study-design').tagsinput('add', element)
});

var diseaseItems = JSON.parse($('#id_disease_choices').val());
diseaseItems.forEach(function(element){
$('#tags-input-disease').tagsinput('add', element)
});

var dataTypeItems = JSON.parse($('#id_data_type_choices').val());
dataTypeItems.forEach(function(element){
$('#tags-input-data-type').tagsinput('add', element)
});
});
8 changes: 8 additions & 0 deletions admin/templates/collection_providers/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ <h2>{{ collection_provider.name }}</h2>
<th>study_design_choices</th>
<td>{{ study_design_choices | safe}}</td>
</tr>
<tr>
<th>disease_choices</th>
<td>{{ disease_choices | safe}}</td>
</tr>
<tr>
<th>data_type_choices</th>
<td>{{ data_type_choices | safe}}</td>
</tr>
</table>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@
<input id="tags-input-study-design" type="text" data-role="tagsinput"/>
</div>
</div>
<div>
<label>Disease choices:</label>
<div class=#bootstrap-tagsinput">
<input id="tags-input-disease" type="text" data-role="tagsinput"/>
</div>
</div>
<div>
<label>Data Type choices:</label>
<div class=#bootstrap-tagsinput">
<input id="tags-input-data-type" type="text" data-role="tagsinput"/>
</div>
</div>
<input class="form-button" type="submit" value="Save" />
</form>
</div>
Expand Down
20 changes: 20 additions & 0 deletions api/collections/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ class CollectionSerializer(JSONAPISerializer):
child=ser.CharField(max_length=127),
default=list(),
)
data_type_choices = ser.ListField(
child=ser.CharField(max_length=127),
default=list(),
)
disease_choices = ser.ListField(
child=ser.CharField(max_length=127),
default=list(),
)

links = LinksField({})

Expand Down Expand Up @@ -241,6 +249,8 @@ def subjects_view_kwargs(self):
program_area = ser.CharField(required=False)
school_type = ser.CharField(required=False)
study_design = ser.CharField(required=False)
data_type = ser.CharField(required=False)
disease = ser.CharField(required=False)

def get_absolute_url(self, obj):
return absolute_reverse(
Expand Down Expand Up @@ -272,6 +282,10 @@ def update(self, obj, validated_data):
obj.school_Type = validated_data.pop('school_type')
if 'study_design' in validated_data:
obj.study_design = validated_data.pop('study_design')
if 'data_type' in validated_data:
obj.study_design = validated_data.pop('data_type')
if 'disease' in validated_data:
obj.study_design = validated_data.pop('disease')

obj.save()
return obj
Expand Down Expand Up @@ -337,6 +351,8 @@ def subjects_view_kwargs(self):
program_area = ser.CharField(required=False)
school_type = ser.CharField(required=False)
study_design = ser.CharField(required=False)
date_type = ser.CharField(required=False)
disease = ser.CharField(required=False)

def get_absolute_url(self, obj):
return absolute_reverse(
Expand Down Expand Up @@ -368,6 +384,10 @@ def update(self, obj, validated_data):
obj.school_Type = validated_data.pop('school_type')
if 'study_design' in validated_data:
obj.study_design = validated_data.pop('study_design')
if 'data_type' in validated_data:
obj.data_type = validated_data.pop('data_type')
if 'disease' in validated_data:
obj.disease = validated_data.pop('disease')

obj.save()
return obj
Expand Down
34 changes: 34 additions & 0 deletions osf/migrations/0017_auto_20231207_1407.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.17 on 2023-12-07 14:07

import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('osf', '0016_auto_20230828_1810'),
]

operations = [
migrations.AddField(
model_name='collection',
name='data_type_choices',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=127), blank=True, default=list, size=None),
),
migrations.AddField(
model_name='collection',
name='disease_choices',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=127), blank=True, default=list, size=None),
),
migrations.AddField(
model_name='collectionsubmission',
name='data_type',
field=models.CharField(blank=True, help_text='This field was added for use by Inflammatory Bowel Disease Genetics Consortium', max_length=500),
),
migrations.AddField(
model_name='collectionsubmission',
name='disease',
field=models.CharField(blank=True, help_text='This field was added for use by Inflammatory Bowel Disease Genetics Consortium', max_length=500),
),
]
21 changes: 20 additions & 1 deletion osf/models/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class Meta:
program_area_choices = ArrayField(models.CharField(max_length=127), blank=True, default=list)
school_type_choices = ArrayField(models.CharField(max_length=127), blank=True, default=list)
study_design_choices = ArrayField(models.CharField(max_length=127), blank=True, default=list)
disease_choices = ArrayField(models.CharField(max_length=127), blank=True, default=list)
data_type_choices = ArrayField(models.CharField(max_length=127), blank=True, default=list)
is_public = models.BooleanField(default=False, db_index=True)
is_promoted = models.BooleanField(default=False, db_index=True)
is_bookmark_collection = models.BooleanField(default=False, db_index=True)
Expand Down Expand Up @@ -160,7 +162,7 @@ def has_permission(self, user, perm):

def collect_object(
self, obj, collector, collected_type=None, status=None, volume=None, issue=None,
program_area=None, school_type=None, study_design=None):
program_area=None, school_type=None, study_design=None, data_type=None, disease=None):
""" Adds object to collection, creates CollectionSubmission reference
Performs type / metadata validation. User permissions checked in view.
Expand All @@ -177,6 +179,9 @@ def collect_object(
program_area = program_area or ''
school_type = school_type or ''
study_design = study_design or ''
data_type = data_type or ''
study_design = study_design or ''
disease = disease or ''

if not self.collected_type_choices and collected_type:
raise ValidationError('May not specify "type" for this collection')
Expand Down Expand Up @@ -220,6 +225,18 @@ def collect_object(
elif study_design not in self.study_design_choices:
raise ValidationError(f'"{study_design}" is not an acceptable "study_design" for this collection')

if disease:
if not self.disease_choices:
raise ValidationError('May not specify "disease" for this collection')
elif disease not in self.disease_choices:
raise ValidationError(f'"{disease}" is not an acceptable "disease" for this collection')

if data_type:
if not self.data_type_choices:
raise ValidationError('May not specify "data_type" for this collection')
elif data_type not in self.data_type_choices:
raise ValidationError(f'"{data_type}" is not an acceptable "data_type" for this collection')

if not any([isinstance(obj, t.model_class()) for t in self.collected_types.all()]):
# Not all objects have a content_type_pk, have to look the other way.
# Ideally, all objects would, and we could do:
Expand Down Expand Up @@ -248,6 +265,8 @@ def collect_object(
collection_submission.program_area = program_area
collection_submission.school_type = school_type
collection_submission.study_design = study_design
collection_submission.data_type = data_type
collection_submission.disease = disease
collection_submission.save()

return collection_submission
Expand Down
Loading

0 comments on commit 7f2ac33

Please sign in to comment.