Skip to content

Commit

Permalink
implement new annotator widget plugins framework and apply to form
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-jones committed Apr 6, 2023
1 parent e161f7a commit 5d0f0f5
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 25 deletions.
11 changes: 7 additions & 4 deletions portality/annotation/annotators/issn_active.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def retrieve_from_source(self, form, resources, annotations, logger):


class ISSNActive(ISSNAnnotator):
__identity__ = "issn_status"
__identity__ = "issn_active"

NOT_FOUND = "not_found"
FULLY_VALIDATED = "fully_validated"
Expand All @@ -73,7 +73,8 @@ def _apply_rule(self, field, value, data, fail, url, logger, annotations):
field=field,
original_value=value,
advice=self.NOT_FOUND,
reference_url=url
reference_url=url,
annotator=self.__identity__
)
else:
if data.is_registered():
Expand All @@ -82,15 +83,17 @@ def _apply_rule(self, field, value, data, fail, url, logger, annotations):
field=field,
original_value=value,
advice=self.FULLY_VALIDATED,
reference_url=url
reference_url=url,
annotator=self.__identity__
)
else:
logger("{y} is not fully validated at {x}".format(y=value, x=url))
annotations.add_annotation(
field=field,
original_value=value,
advice=self.NOT_VALIDATED,
reference_url=url
reference_url=url,
annotator=self.__identity__
)

def annotate(self, form: dict,
Expand Down
8 changes: 4 additions & 4 deletions portality/forms/application_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ class FieldDefinitions:
},
"widgets": [
"trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
"full_contents", # ~~^->FullContents:FormWidget~~
"annotation" # ~~^-> Annotation:FormWidget~~
"annotation", # ~~^-> Annotation:FormWidget~~
"full_contents" # ~~^->FullContents:FormWidget~~
]
},
"editor": {
Expand Down Expand Up @@ -343,8 +343,8 @@ class FieldDefinitions:
},
"widgets": [
"trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
"full_contents", # ~~^->FullContents:FormWidget~~
"annotation" # ~~^-> Annotation:FormWidget~~
"annotation", # ~~^-> Annotation:FormWidget~~
"full_contents" # ~~^->FullContents:FormWidget~~
]
},
"editor": {
Expand Down
11 changes: 8 additions & 3 deletions portality/models/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
"fields": {
"field": {"coerce": "unicode"},
"original_value": {"coerce": "unicode"},
"suggested_value": {"coerce": "unicode"},
"replaced_value": {"coerce": "unicode"},
"advice": {"coerce": "unicode"},
"reference_url": {"coerce": "unicode"}
"reference_url": {"coerce": "unicode"},
"annotator": {"coerce": "unicode"}
},
"lists": {
"suggested_value": {"contains": "field", "coerce": "unicode"}
}
}
}
Expand Down Expand Up @@ -94,7 +97,7 @@ def journal(self):
def journal(self, val):
self.__seamless__.set_with_struct("journal", val)

def add_annotation(self, field=None, original_value=None, suggested_value=None, advice=None, reference_url=None):
def add_annotation(self, field=None, original_value=None, suggested_value=None, advice=None, reference_url=None, annotator=None):
obj = {}
if field is not None:
obj["field"] = field
Expand All @@ -106,6 +109,8 @@ def add_annotation(self, field=None, original_value=None, suggested_value=None,
obj["advice"] = advice
if reference_url is not None:
obj["reference_url"] = reference_url
if annotator is not None:
obj["annotator"] = annotator

# ensure we add the annotation only once
self.__seamless__.delete_from_list("annotations", val=obj)
Expand Down
30 changes: 22 additions & 8 deletions portality/scripts/annotations.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from portality.tasks.application_annotations import Annotations
from portality.tasks.helpers import background_helper
from portality.models.background import StdOutBackgroundJob
from portality.bll import DOAJ
from portality import models

if __name__ == '__main__':

Expand All @@ -9,12 +8,27 @@
parser = argparse.ArgumentParser()

parser.add_argument("-a", "--application", help="ID of application on which to run annotations")
parser.add_argument("-j", "--journal", help="ID of journal on which to run annotations")

args = parser.parse_args()
if args.application is None:
print("You must specify an application id with the -a argument")
if args.application is None and args.journal is None:
print("You must specify an application id with the -a argument, or a journal with the -j argument")
exit(1)

background_helper.execute_by_bg_task_type(Annotations,
job_wrapper=StdOutBackgroundJob,
application=args.application)
anno_svc = DOAJ.annotationsService()

if args.application:
application = models.Application.pull(args.application)
if application is None:
print("Application ID did not resolve to an application")
exit(1)
print("\nAnnotating application {x}".format(x=application.id))
anno_svc.annotate_application(application, logger=lambda x: print(x))

if args.journal:
journal = models.Journal.pull(args.journal)
if journal is None:
print("Journal ID did not resolve to a journal record")
exit(1)
print("\nAnnotating journal {x}".format(x=journal.id))
anno_svc.annotate_journal(journal, logger=lambda x: print(x))
33 changes: 33 additions & 0 deletions portality/static/js/annotators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
if (!window.hasOwnProperty("doaj")) { doaj = {}}
if (!doaj.hasOwnProperty("annotators")) { doaj.annotators = {}}

doaj.annotators.ISSNActive = class {

MESSAGES = {
"unable_to_access": "We were unable to access the ISSN.org service",
"not_found": "The ISSN was not found at ISSN.org",
"fully_validated": "The ISSN is fully registered at ISSN.org",
"not_validated": "The ISSN has not been registered at ISSN.org"
}

ICONS = {
"unable_to_access": "clock",
"not_found": "x-circle",
"fully_validated": "check-circle",
"not_validated": "x-circle"
}

draw(annotation) {
let icon = this.ICONS[annotation.advice];
let message = this.MESSAGES[annotation.advice];

let frag = `<span data-feather="${icon}" aria-hidden="true"></span>
Checked ${annotation.original_value}: ${message} (see
<a href="${annotation.reference_url}">${annotation.reference_url})</a>`;
return frag;
}
}

doaj.annotators.registry = {
"issn_active": doaj.annotators.ISSNActive
}
20 changes: 17 additions & 3 deletions portality/static/js/formulaic.js
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,8 @@ var formulaic = {
let el = $(elements[i]);
el.after(frag);
}

feather.replace();
}

