diff --git a/.elasticbeanstalk/config.yml b/.elasticbeanstalk/config.yml index 2d7244ba0..c4d6b6a63 100644 --- a/.elasticbeanstalk/config.yml +++ b/.elasticbeanstalk/config.yml @@ -4,7 +4,8 @@ branch-defaults: develop: environment: isiscb-develop global: - application_name: IsisCB + application_name: IsisCB Staging Python 3 + #application_name: IsisCB Celery Staging Python 3 (non worker) default_ec2_keyname: isiscb-eb default_platform: 64bit Amazon Linux 2015.03 v2.0.1 running Python 2.7 default_region: us-west-2 diff --git a/isiscb/curation/forms.py b/isiscb/curation/forms.py index 458ee77cd..13636ed4c 100644 --- a/isiscb/curation/forms.py +++ b/isiscb/curation/forms.py @@ -234,6 +234,36 @@ class Meta(object): model = AuthorityValue fields = ['value'] +class CitationValueForm(forms.ModelForm): + value = forms.CharField(label="Citation ID", widget=forms.TextInput(attrs={'data-type':'citation_id'})) + citation_name = forms.CharField(label='Name of stored citation', widget=forms.TextInput(attrs={'readonly': True})) + + def __init__(self, *args, **kwargs): + super(CitationValueForm, self).__init__(*args, **kwargs) + instance = kwargs.get('instance') + + if instance and not self.is_bound: + self.fields['value'].initial = instance.pk + self.fields['citation_name'].initial = instance.value.title_for_display + + def clean_value(self): + value = self.cleaned_data['value'] + + try: + value = Citation.objects.get(id=value) + except: + raise forms.ValidationError('Citation record does not exist.') + + return value + + def save(self, *args, **kwargs): + self.instance.value = self.cleaned_data.get('value') + super(CitationValueForm, self).save(*args, **kwargs) + + class Meta(object): + model = CitationValue + fields = ['value'] + class PartDetailsForm(forms.ModelForm): extent_note = forms.CharField(widget=forms.widgets.Textarea({'rows': '1'}), required=False) diff --git a/isiscb/curation/templates/curation/authority_attribute_changeview.html b/isiscb/curation/templates/curation/authority_attribute_changeview.html index 8c62ad946..8c4a628ad 100644 --- a/isiscb/curation/templates/curation/authority_attribute_changeview.html +++ b/isiscb/curation/templates/curation/authority_attribute_changeview.html @@ -122,6 +122,7 @@ {{ attribute_form.value_freeform|addcss:"form-control" }} +
@@ -170,7 +171,7 @@ var value = $(this).val(); var container = $('#value_form_container'); container.empty(); - var form = $('#form_for_' + value).clone(); + var form = $('#form_for_' + value).clone(withDataAndEvents=true); container.append(form); // update help text @@ -205,6 +206,19 @@ $("#geocode-attr-warning").modal('hide'); $("#attributeForm").submit(); }) + + $("[data-type='citation_id']").change(function() { + $.ajax({ + url: "{% url 'curation:api_citation' %}" + "?id=" + $("[data-type='citation_id']").val(), + success: function(data) { + $("#ajax_errors").text("") + $("#id_value-citation_name").val(data['title']) + }, + error: function(data) { + $("#ajax_errors").html(' Citation with provided id could not be found.') + } + }); + }); }); @@ -265,7 +279,7 @@ You are about to delete an attribute of type . Deletion cannot be undone!

{% if attribute.type_controlled.name == country_code_attribute %} -

+

This attribute is a country code. You might need to reindex related citations to make sure your changes are reflected on the public site.

