From ee641aa42fb14acb2ae772dc95d7764b6f156b56 Mon Sep 17 00:00:00 2001 From: Julia Damerow Date: Sun, 31 Jan 2021 15:27:59 -0500 Subject: [PATCH 1/3] Story/iexp 279 (#677) * [IEXP-279] added task and action for bulk deletion of duplicates * [IEXP-279] added code to delete attributes with same type, value, and freetext value --- isiscb/curation/actions.py | 24 +++++++++++++++++ isiscb/curation/forms.py | 2 +- isiscb/curation/taskslib/authority_tasks.py | 29 +++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/isiscb/curation/actions.py b/isiscb/curation/actions.py index 0b4e739c2..b9797cbe2 100644 --- a/isiscb/curation/actions.py +++ b/isiscb/curation/actions.py @@ -288,4 +288,28 @@ def apply(self, user, filter_params_raw, value, **extra): task.save() return task.id +class DeleteDuplicateAttributes(BaseAction): + model = Authority + label = u'Delete Duplicate Attributes' + + default_value_field = forms.CharField + default_value_field_kwargs = { + 'label': 'Delete Duplicate Attributes', + 'widget': forms.widgets.Textarea(attrs={'class': 'action-value', 'readonly': True, 'initial': 'Delete Duplicate Attributes'}), + } + + def apply(self, user, filter_params_raw, value, **extra): + task = AsyncTask.objects.create() + + result = atasks.delete_duplicate_attributes.delay(user.id, filter_params_raw, task.id) + + # We can use the AsyncResult's UUID to access this task later, e.g. + # to check the return value or task state. + task.async_uuid = result.id + task.value = ('delete_duplicate_attributes', value) + task.label = 'Deleting Duplicate Attributes: ' + _build_filter_label(filter_params_raw) + task.save() + return task.id + AVAILABLE_ACTIONS = [SetRecordStatus, SetRecordStatusExplanation, SetTrackingStatus, PrependToRecordHistory, StoreCreationDataToModel, ReindexCitation] +AVAILABLE_ACTIONS_AUTHORITY = [StoreCreationDataToModel, ReindexAuthorities, DeleteDuplicateAttributes] diff --git a/isiscb/curation/forms.py b/isiscb/curation/forms.py index a0fd1d4b0..188bb09fb 100644 --- a/isiscb/curation/forms.py +++ b/isiscb/curation/forms.py @@ -678,7 +678,7 @@ def bulk_action_form_factory(form=BulkActionForm, **kwargs): action_choices = [] extra_data = {} # hack until we also make tracking status work - avail_actions = [actions.StoreCreationDataToModel, actions.ReindexAuthorities] if object_type == 'AUTHORITY' else actions.AVAILABLE_ACTIONS + avail_actions = actions.AVAILABLE_ACTIONS_AUTHORITY if object_type == 'AUTHORITY' else actions.AVAILABLE_ACTIONS for action_class in avail_actions: if hasattr(action_class, 'extra_js'): media_attrs['js'] = tuple(list(media_attrs['js']) + [action_class.extra_js]) diff --git a/isiscb/curation/taskslib/authority_tasks.py b/isiscb/curation/taskslib/authority_tasks.py index 4ab1190ee..458f490b2 100644 --- a/isiscb/curation/taskslib/authority_tasks.py +++ b/isiscb/curation/taskslib/authority_tasks.py @@ -33,6 +33,23 @@ logger = logging.getLogger(__name__) +@shared_task +def delete_duplicate_attributes(user_id, filter_params_raw, task_id=None, object_type='AUTHORITY'): + queryset, task = _get_task(filter_params_raw, user_id, task_id, object_type) + current_count = 0 + for i, obj in enumerate(queryset): + existing_attributes = [] + for attribute in obj.attributes.all(): + attr_type = attribute.type_controlled + key = attr_type.name + "_" + str(attribute.value.cvalue()) + str(attribute.value_freeform) + if key not in existing_attributes: + existing_attributes.append(key) + else: + # attribute with same values already exist, so remove it + print("Deleting attribute " + attribute.pk + " on object " + obj.pk) + attribute.delete() + current_count = _update_count(current_count, task) + @shared_task def reindex_authorities(user_id, filter_params_raw, task_id=None, object_type='AUTHORITY'): @@ -270,6 +287,18 @@ def add_attributes_to_authority(file_path, error_path, task_id, user_id): task.state = 'SUCCESS' task.save() +def _get_task(filter_params_raw, user_id, task_id, object_type): + queryset, _ = _get_filtered_object_queryset(filter_params_raw, user_id, object_type) + if task_id: + task = AsyncTask.objects.get(pk=task_id) + task.max_value = queryset.count() + _inc = max(2, math.floor(old_div(task.max_value, 200.))) + task.save() + else: + task = None + + return queryset, task + def _add_creation_note(properties, task_id, user_id, created_on): user = User.objects.get(pk=user_id) mod_time = created_on.strftime("%m/%d/%y %r %Z") From d04910129892991da53eed59bb083d501e1437f5 Mon Sep 17 00:00:00 2001 From: Julia Damerow Date: Sun, 7 Feb 2021 17:12:28 -0500 Subject: [PATCH 2/3] [IEXP-68] added check for subject or object before displaying asso. citation (#679) --- isiscb/isisdata/templates/isisdata/citation.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/isiscb/isisdata/templates/isisdata/citation.html b/isiscb/isisdata/templates/isisdata/citation.html index 9cf35faac..35e48a295 100644 --- a/isiscb/isisdata/templates/isisdata/citation.html +++ b/isiscb/isisdata/templates/isisdata/citation.html @@ -280,9 +280,15 @@

Has Response

{% if related_citations_as %}

Associated with

{% for cc_rel in related_citations_as %} + {% if cc_rel.subject.id == citation.id %} {% with cc_rel.object as citation_object %} {% include "isisdata/citation_entry.html" %} {% endwith %} + {% else %} + {% with cc_rel.subject as citation_object %} + {% include "isisdata/citation_entry.html" %} + {% endwith %} + {% endif %} {% endfor %} {% endif %} From c242644db89ccbfcfbd9b89504b17c9762b31de0 Mon Sep 17 00:00:00 2001 From: Julia Damerow Date: Sun, 7 Feb 2021 20:24:57 -0500 Subject: [PATCH 3/3] [IEXP-24] use value node of subjects if there is another enclosing node (#680) --- isiscb/zotero/parse.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/isiscb/zotero/parse.py b/isiscb/zotero/parse.py index 9f913020f..306fb0396 100644 --- a/isiscb/zotero/parse.py +++ b/isiscb/zotero/parse.py @@ -365,6 +365,14 @@ def handle_isPartOf(self, predicate, node): return predicate, dict(parent_document) def handle_subjects(self, predicate, node): + # IEXP-24: somtimes/potentially always, the subject node contains an + # anonymous node that will display its idea if we don't traverse further + # e.g + # + # Australia + # + if type(node) == BNode: + node = self.graph.value(subject=node, predicate=RDF.value) match = re.match('([^\[]+)\[([A-Z0-9\w]+)\]', node.toPython()) if match: name, identifier = match.groups()