Coverage for src/api/metaapi.py: 100%
74 statements
« prev ^ index » next coverage.py v7.10.0, created at 2026-04-03 13:54 +0000
« prev ^ index » next coverage.py v7.10.0, created at 2026-04-03 13:54 +0000
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2022, Arcangelo Massari <arcangelo.massari@unibo.it>
4#
5# Permission to use, copy, modify, and/or distribute this software for any purpose
6# with or without fee is hereby granted, provided that the above copyright notice
7# and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
12# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
13# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15# SOFTWARE.
17__author__ = 'Arcangelo Massari'
19from typing import Tuple
21URI_TYPE_DICT = {
22 'http://purl.org/spar/doco/Abstract': 'abstract',
23 'http://purl.org/spar/fabio/ArchivalDocument': 'archival document',
24 'http://purl.org/spar/fabio/AudioDocument': 'audio document',
25 'http://purl.org/spar/fabio/Book': 'book',
26 'http://purl.org/spar/fabio/BookChapter': 'book chapter',
27 'http://purl.org/spar/fabio/ExpressionCollection': 'book section',
28 'http://purl.org/spar/fabio/BookSeries': 'book series',
29 'http://purl.org/spar/fabio/BookSet': 'book set',
30 'http://purl.org/spar/fabio/ComputerProgram': 'computer program',
31 'http://purl.org/spar/doco/Part': 'book part',
32 'http://purl.org/spar/fabio/Expression': '',
33 'http://purl.org/spar/fabio/DataFile': 'dataset',
34 'http://purl.org/spar/fabio/DataManagementPlan': 'data management plan',
35 'http://purl.org/spar/fabio/Thesis': 'dissertation',
36 'http://purl.org/spar/fabio/Editorial': 'editorial',
37 'http://purl.org/spar/fabio/Journal': 'journal',
38 'http://purl.org/spar/fabio/JournalArticle': 'journal article',
39 'http://purl.org/spar/fabio/JournalEditorial': 'journal editorial',
40 'http://purl.org/spar/fabio/JournalIssue': 'journal issue',
41 'http://purl.org/spar/fabio/JournalVolume': 'journal volume',
42 'http://purl.org/spar/fabio/Newspaper': 'newspaper',
43 'http://purl.org/spar/fabio/NewspaperArticle': 'newspaper article',
44 'http://purl.org/spar/fabio/NewspaperIssue': 'newspaper issue',
45 'http://purl.org/spar/fr/ReviewVersion': 'peer review',
46 'http://purl.org/spar/fabio/AcademicProceedings': 'proceedings',
47 'http://purl.org/spar/fabio/Preprint': 'preprint',
48 'http://purl.org/spar/fabio/Presentation': 'presentation',
49 'http://purl.org/spar/fabio/ProceedingsPaper': 'proceedings article',
50 'http://purl.org/spar/fabio/ReferenceBook': 'reference book',
51 'http://purl.org/spar/fabio/ReferenceEntry': 'reference entry',
52 'http://purl.org/spar/fabio/ReportDocument': 'report',
53 'http://purl.org/spar/fabio/RetractionNotice': 'retraction notice',
54 'http://purl.org/spar/fabio/Series': 'series',
55 'http://purl.org/spar/fabio/SpecificationDocument': 'standard',
56 'http://purl.org/spar/fabio/WebContent': 'web content'
57}
60def generate_id_search(ids: str) -> Tuple[str]:
61 id_searches = list()
62 omid_values = []
63 other_values = []
65 for identifier in ids.split('__'):
66 scheme_literal_value = identifier.split(':', maxsplit=1)
67 scheme = scheme_literal_value[0].lower()
68 literal_value = scheme_literal_value[1]
69 literal_value = literal_value.lower() if scheme == 'doi' else literal_value
70 if scheme == 'omid':
71 omid_values.append("{{ BIND(<https://w3id.org/oc/meta/"+literal_value+"> AS ?res) }}")
72 elif scheme in {'doi', 'issn', 'isbn', 'openalex', 'pmid', 'pmcid', 'url', 'wikidata', 'wikipedia'}:
73 other_values.append('''
74 {{
75 {
76 ?identifier literal:hasLiteralValue "'''+literal_value+'''"
77 }
78 UNION
79 {
80 ?identifier literal:hasLiteralValue "'''+literal_value+'''"^^<http://www.w3.org/2001/XMLSchema#string>
81 }
82 ?identifier datacite:usesIdentifierScheme datacite:'''+scheme+''';
83 ^datacite:hasIdentifier ?res.
84 ?res a fabio:Expression.
85 }}
86 ''')
88 if omid_values:
89 id_searches.append("?res a fabio:Expression."+" UNION ".join(omid_values))
91 if other_values:
92 id_searches.append(" UNION ".join(other_values))
94 ids_search = " UNION ".join(id_searches)
95 return ids_search,
97def generate_ra_search(identifier:str) -> Tuple[str]:
98 scheme_literal_value = identifier.split(':')
99 if len(scheme_literal_value) == 2:
100 scheme = scheme_literal_value[0]
101 literal_value = scheme_literal_value[1]
102 else:
103 scheme = 'orcid'
104 literal_value = scheme_literal_value[0]
105 if scheme == 'omid':
106 return '<https://w3id.org/oc/meta/{0}> ^pro:isHeldBy ?knownRole.'.format(literal_value),
107 else:
108 return '''
109 {
110 ?knownPersonIdentifier literal:hasLiteralValue "'''+literal_value+'''"
111 }
112 UNION
113 {
114 ?knownPersonIdentifier literal:hasLiteralValue "'''+literal_value+'''"^^<http://www.w3.org/2001/XMLSchema#string>
115 }
116 ?knownPersonIdentifier datacite:usesIdentifierScheme datacite:'''+scheme+''';
117 ^datacite:hasIdentifier ?knownPerson.
118 ?knownPerson ^pro:isHeldBy ?knownRole.
119 ''',
121def create_metadata_output(results):
122 header = results[0]
123 output_results = [header]
124 for result in results[1:]:
125 output_result = list()
126 for i, data in enumerate(result):
127 if i == header.index('type'):
128 beautiful_type = __postprocess_type(data[1])
129 output_result.append((data[0], beautiful_type))
130 elif i == header.index('author') or i == header.index('editor') or i == header.index('publisher'):
131 ordered_list = process_ordered_list(data[1])
132 output_result.append((data[0], ordered_list))
133 else:
134 output_result.append(data)
135 output_results.append(output_result)
136 return output_results, True
138def __postprocess_type(type_uri:str) -> str:
139 if type_uri:
140 type_string = URI_TYPE_DICT[type_uri]
141 else:
142 type_string = ''
143 return type_string
145def process_ordered_list(items):
146 if not items:
147 return items
148 items_dict = {}
149 role_to_name = {}
150 l_author = [item for item in items.split('|') if item is not None and item != ""]
151 if len(l_author) == 0:
152 return ""
153 for item in l_author:
154 parts = item.split(':')
155 name = ':'.join(parts[:-2])
156 current_role = parts[-2]
157 next_role = parts[-1] if parts[-1] != '' else None
158 items_dict[current_role] = next_role
159 role_to_name[current_role] = name
161 ordered_items = []
162 start_role = next(iter(role for role, next_role in items_dict.items() if not role in items_dict.values()))
164 current_role = start_role
165 while current_role:
166 ordered_items.append(role_to_name[current_role])
167 current_role = items_dict.get(current_role, '')
169 return "; ".join(ordered_items)