{% endif %} diff --git a/isiscb/curation/urls.py b/isiscb/curation/urls.py index 28a6b9d28..d35f9ee8c 100644 --- a/isiscb/curation/urls.py +++ b/isiscb/curation/urls.py @@ -49,6 +49,8 @@ re_path(r'^citation/(?P[A-Z0-9]+)/$', views.citation, name='curate_citation'), re_path(r'^authority/(?P[A-Z0-9]+)/$', views.authority, name='curate_authority'), + re_path(r'^api/citation$', views.get_citation_by_id, name='api_citation'), + re_path(r'^timelines$', bulk_change_csv_views.timeline_tasks, name='timeline_tasks'), re_path(r'^timelines/(?P[A-Z0-9]+)/delete$', bulk_change_csv_views.timeline_delete, name='delete_timeline'), diff --git a/isiscb/curation/view_helpers.py b/isiscb/curation/view_helpers.py index e1c1a57f1..e03863809 100644 --- a/isiscb/curation/view_helpers.py +++ b/isiscb/curation/view_helpers.py @@ -13,6 +13,8 @@ def _create_attribute_value_forms(): value_forms[at.id] = ISODateValueForm elif value_class is AuthorityValue: value_forms[at.id] = AuthorityValueForm + elif value_class is CitationValue: + value_forms[at.id] = CitationValueForm else: value_forms[at.id] = modelform_factory(value_class, exclude=('attribute', 'child_class')) diff --git a/isiscb/curation/views.py b/isiscb/curation/views.py index 25fbaf7ad..889763f72 100644 --- a/isiscb/curation/views.py +++ b/isiscb/curation/views.py @@ -2136,6 +2136,19 @@ def search_users(request): } for u in queryset[:20]] return JsonResponse(results, safe=False) +@user_passes_test(lambda u: u.is_superuser or u.is_staff) +def get_citation_by_id(request): + id = request.GET.get('id', None) + if not id: + return JsonResponse({'citation': None}) + + citation = Citation.objects.filter(id=id).first() + if not citation: + return JsonResponse({}, status=404) + return JsonResponse({ + 'id': citation.id, + 'title': citation.title_for_display, + }) @user_passes_test(lambda u: u.is_superuser or u.is_staff) def quick_and_dirty_citation_search(request): @@ -2293,7 +2306,7 @@ def _get_filtered_queryset(request, object_type='CITATION'): if 'collection_only' in filter_params: filter_params.pop('collection_only') filter_params_raw = filter_params.urlencode()#.encode('utf-8') - if object_type is 'CITATION': + if object_type == 'CITATION': _qs = operations.filter_queryset(request.user, Citation.objects.all()) queryset = CitationFilter(filter_params, queryset=_qs) else: diff --git a/isiscb/isiscb/local_postgresql_settings.py b/isiscb/isiscb/local_postgresql_settings.py index 7d3fd6f43..8aa1af697 100644 --- a/isiscb/isiscb/local_postgresql_settings.py +++ b/isiscb/isiscb/local_postgresql_settings.py @@ -327,6 +327,7 @@ TIMELINE_PUBLICATION_DATE_ATTRIBUTE = os.environ.get('TIMELINE_PUBLICATION_DATE_ATTRIBUTE', "PublicationDate") RECORD_SUBTYPE_ATTRIBUTE = os.environ.get('RECORD_SUBTYPE_ATTRIBUTE', "RecordSubType") ACCESSED_ATTRIBUTE_NAME = os.environ.get('ACCESSED_ATTRIBUTE_NAME', "LastAccessedDate") +BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME = os.environ.get('BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME', "BibliographicEssay") DOI_LINKED_DATA_NAME = os.environ.get('DOI_LINKED_DATA_NAME', "DOI") ISBN_LINKED_DATA_NAME = os.environ.get('ISBN_LINKED_DATA_NAME', "ISBN") diff --git a/isiscb/isiscb/production_settings.py b/isiscb/isiscb/production_settings.py index fb2f5f92c..1542c10b7 100644 --- a/isiscb/isiscb/production_settings.py +++ b/isiscb/isiscb/production_settings.py @@ -412,6 +412,7 @@ TIMELINE_PUBLICATION_DATE_ATTRIBUTE = os.environ.get('TIMELINE_PUBLICATION_DATE_ATTRIBUTE', "PublicationDate") RECORD_SUBTYPE_ATTRIBUTE = os.environ.get('RECORD_SUBTYPE_ATTRIBUTE', "RecordSubType") ACCESSED_ATTRIBUTE_NAME = os.environ.get('ACCESSED_ATTRIBUTE_NAME', "LastAccessedDate") +BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME = os.environ.get('BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME', "BibliographicEssay") COUNTRY_CODE_ATTRIBUTE = os.environ.get('COUNTRY_CODE_ATTRIBUTE', "CountryCode") diff --git a/isiscb/isiscb/test_settings.py b/isiscb/isiscb/test_settings.py index 6b8b442d1..2f65e30ed 100644 --- a/isiscb/isiscb/test_settings.py +++ b/isiscb/isiscb/test_settings.py @@ -321,6 +321,7 @@ TIMELINE_PUBLICATION_DATE_ATTRIBUTE = os.environ.get('TIMELINE_PUBLICATION_DATE_ATTRIBUTE', "PublicationDate") RECORD_SUBTYPE_ATTRIBUTE = os.environ.get('RECORD_SUBTYPE_ATTRIBUTE', "RecordSubType") ACCESSED_ATTRIBUTE_NAME = os.environ.get('ACCESSED_ATTRIBUTE_NAME', "LastAccessedDate") +BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME = os.environ.get('BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME', "BibliographicEssay") DOI_LINKED_DATA_NAME = os.environ.get('DOI_LINKED_DATA_NAME', "DOI") ISBN_LINKED_DATA_NAME = os.environ.get('ISBN_LINKED_DATA_NAME', "ISBN") diff --git a/isiscb/isisdata/migrations/0095_auto_20210826_0119.py b/isiscb/isisdata/migrations/0095_auto_20210826_0119.py new file mode 100644 index 000000000..1208938d3 --- /dev/null +++ b/isiscb/isisdata/migrations/0095_auto_20210826_0119.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.7 on 2021-08-26 01:19 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('isisdata', '0094_auto_20210717_1853'), + ] + + operations = [ + migrations.AlterField( + model_name='attributetype', + name='value_content_type', + field=models.ForeignKey(limit_choices_to=models.Q(('model', 'textvalue'), ('model', 'charvalue'), ('model', 'intvalue'), ('model', 'datetimevalue'), ('model', 'datevalue'), ('model', 'floatvalue'), ('model', 'locationvalue'), ('model', 'isodatevalue'), ('model', 'isodaterangevalue'), ('model', 'authorityvalue'), ('model', 'citationvalue'), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, related_name='attribute_value', to='contenttypes.ContentType'), + ), + migrations.CreateModel( + name='CitationValue', + fields=[ + ('value_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='isisdata.Value')), + ('name', models.TextField(blank=True, null=True)), + ('value', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='isisdata.Citation')), + ], + options={ + 'verbose_name': 'citation', + }, + bases=('isisdata.value',), + ), + ] diff --git a/isiscb/isisdata/models.py b/isiscb/isisdata/models.py index e14f202ea..6f1a27139 100644 --- a/isiscb/isisdata/models.py +++ b/isiscb/isisdata/models.py @@ -43,7 +43,8 @@ VALUETYPES = Q(model='textvalue') | Q(model='charvalue') | Q(model='intvalue') \ | Q(model='datetimevalue') | Q(model='datevalue') \ | Q(model='floatvalue') | Q(model='locationvalue') \ - | Q(model='isodatevalue') | Q(model='isodaterangevalue') | Q(model='authorityvalue') + | Q(model='isodatevalue') | Q(model='isodaterangevalue') \ + | Q(model='authorityvalue') | Q(model='citationvalue') class Value(models.Model): @@ -533,6 +534,34 @@ def __str__(self): class Meta(object): verbose_name = 'location' +class CitationValue(Value): + """ + A citation value. Points to an instance of :class:`.Citation`\. + """ + # CHECK: Had to add on_delete so chose cascade -> JD: since we don't delete Authorities at the moment, this is probably fine + value = models.ForeignKey('Citation', on_delete=models.CASCADE) + name = models.TextField(blank=True, null=True) + + def __unicode__(self): + return str(self.value) + + def __str__(self): + return str(self.value) + + class Meta(object): + verbose_name = 'citation' + + @staticmethod + def convert(value): + if type(value) is Citation: + return value + + try: + return Citation.objects.get(pk=value) + except ValueError: + raise ValidationError('Must be the id of an existing citation.') + + class AuthorityValue(Value): """ An authority value. Points to an instance of :class:`.Authority`\. diff --git a/isiscb/isisdata/templates/isisdata/authority_fragments/fragment_authority_info_box.html b/isiscb/isisdata/templates/isisdata/authority_fragments/fragment_authority_info_box.html index 935c4d4b7..dad6f910e 100644 --- a/isiscb/isisdata/templates/isisdata/authority_fragments/fragment_authority_info_box.html +++ b/isiscb/isisdata/templates/isisdata/authority_fragments/fragment_authority_info_box.html @@ -2,6 +2,7 @@ {% load static %} {% load search_filters %} {% load authority_filters %} +{% load citation_filters %} {% load facet_filters %} @@ -131,8 +132,24 @@

