Project

General

Profile

1
package eu.dnetlib.openaire.directindex.objects;
2

    
3
import java.text.SimpleDateFormat;
4
import java.util.*;
5
import java.util.concurrent.TimeUnit;
6
import java.util.stream.Collector;
7
import java.util.stream.Collectors;
8

    
9
import org.apache.commons.lang.StringUtils;
10
import org.apache.commons.logging.Log;
11
import org.apache.commons.logging.LogFactory;
12
import org.apache.velocity.app.VelocityEngine;
13
import org.springframework.ui.velocity.VelocityEngineUtils;
14

    
15
import com.google.gson.Gson;
16

    
17
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
18
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
19
import eu.dnetlib.miscutils.datetime.DateUtils;
20
import eu.dnetlib.miscutils.functional.hash.Hashing;
21
import eu.dnetlib.miscutils.functional.string.EscapeXml;
22
import eu.dnetlib.openaire.directindex.api.DirecIndexApiException;
23
import eu.dnetlib.openaire.directindex.api.OpenAIRESubmitterUtils;
24
import io.swagger.annotations.ApiModelProperty;
25
import org.springframework.web.client.RestTemplate;
26

    
27
/**
28
 * Created by michele on 02/12/15.
29
 */
30
public class ResultEntry {
31

    
32

    
33
	private String openaireId;
34
	private String originalId;
35
	private String title;
36
	private List<String> authors = new ArrayList<>();
37
	private String publisher;
38
	private String description;
39
	private String language;
40
	private List<PidEntry> pids = new ArrayList<>();
41
	/**
42
	 * @Deprecated: use accessRightCode
43
	 */
44
	@Deprecated
45
	private String licenseCode;
46
	private String accessRightCode;
47
	private String embargoEndDate;
48
	/**
49
	 * One of publication, dataset, software, other. Default value is publication.
50
	 */
51
	private String type = "publication";
52
	private String resourceType;
53
	private String url;
54
	private String collectedFromId;
55
	private String hostedById;
56

    
57
	// String according to the EGI context profile, example: egi::classification::natsc::math
58
	private List<String> contexts = new ArrayList<>();
59

    
60
	// String according to openaire guidelines:
61
	// info:eu-repo/grantAgreement/Funder/FundingProgram/ProjectID/[Jurisdiction]/[ProjectName]/[ProjectAcronym]
62
	private List<String> linksToProjects = new ArrayList<>();
63

    
64
	private static long last_cache_update = 0;
65
	private static final Map<String, Map<String, String>> cached_vocabularies = new HashMap<>();
66
	private static final Map<String, DatasourceEntry> cached_datasources = new HashMap<>();
67
	private static final Map<String, String> cached_contexts = new HashMap<>();
68

    
69
	private static final Log log = LogFactory.getLog(ResultEntry.class);
70

    
71
	public ResultEntry() {}
72

    
73
	public String getOpenaireId() {
74
		return openaireId;
75
	}
76

    
77
	public void setOpenaireId(final String openaireId) {
78
		this.openaireId = openaireId;
79
	}
80

    
81
	public String getOriginalId() {
82
		return originalId;
83
	}
84

    
85
	public void setOriginalId(final String originalId) {
86
		this.originalId = originalId;
87
	}
88

    
89
	@ApiModelProperty(required = true)
90
	public String getTitle() {
91
		return title;
92
	}
93

    
94
	public void setTitle(final String title) {
95
		this.title = title;
96
	}
97

    
98
	public List<String> getAuthors() {
99
		return authors;
100
	}
101

    
102
	public void setAuthors(final List<String> authors) {
103
		this.authors = authors;
104
	}
105

    
106
	public String getPublisher() {
107
		return publisher;
108
	}
109

    
110
	public void setPublisher(final String publisher) {
111
		this.publisher = publisher;
112
	}
113

    
114
	public String getDescription() {
115
		return description;
116
	}
117

    
118
	public void setDescription(final String description) {
119
		this.description = description;
120
	}
121

    
122
	@ApiModelProperty(value = "ISO Alpha-3 code. E.g. 'eng', 'ita'")
123
	public String getLanguage() {
124
		return language;
125
	}
126

    
127
	public void setLanguage(final String language) {
128
		this.language = language;
129
	}
130

    
131
	public List<PidEntry> getPids() {
132
		return pids;
133
	}
134

    
135
	public void setPids(final List<PidEntry> pids) {
136
		this.pids = pids;
137
	}
138

    
139
	@Deprecated
140
	@ApiModelProperty(required = false, allowableValues = "OPEN, CLOSED, RESTRICTED, EMBARGO, UNKNOWN, OTHER, OPEN SOURCE")
141
	public String getLicenseCode() {
142
		return licenseCode;
143
	}
144
	@Deprecated
145
	public void setLicenseCode(final String licenseCode) {
146
		this.licenseCode = licenseCode;
147
	}
148

    
149
	/**
150
	 * Set required = true when the deprecated licenseCode is not used anymore by our client and it is removed
151
	 * @return access rights code
152
	 */
153
	@ApiModelProperty(required = false, allowableValues = "OPEN, CLOSED, RESTRICTED, EMBARGO, UNKNOWN, OTHER, OPEN SOURCE")
154
	public String getAccessRightCode() {
155
		return accessRightCode;
156
	}
157

    
158
	public void setAccessRightCode(final String accessRightCode) {
159
		this.accessRightCode = accessRightCode;
160
	}
161

    
162
	@ApiModelProperty(required = true, value = "Use 001 for articles, 021 for datasets, 0029 for software. See: http://api.openaire.eu/vocabularies/dnet:publication_resource for all the available resource types.")
163
	public String getResourceType() {
164
		return resourceType;
165
	}
166

    
167
	public void setResourceType(final String resourceType) {
168
		this.resourceType = resourceType;
169
	}
170

    
171
	@ApiModelProperty(required = true)
172
	public String getUrl() {
173
		return url;
174
	}
175

    
176
	public void setUrl(final String url) {
177
		this.url = url;
178
	}
179

    
180
	@ApiModelProperty(required = true, value = "Use opendoar___::2659 for Zenodo Publications; re3data_____::r3d100010468 for Zenodo datasets; infrastruct::openaire for OpenAIRE portal.")
181
	public String getCollectedFromId() {
182
		return collectedFromId;
183
	}
184

    
185
	public void setCollectedFromId(final String collectedFromId) {
186
		this.collectedFromId = collectedFromId;
187
	}
188

    
189
	public String getHostedById() {
190
		return hostedById;
191
	}
192

    
193
	public void setHostedById(final String hostedById) {
194
		this.hostedById = hostedById;
195
	}
196

    
197
	@ApiModelProperty(value = "E.g. fet, egi::classification::natsc::math::pure, egi::projects::EMI")
198
	public List<String> getContexts() {
199
		return contexts;
200
	}
201

    
202
	public void setContexts(final List<String> contexts) {
203
		this.contexts = contexts;
204
	}
205

    
206

    
207

    
208
	@ApiModelProperty(value = "E.g. info:eu-repo/grantAgreement/EC/FP7/283595/EU//OpenAIREplus")
209
	public List<String> getLinksToProjects() {
210
		return linksToProjects;
211
	}
212

    
213
	public void setLinksToProjects(final List<String> linksToProjects) {
214
		this.linksToProjects = linksToProjects;
215
	}
216

    
217
	@ApiModelProperty(allowableValues = "publication, dataset, software, other")
218
	public String getType() {
219
		return type;
220
	}
221

    
222
	public void setType(final String type) {
223
		this.type = type;
224
	}
225

    
226
	public String getEmbargoEndDate() {
227
		return embargoEndDate;
228
	}
229

    
230
	public void setEmbargoEndDate(final String embargoEndDate) {
231
		this.embargoEndDate = embargoEndDate;
232
	}
233

    
234
	public String asOafRecord(final VelocityEngine ve,
235
			final ISLookUpService lookupService,
236
			final String oafSchemaLocation) throws Exception {
237

    
238
		if (StringUtils.isBlank(getOriginalId())
239
				&& StringUtils
240
						.isBlank(getOpenaireId())) { throw new DirecIndexApiException("One of the following fields is required: originalId or openaireId"); }
241
		if (StringUtils.isBlank(getTitle())) { throw new DirecIndexApiException("A required field is missing: title"); }
242
		if (StringUtils.isBlank(getUrl())) { throw new DirecIndexApiException("A required field is missing: url"); }
243
		if (StringUtils.isBlank(getLicenseCode()) && StringUtils.isBlank(getAccessRightCode())) { throw new DirecIndexApiException("A required field is missing: accessRightCode"); }
244
		if (StringUtils.isBlank(getResourceType())) { throw new DirecIndexApiException("A required field is missing: resourceType"); }
245
		if (StringUtils.isBlank(getCollectedFromId())) { throw new DirecIndexApiException("A required field is missing: collectedFromId"); }
246
		if (StringUtils.isBlank(getType())) { throw new DirecIndexApiException("A required field is missing: type"); }
247

    
248
		final DatasourceEntry collectedFromEntry = getDatasourceInfo(collectedFromId, lookupService);
249
		final DatasourceEntry hostedByEntry = getDatasourceInfo(hostedById, lookupService);
250

    
251
		if (StringUtils.isBlank(openaireId)) {
252
			setOpenaireId(calculateOpenaireId(originalId, collectedFromEntry));
253
		}
254

    
255
		if (!openaireId
256
				.matches("^\\w{12}::\\w{32}$")) { throw new DirecIndexApiException(
257
						"Invalid openaireId: " + openaireId + " - regex ^\\w{12}::\\w{32}$ not matched"); }
258

    
259
		final Map<String, Object> model = new HashMap<>();
260
		model.put("esc", new EscapeXml());
261
		model.put("util", new OpenAIRESubmitterUtils());
262
		model.put("pub", this);
263
		model.put("objIdentifier", getOpenaireId());
264
		model.put("oafSchemaLocation", oafSchemaLocation);
265
		model.put("resultTypes", getVocabulary("dnet:result_typologies", lookupService));
266
		model.put("rights", getVocabulary("dnet:access_modes", lookupService));
267
		model.put("resourceTypes", getVocabulary("dnet:publication_resource", lookupService));
268
		model.put("pidTypes", getVocabulary("dnet:pid_types", lookupService));
269
		model.put("languages", getVocabulary("dnet:languages", lookupService));
270
		model.put("contexts", getContexts(lookupService));
271
		model.put("dateOfCollection", new SimpleDateFormat("yyyy-MM-dd\'T\'hh:mm:ss\'Z\'").format(new Date()));
272
		model.put("collectedFrom", collectedFromEntry);
273
		model.put("hostedBy", hostedByEntry);
274

    
275
		return VelocityEngineUtils.mergeTemplateIntoString(ve, "/eu/dnetlib/openaire/directindex/indexRecord.xml.vm", "UTF-8", model);
276
	}
277

    
278
	private static String calculateOpenaireId(final String originalId, final DatasourceEntry collectedFromEntry) {
279
		return collectedFromEntry.getPrefix() + "::" + Hashing.md5(originalId);
280
	}
281

    
282
	public static String calculateOpenaireId(final String originalId, final String collectedFromId, final ISLookUpService lookupService)
283
			throws ISLookUpException {
284
		return calculateOpenaireId(originalId, getDatasourceInfo(collectedFromId, lookupService));
285
	}
286

    
287
	private synchronized static DatasourceEntry getDatasourceInfo(final String dsId, final ISLookUpService lookupService) throws ISLookUpException {
288
		if (StringUtils
289
				.isBlank(dsId)) { return new DatasourceEntry("openaire____::1256f046-bf1f-4afc-8b47-d0b147148b18", "Unknown Repository", "unknown_____"); }
290

    
291
		if (!cached_datasources.containsKey(dsId)) {
292
			final String query =
293
					"collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//CONFIGURATION[./DATASOURCE_ORIGINAL_ID='" + dsId
294
							+ "']/concat(./OFFICIAL_NAME, ' @@@ ', .//FIELD/value[../key='NamespacePrefix'])";
295
			final String s = lookupService.getResourceProfileByQuery(query);
296
			final String[] arr = s.split("@@@");
297

    
298
			final DatasourceEntry ds = new DatasourceEntry(dsId, arr[0].trim(), arr[1].trim());
299

    
300
			if (StringUtils.isBlank(ds.getName()) || StringUtils.isBlank(ds.getPrefix())) {
301
				log.error("Invalid datasource id: " + dsId);
302
				throw new ISLookUpException("Invalid datasource id: " + dsId);
303
			} else {
304
				cached_datasources.put(dsId, ds);
305
			}
306
		}
307

    
308
		return cached_datasources.get(dsId);
309

    
310
	}
311

    
312
	private synchronized static Map<String, String> getVocabulary(final String voc, final ISLookUpService lookupService) throws ISLookUpException {
313

    
314
		if (DateUtils.now() - last_cache_update < TimeUnit.MINUTES.toMillis(15) && cached_vocabularies.containsKey(voc)) {
315
			return cached_vocabularies.get(voc);
316
		} else {
317
			final String query = "collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')[.//VOCABULARY_NAME/@code='" + voc
318
					+ "']//TERM/concat(@code, ' @@@ ', @english_name)";
319

    
320
			final Map<String, String> map = new HashMap<>();
321
			for (final String s : lookupService.quickSearchProfile(query)) {
322
				final String[] arr = s.split("@@@");
323
				map.put(arr[0].trim(), arr[1].trim());
324
			}
325

    
326
			cached_vocabularies.put(voc, map);
327

    
328
			last_cache_update = DateUtils.now();
329

    
330
			return map;
331
		}
332
	}
333

    
334
	private synchronized static Map<String, String> getContexts(final ISLookUpService lookupService) throws ISLookUpException {
335
		if (DateUtils.now() - last_cache_update > TimeUnit.MINUTES.toMillis(15) || cached_contexts.isEmpty()) {
336
			final String query =
337
					"collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')[.//context/@type='community' or .//context/@type='ri']//*[name()='context' or name()='category' or name()='concept']/concat(@id, ' @@@ ', @label)";
338

    
339
			cached_contexts.clear();
340
			for (final String s : lookupService.quickSearchProfile(query)) {
341
				final String[] arr = s.split("@@@");
342
				cached_contexts.put(arr[0].trim(), arr[1].trim());
343
			}
344
			last_cache_update = DateUtils.now();
345
		}
346
		return cached_contexts;
347
	}
348

    
349
	@Override
350
	public String toString() {
351
		return new Gson().toJson(this);
352
	}
353

    
354
	public String getAnyId() {
355
		return StringUtils.isNotBlank(openaireId) ? openaireId : originalId;
356
	}
357
}
(3-3/4)