this._getAnnotationsForField = function() {
Expand All @@ -1119,20 +1121,32 @@ var formulaic = {

this._renderAnnotation = function(annotation) {
let frag = "<li>";

if (annotation.annotator && doaj.annotators &&
doaj.annotators.registry.hasOwnProperty(annotation.annotator)) {
frag += (new doaj.annotators.registry[annotation.annotator]()).draw(annotation)
} else {
frag += this._defaultRender(annotation);
}

frag += `</li>`;
return frag;
}

this._defaultRender = function(annotation) {
let frag = "";
if (annotation.advice) {
frag += `${annotation.advice}<br>`
}
if (annotation.reference_url) {
frag += `<a href="${annotation.reference_url}" target="_blank">${annotation.reference_url}</a><br>`
}
if (annotation.suggested_value) {
frag += `Suggested Value: ${annotation.suggested_value}<br>`
frag += `Suggested Value(s): ${annotation.suggested_value.join(", ")}<br>`
}
if (annotation.original_value) {
frag += `(Original value when automated checks ran: ${annotation.original_value})`
}
frag += `</li>`;

return frag;
}

Expand Down
3 changes: 3 additions & 0 deletions portality/templates/application_form/maned_application.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ <h3 id="modalLabel-quick_reject" class="modal__title">
{% endblock %}

{% block extra_js_bottom scoped %}
{% if annotations %}
<script type="application/javascript">
doaj.annotations = {{ annotations.data|tojson }}
</script>
<script type="text/javascript" src="/static/js/annotators.js?v={{config.get('DOAJ_VERSION')}}"></script>
{% endif %}
{% include "application_form/js/_form_js.html" %}
{% endblock %}
6 changes: 6 additions & 0 deletions portality/templates/application_form/maned_journal.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ <h3 class="modal__title" id="modalLabel-withdraw_reinstate">
{% endblock %}

{% block extra_js_bottom scoped %}
{% if annotations %}
<script type="application/javascript">
doaj.annotations = {{ annotations.data|tojson }}
</script>
<script type="text/javascript" src="/static/js/annotators.js?v={{config.get('DOAJ_VERSION')}}"></script>
{% endif %}
{% set factory = "journal" %}
{% include "application_form/js/_form_js.html" %}
{% endblock %}
9 changes: 6 additions & 3 deletions portality/view/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ def journal_page(journal_id):
return render_template("admin/journal_locked.html", journal=journal, lock=l.lock)

fc = JournalFormFactory.context("admin")

annotations = models.Annotation.for_journal(journal_id)

if request.method == "GET":
job = None
job_id = request.values.get("job")
Expand All @@ -220,7 +223,7 @@ def journal_page(journal_id):
url = url_for("admin.background_jobs_search") + "?source=" + dao.Facetview2.url_encode_query(dao.Facetview2.make_query(job_id))
Messages.flash_with_url(Messages.ADMIN__WITHDRAW_REINSTATE.format(url=url), "success")
fc.processor(source=journal)
return fc.render_template(lock=lockinfo, job=job, obj=journal, lcc_tree=lcc_jstree)
return fc.render_template(lock=lockinfo, job=job, obj=journal, lcc_tree=lcc_jstree, annotations=annotations)

elif request.method == "POST":
processor = fc.processor(formdata=request.form, source=journal)
Expand All @@ -235,7 +238,7 @@ def journal_page(journal_id):
flash(str(e))
return redirect(url_for("admin.journal_page", journal_id=journal.id, _anchor='cannot_edit'))
else:
return fc.render_template(lock=lockinfo, obj=journal, lcc_tree=lcc_jstree)
return fc.render_template(lock=lockinfo, obj=journal, lcc_tree=lcc_jstree, annotations=annotations)

######################################################
# Endpoints for reinstating/withdrawing journals from the DOAJ
Expand Down Expand Up @@ -391,7 +394,7 @@ def application(application_id):
flash(str(e))
return redirect(url_for("admin.application", application_id=ap.id, _anchor='cannot_edit'))
else:
return fc.render_template(obj=ap, lock=lockinfo, form_diff=form_diff, current_journal=current_journal, lcc_tree=lcc_jstree)
return fc.render_template(obj=ap, lock=lockinfo, form_diff=form_diff, current_journal=current_journal, lcc_tree=lcc_jstree, annotations=annotations)


@blueprint.route("/application_quick_reject/<application_id>", methods=["POST"])
Expand Down

0 comments on commit 5d0f0f5

Please sign in to comment.