{{ authority.name }}

{% endif %} + {% with authority|get_bibliographic_essays as bib_essays %} + {% if bib_essays %} + {% for bib_essay in bib_essays %} +

+ There is a bibliographic essay on this topic: "{{bib_essay.value.cvalue.title_for_display}}" by {{bib_essay.value.cvalue|get_authors|join_authors:""}} + + {% if bib_essay.value.cvalue|get_urls %} + {% for url in bib_essay.value.cvalue|get_urls %} +
Link: {{ url.resource_name }} + {% endfor %} + {% endif %} +

+ {% endfor %} + {% endif %} + {% endwith %} + {% for attribute in authority.attributes.all %} - {% if attribute|is_attribute_visible %} + {% if attribute|is_attribute_visible and not attribute|is_bibliographic_essay %}

{{ attribute.type_controlled.display_name }}: {% if attribute.value_freeform %}{{ attribute.value_freeform }}{% else %}{{ attribute.value.display }}{% endif %}

{% endif %} {% endfor %} diff --git a/isiscb/isisdata/templatetags/authority_filters.py b/isiscb/isisdata/templatetags/authority_filters.py index cafed3b6f..9694573bc 100644 --- a/isiscb/isisdata/templatetags/authority_filters.py +++ b/isiscb/isisdata/templatetags/authority_filters.py @@ -15,3 +15,15 @@ def is_attribute_visible(attribute): return False return attribute.public + +@register.filter +def is_bibliographic_essay(attribute): + return attribute.type_controlled.name==settings.BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME + +@register.filter +def get_bibliographic_essays(authority): + return authority.attributes.filter(type_controlled__name=settings.BIBLIOGRAPHIC_ESSAY_ATTRIBUTE_NAME) + +@register.filter +def get_urls(citation): + return citation.linkeddata_entries.filter(type_controlled__name=settings.URL_LINKED_DATA_NAME) diff --git a/isiscb/isisdata/templatetags/citation_filters.py b/isiscb/isisdata/templatetags/citation_filters.py index 4c6a92748..6f400707d 100644 --- a/isiscb/isisdata/templatetags/citation_filters.py +++ b/isiscb/isisdata/templatetags/citation_filters.py @@ -46,6 +46,12 @@ def get_editors(citation): return citation.acrelation_set.filter(type_controlled__in=['ED']) return citation +@register.filter +def get_authors(citation): + if citation: + return citation.acrelation_set.filter(type_controlled__in=['AU'], citation__public=True, public=True).order_by('data_display_order') + return citation + @register.filter def join_names_with_postfix(name_list, postfix): diff --git a/requirements.txt b/requirements.txt index c3a0c059c..e58aeb6ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,14 @@ amqp==2.5.2 -asgiref==3.2.7 +#asgiref==3.2.7 attrs==19.3.0 -awsebcli==3.18.2 +awsebcli==3.20.2 backports.ssl-match-hostname==3.7.0.1 bcrypt==3.1.7 billiard==3.6.3.0 bleach==3.1.4 blessed==1.17.4 -boto3==1.12.36 -botocore==1.15.36 +boto3==1.18.35 +botocore==1.21.36 cached-property==1.5.1 cachetools==4.0.0 celery==4.4.2 @@ -21,14 +21,14 @@ colorclass==2.2.0 confusable-homoglyphs==3.2.0 cryptography==2.9 defusedxml==0.6.0 -Django==3.0.7 +Django==3.1.12 django-allauth==0.41.0 django-autocomplete-light==3.5.1 django-braces==1.14.0 django-celery-results==1.2.1 django-cors-headers==3.2.1 django-extensions==2.2.9 -django-filter==2.2.0 +django-filter==2.4.0 django-guardian==2.2.0 django-haystack==3.0b1 django-ipware==2.1.0 @@ -76,7 +76,7 @@ paramiko==2.7.1 pathspec==0.5.9 pbr==5.4.4 pefile==2019.4.18 -Pillow==8.0 +Pillow==8.3.2 pip-upgrader==1.4.15 protobuf==3.11.3 psycopg2-binary==2.8.6 @@ -96,12 +96,12 @@ PyYAML==5.3.1 rdflib==4.2.2 redis==3.4.1 regex==2020.4.4 -requests==2.20.1 +#requests==2.20.1 requests-oauthlib==1.3.0 rsa==4.0 rules==2.2 -s3transfer==0.3.3 -semantic-version==2.5.0 +#s3transfer==0.3.3 +#semantic-version==2.5.0 six==1.11.0 smart-open==1.10.0 social-auth-app-django==3.1.0 @@ -112,7 +112,7 @@ termcolor==1.1.0 terminaltables==3.1.0 texttable==1.6.2 Unidecode==1.1.1 -urllib3==1.24.3 +urllib3==1.26.6 vine==1.3.0 wcwidth==0.1.9 webencodings==0.5.1