From bfb78a6c951d90e356fb5def2c4576e1526e444f Mon Sep 17 00:00:00 2001
From: Sebastiano Giacomini <92300303+Sebastiano-G@users.noreply.github.com>
Date: Wed, 14 Feb 2024 19:16:01 +0100
Subject: [PATCH] save hidden triples #11; modify/remove i. t. #10
#11 : introduced a mechanism to retrieve and save previously added hidden triples while updating a named graph;
#10 : introduced the possibility to integrate advanced field types (e.g. Textbox > entity; SKOS vocabularies; etc.) within subrecord forms. Automatic suggestions' visualisation should be improved (i.e., needs to be properly placed within the corresponding section). Moreover, two novel functions have been introduced to either remove entire subrecords or modify them (the latter function is not over yet: advanced fields require further attention)
---
mapping.py | 41 ++++++++---
queries.py | 28 ++++++++
static/js/main.js | 173 +++++++++++++++++++++++++++++++---------------
3 files changed, 176 insertions(+), 66 deletions(-)
diff --git a/mapping.py b/mapping.py
index b4e5fc4..4c7ae9e 100644
--- a/mapping.py
+++ b/mapping.py
@@ -98,6 +98,20 @@ def inputToRDF(recordData, userID, stage, knowledge_extraction, graphToClear=Non
if creatorIRI is not None and creatorLabel is not None:
wd.add(( URIRef(base+graph_name+'/'), PROV.wasAttributedTo, URIRef(creatorIRI) ))
wd.add(( URIRef(creatorIRI), RDFS.label , Literal(creatorLabel ) ))
+
+ # retrieve hidden triples (to be saved) and re-introduce them in the named graph
+ to_be_saved = queries.saveHiddenTriples(graphToClear, tpl_form)
+ if to_be_saved:
+ for binding in to_be_saved['results']['bindings']:
+ subject = URIRef(binding['subject']['value'])
+ for predicate, obj in binding.items():
+ if predicate != 'subject':
+ if obj['type'] == 'uri':
+ object_ = URIRef(obj['value'])
+ else:
+ object_ = Literal(obj['value'])
+ wd.add((subject, URIRef(predicate), object_))
+
queries.clearGraph(graphToClear)
wd.add(( URIRef(base+graph_name+'/'), PROV.generatedAtTime, Literal(datetime.datetime.now(),datatype=XSD.dateTime) ))
wd.add(( URIRef(base+graph_name+'/'), URIRef('http://dbpedia.org/ontology/currentStatus'), Literal(stage, datatype="http://www.w3.org/2001/XMLSchema#string") ))
@@ -110,6 +124,7 @@ def inputToRDF(recordData, userID, stage, knowledge_extraction, graphToClear=Non
if len(is_any_disambiguate) == 0:
wd.add(( URIRef(base+graph_name+'/'), RDFS.label, Literal("no title") ))
+ fields = [input_field for input_field in fields if input_field['hidden'] == 'False']
for field in fields:
if field['type'] not in ['KnowledgeExtractor', 'Subtemplate']:
# URI, Textarea (only text at this stage), Literals
@@ -212,31 +227,36 @@ def inputToRDF(recordData, userID, stage, knowledge_extraction, graphToClear=Non
# convert the dict of inputs into a series of nested dictionaries to be parsed as single records
def process_subrecords(data, id):
results = {}
- created_subrecords = [key for key in data if key.startswith(id+"-")]
+ created_subrecords = [key for key in data if key.startswith(id+"__")]
if created_subrecords != []:
for subrecord in created_subrecords:
add_results = {}
- subrecord_split = subrecord.split('-')
+ subrecord_split = subrecord.split('__')
prefix, num = subrecord_split[0], subrecord_split[-1]
subrecord_fields = data[subrecord].split(',')
- inner_subrecords = [key for item in subrecord_fields for key in data.keys() if key.startswith(item + "-")]
+ inner_subrecords = [key for item in subrecord_fields for key in data.keys() if key.startswith(item + "__")]
for key in subrecord_fields:
if data[key] != "":
- add_results[key.split('-')[0]] = data[key]
+ add_results[key.split('__')[0]] = data[key]
else:
- inner_subrecords = [inner_subrecord for inner_subrecord in data.keys() if inner_subrecord.startswith(key + "-")]
- for inner_subrecord in inner_subrecords:
- if inner_subrecord.startswith(key + '-'):
- inner_subrecord_split = inner_subrecord.split('-')
+ inner_subrecords = [inner_subrecord for inner_subrecord in data.keys() if inner_subrecord.startswith(key + "__")]
+ if inner_subrecords != []:
+ for inner_subrecord in inner_subrecords:
+ inner_subrecord_split = inner_subrecord.split('__')
inner_prefix, inner_num = inner_subrecord_split[0], inner_subrecord_split[-1]
add_results[inner_prefix] = {
inner_num: process_subrecords(data, inner_subrecord)
}
+ else:
+ imported_values = [import_key for import_key in data.keys() if import_key.startswith(key + "-")]
+ for imported_value in imported_values:
+ new_key = imported_value.split('__')[0] + "-" + imported_value.split('-')[-1]
+ add_results[new_key] = data[imported_value]
if prefix in results:
results[prefix][num] = add_results
else:
results[prefix] = { num: add_results }
- else:
+ elif data[id] != "":
for el in data[id].split(','):
results[el.split('-')[0]] = data[el]
return results
@@ -250,5 +270,4 @@ def find_label(tpl, subrecord, alternative_label):
# Add a mechanism to handle potential Templates without a Primary Key (e.g. the primary key has been set to "hidden")
label = subrecord[label_field_id] if label_field_id in subrecord else alternative_label+"-"+subrecord['recordID']
- return label
-
+ return label
\ No newline at end of file
diff --git a/queries.py b/queries.py
index 386e597..45435c4 100644
--- a/queries.py
+++ b/queries.py
@@ -352,3 +352,31 @@ def retrieve_extractions(res_uri):
next_id = max(ke_dict[res_uri.split("/")[-1]], key=lambda x: int(x["internalID"]))
res_dict['next_id'] = int(next_id['internalID']) + 1
return res_dict
+
+def saveHiddenTriples(graph, tpl):
+ with open(tpl) as template:
+ fields = json.load(template)
+
+ results = []
+ hidden_fields = [field for field in fields if field['hidden'] == 'True']
+ patterns = [ 'OPTIONAL {?subject <'+hidden_field['property']+'> ?'+hidden_field['id']+'.}. ' if hidden_field['value'] in ['Literal','Date','gYearMonth','gYear','URL'] else 'OPTIONAL {?subject <'+hidden_field['property']+'> ?'+hidden_field['id']+'. ?'+hidden_field['id']+' rdfs:label ?'+hidden_field['id']+'_label .} .' for hidden_field in hidden_fields if 'value' in hidden_field and hidden_field['hidden'] == 'True']
+ if patterns != []:
+ patterns_string = ''.join(patterns)
+ queryNGraph = '''
+ PREFIX base: <'''+conf.base+'''>
+ PREFIX schema:
+ SELECT DISTINCT *
+ WHERE {
+ GRAPH <'''+graph+'''>
+ {
+ '''+patterns_string+'''
+ }
+ }
+ '''
+ print(queryNGraph)
+ sparql = SPARQLWrapper(conf.myEndpoint)
+ sparql.setQuery(queryNGraph)
+ sparql.setReturnFormat(JSON)
+ results = sparql.query().convert()
+ print(results)
+ return results
diff --git a/static/js/main.js b/static/js/main.js
index 4911621..a809a66 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -107,9 +107,17 @@ $(document).ready(function() {
});
// search WD, VIAF, my data, vocabs, years + add URLs
- $("input[type='text']").click(function () {
+ $(".main_content").on("click", "input[type='text']", function () { // make the onclick function valid for later generated inputs
searchID = $(this).attr('id');
+ const searchresult = $('#searchresult').detach();
+ if ($(this).closest('.subform_section').length) {
+ $(this).closest('.subform_section').prepend(searchresult);
+ } else if ($(this).closest('form').length) {
+ $(this).closest('form').parent().after(searchresult);
+ }
+
+
if ( $(this).hasClass('searchWikidata') ) {
searchWD(searchID);
};
@@ -447,14 +455,14 @@ function searchGeonames(searchterm) {
format: "json",
},
function(data) {
- // autocomplete positioning
+ // autocomplete positioning;
var position = $('#'+searchterm).position();
- var leftpos = position.left+15;
+ var leftpos = $('.subform_section').length !== 0 ? position.left-35 : position.left+15;
var offset = $('#'+searchterm).offset();
var height = $('#'+searchterm).height();
- var width = $('#'+searchterm).width();
- var top = offset.top + height + "px";
- var right = offset.left + width + "px";
+ var top = $('.subform_section').length !== 0 ? offset.top - $('.subform_section').offset().top + height + "px" : offset.top + height + "px";
+ var max_width = $('.subform_section').length !== 0 ? '90%' : '600px';
+ console.log(max_width);
$('#searchresult').css( {
'position': 'absolute',
@@ -463,7 +471,7 @@ function searchGeonames(searchterm) {
'z-index':1000,
'background-color': 'white',
'border':'solid 1px grey',
- 'max-width':'600px',
+ 'max-width':max_width,
'border-radius': '4px'
});
$("#searchresult").empty();
@@ -605,14 +613,14 @@ function searchWD(searchterm) {
limit: 5,
},
function(data) {
- // autocomplete positioning
+ // autocomplete positioning;
var position = $('#'+searchterm).position();
- var leftpos = position.left+15;
+ var leftpos = $('.subform_section').length !== 0 ? position.left-35 : position.left+15;
var offset = $('#'+searchterm).offset();
var height = $('#'+searchterm).height();
- var width = $('#'+searchterm).width();
- var top = offset.top + height + "px";
- var right = offset.left + width + "px";
+ var top = $('.subform_section').length !== 0 ? offset.top - $('.subform_section').offset().top + height + "px" : offset.top + height + "px";
+ var max_width = $('.subform_section').length !== 0 ? '90%' : '600px';
+ console.log(max_width);
$('#searchresult').css( {
'position': 'absolute',
@@ -621,7 +629,7 @@ function searchWD(searchterm) {
'z-index':1000,
'background-color': 'white',
'border':'solid 1px grey',
- 'max-width':'600px',
+ 'max-width':max_width,
'border-radius': '4px'
});
$("#searchresult").empty();
@@ -1046,26 +1054,25 @@ function searchVocab(searchterm) {
$("#searchresult").show();
- var position = $('#'+searchterm).position();
- var leftpos = position.left+80;
- var offset = $('#'+searchterm).offset();
- var height = $('#'+searchterm).height();
- var width = $('#'+searchterm).width();
- var top = offset.top + height + "px";
- var right = offset.left + width + "px";
-
- $('#searchresult').css( {
- 'position': 'absolute',
- 'margin-left': leftpos+'px',
- 'top': top,
- 'z-index':1000,
- 'background-color': 'white',
- 'border':'solid 1px grey',
- 'max-width':'600px',
- 'border-radius': '4px',
- 'max-height': '300px',
- 'overflow-y': 'auto'
- });
+ // autocomplete positioning;
+ var position = $('#'+searchterm).position();
+ var leftpos = $('.subform_section').length !== 0 ? position.left-25 : position.left+80;
+ var offset = $('#'+searchterm).offset();
+ var height = $('#'+searchterm).height();
+ var top = $('.subform_section').length !== 0 ? offset.top - $('.subform_section').offset().top + height + "px" : offset.top + height + "px";
+ var max_width = $('.subform_section').length !== 0 ? '90%' : '600px';
+ console.log(max_width);
+
+ $('#searchresult').css( {
+ 'position': 'absolute',
+ 'margin-left': leftpos+'px',
+ 'top': top,
+ 'z-index':1000,
+ 'background-color': 'white',
+ 'border':'solid 1px grey',
+ 'max-width':max_width,
+ 'border-radius': '4px'
+ });
$("#searchresult").empty();
options.forEach(function(option) {
// each option (i.e., retrieved term) has this structure: URI,LABEL,VOCABULARY
@@ -1249,10 +1256,8 @@ function checkPriorRecords(elem) {
});
};
-// create subrecords
-function create_subrecord(resource_class, field_name, el) {
- // handle multiple subform
- //var subform_sections = check_subform_sections();
+// set the webpage to display a new subform
+function replace_existing_subforms() {
if ($('.subform_section').length) {
$('.subform_section').each(function () {
var right_css = parseInt($(this).css('right'));
@@ -1261,7 +1266,13 @@ function create_subrecord(resource_class, field_name, el) {
} else {
$('body').after("
");
}
+}
+// create subrecords
+function create_subrecord(resource_class, field_name, el) {
+ // handle multiple subform
+ //var subform_sections = check_subform_sections();
+ replace_existing_subforms();
const subrecord_section = $("");
const subform_id = Date.now().toString();
@@ -1274,54 +1285,52 @@ function create_subrecord(resource_class, field_name, el) {
const clone_element = $(this).parent().parent().clone();
clone_element.attr("style", "display: block");
clone_element.find('input').removeClass('original_subtemplate');
- console.log(clone_element.html())
// associate proper input_ids to input fields belonging to the subrecord form
var input_id = clone_element.find('input').attr('id');
var class_occurences = $("input[id*='"+input_id+"']").length;
- clone_element.find('input').attr('id', input_id+"-"+class_occurences.toString())
- clone_element.find('input').attr('name', input_id+"-"+class_occurences.toString())
+ clone_element.find('input').attr('id', input_id+"__"+class_occurences.toString())
+ clone_element.find('input').attr('name', input_id+"__"+class_occurences.toString())
subrecord_form.append(clone_element);
})
// save or cancel subrecord
const subrecord_buttons = $("");
const save_subrecord_btn = $("");
- const cancel_subrecord_btn = $("");
+ const cancel_subrecord_btn = $("");
+
// SAVE
- save_subrecord_btn.bind('click', function(e) {
+ save_subrecord_btn.on('click', function(e) {
// generate a tag
- if (subrecord_form.find('.disambiguate').length) {
- var tag_label = subrecord_form.find('.disambiguate').val();
- } else {
- var tag_label = field_name + "-" + ($('.tag-subrecord.'+resource_class).length + 1).toString();
- };
+ var tag_label = subrecord_form.find('.disambiguate').val() || (field_name + "-" + $(".tag-subrecord[class~='"+resource_class+"']").length + 1);
var subinputs = [];
subrecord_form.find('input:not(.btn)').each(function() {
$("#recordForm").append($(this));
$(this).hide();
- subinputs.push($(this).attr('id'));
+ if ($(this).attr('id') !== undefined) {subinputs.push($(this).attr('id'))};
});
var subrecord_index = $("[subtemplate='"+resource_class+"']").parent().parent().find('.tag-subrecord').length + 1;
- var subrecord_id = $("[subtemplate='"+resource_class+"']").attr('id') + "-" + subrecord_index;
- $(el).after("
" + tag_label + "");
+ var subrecord_id = $("[subtemplate='"+resource_class+"']").attr('id') + "__" + subrecord_index;
+ $(el).after("
" + tag_label + "");
$('#recordForm').append("");
// hide_subform
cancel_subrecord(this);
});
+ // CANCEL
+ cancel_subrecord_btn.on('click', function(e) {
+ // hide_subform
+ cancel_subrecord(this);
+ });
subrecord_buttons.append(cancel_subrecord_btn, save_subrecord_btn);
subrecord_form.append(subrecord_buttons);
-
subrecord_section.append(subrecord_form);
- $('.main_content').eq(0).prepend(subrecord_section);
-
+ $('.main_content').eq(0).prepend(subrecord_section);
}
-// CANCEL SUBRECORD
+// CANCEL SUBRECORD (before adding it to #recordForm)
function cancel_subrecord(subrecord_section) {
- console.log(subrecord_section)
if ($('.subform_section').length > 1) {
$('.subform_section').each(function () {
var right_css = parseInt($(this).css('right'));
@@ -1330,9 +1339,63 @@ function cancel_subrecord(subrecord_section) {
} else {
$('.modal-previewMM').remove();
}
+ $('main form').append($('#searchresult'));
$(subrecord_section).closest('.subform_section').remove();
};
+// DELETE or MODIFY SUBRECORD (after it has been added to #recordForm)
+function modify_subrecord(sub_id, keep) {
+ var original_subtemplate_id = sub_id.split("__")[0];
+ var original_subtemplate_class = $('#'+original_subtemplate_id).attr('subtemplate');
+ console.log(original_subtemplate_class, sub_id)
+
+ if (!keep) {
+ // remove all inputs
+ var inner_inputs = $('#'+sub_id).val().split(",");
+ delete_inner_subrecord(inner_inputs); // collect nested inputs and remove them
+ $('#'+sub_id+'-tag').next('i').remove();
+ $('#'+sub_id+'-tag').next('i').remove();
+ $('#'+sub_id+'-tag').remove();
+ $('#'+sub_id).remove();
+ }
+ else {
+ // recollect the first level nested inputs to be displayed
+ var inner_inputs = $('#'+sub_id).val().split(",");
+
+ // recreate subrecord_section
+ var field_name = $('#'+sub_id+'-tag').parent().prev().text();
+ var el = $('#'+sub_id+'-tag').prev('.fa-plus-circle');
+ create_subrecord(original_subtemplate_class, field_name, el);
+
+ for (let i=0; i