From 7321f5bf1323568400ea2de56bb0b346681bbe3e Mon Sep 17 00:00:00 2001 From: Sebastiano Giacomini <92300303+Sebastiano-G@users.noreply.github.com> Date: Tue, 2 Jul 2024 00:05:21 +0200 Subject: [PATCH] #14 Inverse relationships in View Page & Term Page UI update still in progress. View pages have been enriched with a novel section to display incoming properties related to the entity. The Term Page "Appears in" section is now organized according to Classes. Term Pages becomes available also for imported entities. --- app.py | 65 +- forms.py | 14 + mapping.py | 152 +- queries.py | 22 +- static/css/main.css | 93 +- static/js/backup js.js | 4460 +++++++++++++++++++++++++++++++++++++++ static/js/main.js | 878 ++++---- templates/modify.html | 33 +- templates/record.html | 2 +- templates/template.html | 31 +- templates/term.html | 19 +- templates/view.html | 58 +- utils.py | 25 +- 13 files changed, 5375 insertions(+), 477 deletions(-) create mode 100644 static/js/backup js.js diff --git a/app.py b/app.py index 704f293..a174f20 100644 --- a/app.py +++ b/app.py @@ -520,7 +520,7 @@ def POST(self, page): # create a new template elif actions.action.startswith('createTemplate'): - print('create template') + print('create template:', actions) is_git_auth = github_sync.is_git_auth() res_type = sorted([ urllib.parse.unquote(actions[class_input].strip()) for class_input in actions if class_input.startswith("uri_class")]) res_type = conf.main_entity if res_type == [] else res_type @@ -995,7 +995,7 @@ def GET(self, name): base = conf.base record = base+name res_class = queries.getClass(conf.base+name) - data, stage, title, properties, data_labels, extractions_data = None, None, None, None, {}, {} + data, stage, title, properties, data_labels, extractions_data, new_dict_classes, properties_sorted = None, None, None, None, {}, {}, {}, {} try: res_template = u.get_template_from_class(res_class) @@ -1019,11 +1019,46 @@ def GET(self, name): except Exception as e: pass + try: + incoming_links = queries.get_records_from_object(base+name) + class_sorted = {} + for result in incoming_links['results']['bindings']: + result_class = result['class']['value'] + result_property_uri = result['property']['value'] + result_property = result_property_uri + "," + u.get_LOV_namespace(result_property_uri) + result_subject = result['subject']['value'] + ',' + result['label']['value'] + if result_class not in class_sorted: + class_sorted[result_class] = { result_property : [result_subject] } + else: + if result_property in class_sorted[result_class]: + class_sorted[result_class][result_property].append(result_subject) + else: + class_sorted[result_class][result_property] = [result_subject] + + with open(TEMPLATE_LIST) as tpl_list: + templates = json.load(tpl_list) + for k in list(class_sorted.keys()): + template = next((t["name"], t["template"]) for t in templates if t["type"] == sorted(k.split("; "))) + template_name, template_file = template + with open(template_file) as tpl_file: + template_fields = json.load(tpl_file) + property_label = list(class_sorted[k].keys())[0] + property_name = next(f["label"] for f in template_fields if f["property"] == property_label.split(',',1)[0]) + if property_label in properties_sorted: + properties_sorted[property_label].extend(class_sorted[k][property_label]) + else: + properties_sorted[property_label] = class_sorted[k][property_label] + new_dict_classes[template_name] = {'class':k, 'results': { property_label+','+property_name : class_sorted[k][property_label]} } + except Exception as e: + pass + + return render.view(user=session['username'], graphdata=data_labels, graphID=name, title=title, stage=stage, base=base,properties=properties, - is_git_auth=is_git_auth,project=conf.myProject,knowledge_extractor=extractions_data) + is_git_auth=is_git_auth,project=conf.myProject,knowledge_extractor=extractions_data, + inverses_by_class=new_dict_classes, inverses_by_properties = properties_sorted) def POST(self,name): """ Record web page @@ -1050,15 +1085,31 @@ def GET(self, name): the ID of the term, generally the last part of the URL """ web.header("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store") - data = queries.describeTerm(name) + uri = mapping.getRightURIbase(name) + data = queries.describeTerm(uri) is_git_auth = github_sync.is_git_auth() - count = len([ result["subject"]["value"] \ + results_by_class = {} + appears_in = [ result["subject"]["value"] \ for result in data["results"]["bindings"] \ - if (name in result["object"]["value"] and result["object"]["type"] == 'uri') ]) + if (name in result["object"]["value"] and result["object"]["type"] == 'uri') ] + + + with open(TEMPLATE_LIST) as tpl_list: + res_templates = json.load(tpl_list) + for res_uri in appears_in: + res_class = sorted(queries.getClass(res_uri)) + res_type = next(t["name"] for t in res_templates if t["type"] == res_class) + if res_type in results_by_class: + results_by_class[res_type]['results'].append(res_uri) + else: + results_by_class[res_type] = {'class':res_class, 'results':[res_uri]} + + count = len(appears_in) return render.term(user=session['username'], data=data, count=count, - is_git_auth=is_git_auth,project=conf.myProject,base=conf.base,name=name) + is_git_auth=is_git_auth,project=conf.myProject,base=conf.base, + uri=uri,name=name,results=results_by_class) def POST(self,name): """ controlled vocabulary term web page diff --git a/forms.py b/forms.py index 0b9a1ae..12512b3 100644 --- a/forms.py +++ b/forms.py @@ -78,7 +78,9 @@ def get_form(json_form, from_dict=False, subtemplate=False): classes = classes+' websitePreview' if field['type'] == 'WebsitePreview' else classes classes = classes+' disabled' if 'disabled' in field and field['disabled'] == "True" else classes classes = classes+ ' ' + field['cardinality'] if 'cardinality' in field else classes + classes = classes+ ' ' + field['dataReuse'] if 'dataReuse' in field else classes autocomplete = field['cache_autocomplete'] if 'cache_autocomplete' in field and len(field['cache_autocomplete']) > 0 else '' + rdf_property = field['property'] if 'property' in field else '' mandatory = field['mandatory'] if 'mandatory' in field and field['mandatory'] == 'True' else 'False' # text box @@ -100,6 +102,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): class_= classes, value=default, lang=conf.mainLang, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class) , ) else: @@ -112,6 +115,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): class_= classes, value=default, lang=conf.mainLang, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -124,6 +128,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): pre = prepend, class_= classes, value=default, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -136,6 +141,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): pre = prepend, class_= classes, value=default, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class) , ) @@ -149,6 +155,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): class_= classes, value=default, lang=conf.mainLang, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -159,6 +166,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): id=myid, pre = prepend, class_= classes, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) elif field['calendar'] == 'Day': @@ -167,6 +175,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): id=myid, pre = prepend, class_= classes, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) elif field['calendar'] == 'Year': @@ -176,6 +185,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): pre = prepend, class_= classes, value=default, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -187,6 +197,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): id=myid, pre = prepend, class_= classes, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -200,6 +211,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): pre = prepend_title, class_= classes+' checkbox_group', checked=False, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -212,6 +224,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): pre = '', class_= classes+' checkbox_group following_checkbox', checked=False, + data_property = rdf_property, data_mandatory = mandatory, data_class=res_class), ) @@ -227,6 +240,7 @@ def get_form(json_form, from_dict=False, subtemplate=False): value=default, data_mandatory = mandatory, data_class=res_class, + data_property = rdf_property, data_subtemplate = resource_class, data_subtemplateID = field['import_subtemplate']), ) + get_form(field['import_subtemplate'], subtemplate=True) diff --git a/mapping.py b/mapping.py index c82784b..73466e5 100644 --- a/mapping.py +++ b/mapping.py @@ -34,6 +34,7 @@ server = sparql.SPARQLServer(conf.myEndpoint) dir_path = os.path.dirname(os.path.realpath(__file__)) RESOURCE_TEMPLATES = conf.resource_templates +ASK_CLASS = conf.ask_form TEMPLATE_LIST = conf.template_list def getValuesFromFields(fieldPrefix, recordData, fields=None, field_type=None): @@ -48,12 +49,9 @@ def getValuesFromFields(fieldPrefix, recordData, fields=None, field_type=None): values = value.split(',', 1) results.add(( values[0].strip(), urllib.parse.unquote(values[1]) )) # (id, label) else: - print("key:",key, fieldPrefix) if key.startswith(fieldPrefix+'_') and ',' in value: # multiple values from text box (entities) and URL values = value.split(',', 1) - print("val:",values) results.add(( values[0].strip(), urllib.parse.unquote(values[1]) )) # (id, label) - print(results) elif key == fieldPrefix: # uri from dropdown (single value from controlled vocabulary) and URL if fields: field = next(field for field in fields if field["id"] == fieldPrefix) @@ -131,10 +129,12 @@ def inputToRDF(recordData, userID, stage, graphToClear=None,tpl_form=None): for predicate_obj, inner_dict in binding.items(): if predicate_obj.endswith('_property'): predicate = URIRef(inner_dict['value']) - obj_value = binding[predicate_obj.replace("_property", "")]['value'] - obj_type = binding[predicate_obj.replace("_property", "")]['type'] - obj = URIRef(obj_value) if obj_type == "uri" else Literal(obj_value, datatype="http://www.w3.org/2001/XMLSchema#string") - label = binding[predicate_obj.replace("_property", "") + "_label"]['value'] if predicate_obj.replace("_property", "") + "_label" in binding else None + short_predicate_obj = predicate_obj.replace("_property", "") + obj_value = binding[short_predicate_obj]['value'] + obj_type = binding[short_predicate_obj]['type'] + obj_datatype = binding[short_predicate_obj]['datatype'] if 'datatype' in binding[short_predicate_obj] else '' + obj = URIRef(obj_value) if obj_type == "uri" else Literal(obj_value, datatype=obj_datatype) + label = binding[short_predicate_obj + "_label"]['value'] if predicate_obj.replace("_property", "") + "_label" in binding else None wd.add((subject, URIRef(predicate), obj)) print(subject, predicate, obj, label) if label: @@ -161,6 +161,7 @@ def inputToRDF(recordData, userID, stage, graphToClear=None,tpl_form=None): else getValuesFromFields(field['id'], recordData, field_type=field['value']) if 'value' in field and field['value'] == 'URL' \ else getLiteralValuesFromFields(field['id'], recordData) if 'value' in field and field['value'] == 'Literal' else recordData[field['id']] # TODO disambiguate as URI, value + print("VALUE:", value) if field["disambiguate"] == 'True': # use the key 'disambiguate' as title of the graph main_lang = value['mainLang'] main_value = [label for label in value['results'] if label[1] == main_lang] @@ -287,7 +288,8 @@ def inputToRDF(recordData, userID, stage, graphToClear=None,tpl_form=None): # process a new subrecord, send its data to the triplestore, and link it to the main record subrecord_id = subrecord subrecord_template = field['import_subtemplate'] - processed_subrecord = process_new_subrecord(recordData,userID,stage,subrecord_template,subrecord) + allow_data_reuse = fields if 'dataReuse' in field and field['dataReuse']=='allowDataReuse' else False + processed_subrecord = process_new_subrecord(recordData,userID,stage,subrecord_template,subrecord,supertemplate=None,allow_data_reuse=allow_data_reuse) subrecord_id, retrieved_label = processed_subrecord wd.add(( URIRef(base+graph_name), URIRef(field['property']), URIRef(base+subrecord_id) )) wd.add(( URIRef(base+subrecord_id), RDFS.label, Literal(retrieved_label, datatype="http://www.w3.org/2001/XMLSchema#string"))) @@ -315,57 +317,97 @@ def inputToRDF(recordData, userID, stage, graphToClear=None,tpl_form=None): return 'records/'+recordID+'.ttl' -def process_new_subrecord(data, userID, stage, sub_tpl, subrecord_id): +def process_new_subrecord(data, userID, stage, sub_tpl, subrecord_id, supertemplate=None, allow_data_reuse=False): # prepare a new dict to store data of subrecord-x new_record_data = {'recordID': subrecord_id,} - + label = 'No Label!' with open(sub_tpl) as fields: subtemplate = json.load(fields) + subtemplate = sorted(subtemplate, key=lambda x: x['type'] == 'subtemplate') # process the input data related to subrecord-x for subtemplate_field in subtemplate: - subfield_id = subtemplate_field['id'] - - # Subtemplate - if subtemplate_field['type'] == 'Subtemplate': - key = subfield_id+"_"+subrecord_id - # Process inner-subrecords and retrieve their ids,labels in order to provide a link to them in the upper-level subrecord - if key+"-subrecords" in data: - new_record_data[subfield_id] = [[]] - inner_subtemplate = subtemplate_field['import_subtemplate'] - for inner_subrecord in data[key+"-subrecords"].split(","): - if ";" in inner_subrecord: - processed_subrecord = inner_subrecord.split(";",1) - else: - processed_subrecord = process_new_subrecord(data,userID,stage,inner_subtemplate,inner_subrecord) - new_record_data[subfield_id][0].append(processed_subrecord) # store the id,label pair inside the subrecord dict - - # Date - elif subtemplate_field['type'] == 'Date': - key = subtemplate_field['id']+"_"+subrecord_id - new_record_data[subtemplate_field['id']] = data[key] - - # Knowledge Extraction - elif subtemplate_field['type'] == 'KnowledgeExtractor' and 'extractions-dict' in data: - new_record_data['extractions-dict'] = data['extractions-dict'] - for keyword_key,keyword_value in data.items(): - print("FFF", subrecord_id) - if keyword_key.startswith('keyword_'+subrecord_id): - print("QQQQ",keyword_key,keyword_value) - new_record_data[keyword_key]=keyword_value - - # Multiple values fields: Literals or URI - elif 'value' in subtemplate_field and (subtemplate_field['value'] == 'Literal' or subtemplate_field['value'] in ['URI','URL','Place']): - keys = [input_id for input_id in data.keys() if input_id.startswith(subtemplate_field['id']+"_") and input_id.endswith("_"+subrecord_id)] - for key in keys: - shortened_key = key.rsplit("_",1)[0] - new_record_data[shortened_key] = data[key] - - # Label: disambiguate field - if subtemplate_field['disambiguate'] == "True": - main_lang_input_field = subfield_id+'_mainLang_'+subrecord_id - main_lang = data[main_lang_input_field] if main_lang_input_field in data else "No main lang" - label_input_field = subfield_id+"_"+main_lang+"_"+subrecord_id - label = data[label_input_field] if label_input_field in data else "No label" + if subtemplate_field['hidden'] == 'False': + subfield_id = subtemplate_field['id'] + rdf_property = subtemplate_field['property'] + + # Subtemplate + if subtemplate_field['type'] == 'Subtemplate': + key = subfield_id+"_"+subrecord_id + # Process inner-subrecords and retrieve their ids,labels in order to provide a link to them in the upper-level subrecord + if key+"-subrecords" in data: + new_record_data[subfield_id] = [[]] + inner_subtemplate = subtemplate_field['import_subtemplate'] + data_reuse = subtemplate if 'dataReuse' in subtemplate_field and subtemplate_field['dataReuse']=='allowDataReuse' else False + for inner_subrecord in data[key+"-subrecords"].split(","): + if ";" in inner_subrecord: + processed_subrecord = inner_subrecord.split(";",1) + else: + processed_subrecord = process_new_subrecord(data,userID,stage,inner_subtemplate,inner_subrecord,subrecord_id,data_reuse) + new_record_data[subfield_id][0].append(processed_subrecord) # store the id,label pair inside the subrecord dict + + # Date + elif subtemplate_field['type'] == 'Date': + key = subtemplate_field['id']+"_"+subrecord_id + if key in data: + new_record_data[subtemplate_field['id']] = data[key] + elif allow_data_reuse: + upper_level_field_ids = [field['id'] for field in allow_data_reuse if field['property'] == rdf_property and field['type'] == subtemplate_field['type']] + if len(upper_level_field_id) > 0: + upper_level_field_id = upper_level_field_ids[0] + look_for_id = upper_level_field_id+'_'+supertemplate if supertemplate else upper_level_field_id + new_record_data[subtemplate_field['id']] = data[look_for_id] + data[key] = data[look_for_id] + + # Knowledge Extraction + elif subtemplate_field['type'] == 'KnowledgeExtractor' and 'extractions-dict' in data: + new_record_data['extractions-dict'] = data['extractions-dict'] + for keyword_key,keyword_value in data.items(): + if keyword_key.startswith('keyword_'+subrecord_id): + new_record_data[keyword_key]=keyword_value + + # Multiple values fields: Literals or URI + elif 'value' in subtemplate_field and (subtemplate_field['value'] == 'Literal' or subtemplate_field['value'] in ['URI','URL','Place']): + keys = [input_id for input_id in data.keys() if input_id.startswith(subtemplate_field['id']+"_") and input_id.endswith("_"+subrecord_id)] + if len(keys) > 0: + for key in keys: + shortened_key = key.rsplit("_",1)[0] + new_record_data[shortened_key] = data[key] + + # Label: disambiguate field + if subtemplate_field['disambiguate'] == "True": + main_lang_input_field = subfield_id+'_mainLang_'+subrecord_id + main_lang = data[main_lang_input_field] if main_lang_input_field in data else "No main lang" + label_input_field = subfield_id+"_"+main_lang+"_"+subrecord_id + label = data[label_input_field] if label_input_field in data else "No label" + elif allow_data_reuse: + upper_level_field_ids = [field['id'] for field in allow_data_reuse if field['property'] == rdf_property and field['type'] == subtemplate_field['type']] + if len(upper_level_field_ids) > 0: + upper_level_field_id = upper_level_field_ids[0] + if supertemplate: + keys = [input_id for input_id in data.keys() if input_id.startswith(upper_level_field_id+"_") and input_id.endswith("_"+supertemplate)] + else: + keys = [input_id for input_id in data.keys() if input_id.startswith(upper_level_field_id+"_")] + for key in keys: + cut_number = 1 if supertemplate else 0 + shortened_key = key.rsplit("_",cut_number)[0] + shortened_key = key.split("_")[1] + shortened_key = subfield_id+'_'+shortened_key + new_record_data[shortened_key] = data[key] + data[shortened_key+'_'+subrecord_id] = data[key] + + + # Label: disambiguate field + if subtemplate_field['disambiguate'] == "True": + main_lang_input_field = subfield_id+'_mainLang_'+subrecord_id + main_lang = data[main_lang_input_field] if main_lang_input_field in data else "No main lang" + label_input_field = upper_level_field_id+"_"+main_lang+"_"+ supertemplate if supertemplate else upper_level_field_id+'_'+main_lang + with open(TEMPLATE_LIST,'r') as tpl_file: + tpl_list = json.load(tpl_file) + disambiguation_label = [t['name'] for t in tpl_list if t['template']==sub_tpl][0] + label = data[label_input_field] + " - " + disambiguation_label if label_input_field in data else "No label" + new_label_input_id = subfield_id + '_' + main_lang + new_record_data[new_label_input_id] = label + @@ -377,7 +419,7 @@ def process_new_subrecord(data, userID, stage, sub_tpl, subrecord_id): result = [subrecord_id,label] return result -def find_label(tpl): +""" def find_label(tpl): # Retrieve the field associated with the Primary Key (i.e., the label) of the Record with open(tpl) as tpl_file: tpl_fields = json.load(tpl_file) @@ -385,4 +427,4 @@ def find_label(tpl): label_field_id = fields_id[0] if fields_id != [] else False # TODO: add a mechanism to handle potential Templates without a Primary Key in case it's needed - return label_field_id \ No newline at end of file + return label_field_id """ \ No newline at end of file diff --git a/queries.py b/queries.py index 859f23b..e0e3734 100644 --- a/queries.py +++ b/queries.py @@ -326,10 +326,11 @@ def disambiguate_pattern(properties,fields,k): def describeTerm(name): """ ask if the resource exists, then describe it.""" - ask = """ASK { ?s ?p <""" +conf.base+name+ """> .}""" + ask = """ASK { ?s ?p <""" +name+ """> .}""" results = hello_blazegraph(ask) if results["boolean"] == True: # new entity - describe = """DESCRIBE <"""+conf.base+name+ """>""" + describe = """DESCRIBE <"""+name+ """>""" + print("DESCRIBE", describe, hello_blazegraph(describe)) return hello_blazegraph(describe) else: # vocab term ask = """ASK { ?s ?p ?o . @@ -537,4 +538,21 @@ def saveHiddenTriples(graph, tpl): sparql.setQuery(queryNGraph) sparql.setReturnFormat(JSON) results = sparql.query().convert() + print(results) return results + +def get_records_from_object(graph_uri): + query = """ + SELECT DISTINCT ?subject ?property ?class ?label + WHERE { + ?subject ?property <"""+graph_uri+""">. + ?subject rdfs:label ?label . + ?subject a ?class . + } + """ + sparql = SPARQLWrapper(conf.myEndpoint) + sparql.setQuery(query) + sparql.setReturnFormat(JSON) + results = sparql.query().convert() + print(results) + return results \ No newline at end of file diff --git a/static/css/main.css b/static/css/main.css index fa3238b..d91a70c 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -184,6 +184,12 @@ button.active { border-color: #F5F6FA #F5F6FA rgba(100, 23, 180, 1); } +.nav-tabs a.dark-nav.nav-link.active { + text-decoration: none !important; + background-color: #f5f6fa3a !important; + border-color: rgba(100, 23, 180, 1); +} + .logo_header { display: inline-block; padding: 5px; @@ -518,11 +524,11 @@ article p { padding: 4em !important; } -#recordForm .homeheading:nth-child(2) { +.corners.row .homeheading:nth-child(2) { padding-right: 0.5em !important; } -#recordForm .homeheading:nth-child(3) { +.corners.row .homeheading:nth-child(3) { padding-top: 0.35em !important; padding-left: 0.5em !important; } @@ -735,7 +741,7 @@ th { .label .title { flex: 1; - font-size: 1.2em; + font-size: 1em; } .label img, .label i { @@ -1109,6 +1115,9 @@ table.extraction-table { margin-top: 2em; margin-bottom: -2.5em; } +table.extraction-table + span + .block_field { + margin-top: 3.5em; +} table.url-table th, table.url-table td, table.extraction-table th, table.extraction-table td { border: 1px solid black; @@ -1152,6 +1161,11 @@ table.url-table i, table.extraction-table i { text-transform: uppercase; color: grey; font-size: 0.9em !important; + display: inline-block; +} + +.subtemplateField:last-child { + margin-bottom: 2em; } /*display: inline !important;*/ @@ -1254,10 +1268,16 @@ table.url-table i, table.extraction-table i { transition-duration: 0.4s; } -.relatedResources { - margin: 20px 0px; +.related-resources-container .articleSubtitle { + margin: 2em 0 0 0; + display: inline-block; +} +.related-resources-container button { + display: inline-block; + margin-bottom: 0 !important; } + /* historian/collection template */ /*.info-item {font-style: italic;}*/ .sideBoxes { @@ -1265,6 +1285,52 @@ table.url-table i, table.extraction-table i { margin-bottom: 3em; } +.inverse-subtitle { + padding-left: 1em; + padding-top: 1em; + text-transform: uppercase; + font-weight: 600; + color: rgb(100, 23, 180); + display: inline-block; +} + +.inverse-results-container { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 20px; + border-bottom: 2px solid rgba(100, 23, 180, 0.12); + padding-left: 1em; +} + +.inverse-property { + display: flex; + flex-direction: column; + justify-content: center; + padding: 10px 30px 10px 0; +} + +.inverse-property:first-of-type { + width: 20% +} + +.inverse-property span { + font-weight: 400; + color: #333; + +} + +.inverses { + margin: 3em 0 -1em 0; + max-height: 1000px; + background-color: silver; +} + +.inverses .section-subtitle { + font-weight: 400; + color: grey; +} + .info-bio { margin-top: 2em; } @@ -1290,6 +1356,13 @@ table.url-table i, table.extraction-table i { font-size: 32px; } +.biblio-navigate { + font-weight: 500; + text-decoration: underline; + color:#333; + cursor:pointer; +} + .bibliospace { padding-top: 25px !important; padding-bottom: 10px !important; @@ -2528,7 +2601,11 @@ button#showTemplates { overflow: hidden; transition: max-height 0.3s ease, padding 0.3s ease, margin-top 0.3s ease, margin-bottom 0.3s ease; /* Corretta sintassi per la transizione */ padding: 1em; - max-height: 1000px; + max-height: 10000px; +} + +.subform:first-child { + margin-top: 1.5em; } .subform.closed { @@ -2548,6 +2625,10 @@ button#showTemplates { align-items: center; } +.subform .title { + font-size: 0.8em; +} + .subform-buttons { display: flex; } diff --git a/static/js/backup js.js b/static/js/backup js.js new file mode 100644 index 0000000..cab52d5 --- /dev/null +++ b/static/js/backup js.js @@ -0,0 +1,4460 @@ + +if (graph.length) {var inGraph = "FROM <"+graph+">"} else {var inGraph = ""} +const wdImg = '' +const wdImgIcon = '' +const geoImg = ''; +const viafImg = ''; +const viafImgIcon = '' +const wikidataEndpoint = "https://query.wikidata.org/sparql" +$(document).ready(function() { + + // loader + $(".se-pre-con").fadeOut("slow"); + + // disable submit form when pressing return + $("input[type='text'], input[type='textarea']").on('keyup keypress', function(e) { + var keyCode = e.keyCode || e.which; + if (keyCode === 13) { + e.preventDefault(); + return false; + } + }); + + // create a new TEMPLATE + $("#selectTemplateClassButton").on('click', function() { + // show modal + $("#selectTemplateClassModal").toggleClass('open-modal'); + $('body').append($("