From 9d44418d381b14ab7a6301f1979b2a3af5efbd2f Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Thu, 14 Sep 2023 18:43:25 +0300 Subject: [PATCH 01/16] Add collecting software code repository URLs --- dhp-workflows/dhp-swh/pom.xml | 104 +++++++++ .../swh/CollectSoftwareRepositoryURLs.java | 211 ++++++++++++++++++ .../dhp/swh/models/LastVisitResponse.java | 40 ++++ .../eu/dnetlib/dhp/swh/input_parameters.json | 26 +++ .../eu/dnetlib/dhp/swh/job.properties | 25 +++ .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 101 +++++++++ dhp-workflows/pom.xml | 1 + 7 files changed, 508 insertions(+) create mode 100644 dhp-workflows/dhp-swh/pom.xml create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml diff --git a/dhp-workflows/dhp-swh/pom.xml b/dhp-workflows/dhp-swh/pom.xml new file mode 100644 index 000000000..501b2aef8 --- /dev/null +++ b/dhp-workflows/dhp-swh/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + eu.dnetlib.dhp + dhp-workflows + 1.2.5-SNAPSHOT + + dhp-swh + + + + org.apache.spark + spark-core_${scala.binary.version} + + + + org.apache.spark + spark-sql_${scala.binary.version} + + + + eu.dnetlib.dhp + dhp-common + ${project.version} + + + net.sf.saxon + Saxon-HE + + + + + + dom4j + dom4j + + + + xml-apis + xml-apis + + + + jaxen + jaxen + + + + org.apache.hadoop + hadoop-distcp + + + + eu.dnetlib + dnet-actionmanager-api + + + eu.dnetlib + dnet-actionmanager-common + + + eu.dnetlib + dnet-openaireplus-mapping-utils + + + saxonica + saxon + + + saxonica + saxon-dom + + + jgrapht + jgrapht + + + net.sf.ehcache + ehcache + + + org.springframework + spring-test + + + org.apache.* + * + + + apache + * + + + + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + + diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java new file mode 100644 index 000000000..c91f2bb8c --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java @@ -0,0 +1,211 @@ + +package eu.dnetlib.dhp.swh; + +import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; +import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.apache.spark.SparkConf; +import org.apache.spark.api.java.function.FlatMapFunction; +import org.apache.spark.sql.*; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.types.DataTypes; +import org.apache.spark.sql.types.StructType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.schema.common.ModelConstants; +import eu.dnetlib.dhp.schema.oaf.*; + +/** + * Creates action sets for Crossref affiliation relations inferred by BIP! + */ +public class CollectSoftwareRepositoryURLs implements Serializable { + + private static final Logger log = LoggerFactory.getLogger(CollectSoftwareRepositoryURLs.class); + // public static final String BIP_AFFILIATIONS_CLASSID = "result:organization:bipinference"; +// public static final String BIP_AFFILIATIONS_CLASSNAME = "Affiliation relation inferred by BIP!"; +// public static final String BIP_INFERENCE_PROVENANCE = "bip:affiliation:crossref"; + private static final String DEFAULT_VISIT_TYPE = "git"; + private static final int CONCURRENT_API_CALLS = 1; + + private static final String SWH_LATEST_VISIT_URL = "https://archive.softwareheritage.org/api/1/origin/%s/visit/latest/"; + + public static void main(String[] args) throws Exception { + + String jsonConfiguration = IOUtils + .toString( + CollectSoftwareRepositoryURLs.class + .getResourceAsStream("/eu/dnetlib/dhp/swh/input_parameters.json")); + + final ArgumentApplicationParser parser = new ArgumentApplicationParser(jsonConfiguration); + parser.parseArgument(args); + + final Boolean isSparkSessionManaged = Optional + .ofNullable(parser.get("isSparkSessionManaged")) + .map(Boolean::valueOf) + .orElse(Boolean.TRUE); + log.info("isSparkSessionManaged: {}", isSparkSessionManaged); + + final String hiveDbName = parser.get("hiveDbName"); + log.info("hiveDbName {}: ", hiveDbName); + + final String outputPath = parser.get("softwareCodeRepositoryURLs"); + log.info("softwareCodeRepositoryURLs {}: ", outputPath); + + final String hiveMetastoreUris = parser.get("hiveMetastoreUris"); + log.info("hiveMetastoreUris: {}", hiveMetastoreUris); + + SparkConf conf = new SparkConf(); + conf.set("hive.metastore.uris", hiveMetastoreUris); + + runWithSparkHiveSession( + conf, + isSparkSessionManaged, + spark -> { + doRun(spark, hiveDbName, outputPath); + }); + } + + private static void doRun(SparkSession spark, String hiveDbName, String outputPath) { + + String queryTemplate = "SELECT distinct coderepositoryurl.value " + + "FROM %s.software " + + "WHERE coderepositoryurl.value IS NOT NULL"; + String query = String.format(queryTemplate, hiveDbName); + + log.info("Hive query to fetch software code URLs: {}", query); + + Dataset df = spark.sql(query); + + // write distinct repository URLs + df + .write() + .mode(SaveMode.Overwrite) +// .option("compression", "gzip") + .csv(outputPath); + } + + private static Dataset readSoftware(SparkSession spark, String inputPath) { + return spark + .read() + .json(inputPath) + .select( + new Column("codeRepositoryUrl.value").as("codeRepositoryUrl"), + new Column("dataInfo.deletedbyinference"), + new Column("dataInfo.invisible")); + } + + private static Dataset filterSoftware(Dataset softwareDF, Integer limit) { + + Dataset df = softwareDF + .where(softwareDF.col("codeRepositoryUrl").isNotNull()) + .where("deletedbyinference = false") + .where("invisible = false") + .drop("deletedbyinference") + .drop("invisible"); + +// TODO remove when done + df = df.limit(limit); + + return df; + } + + public static Dataset makeParallelRequests(SparkSession spark, Dataset softwareDF) { + // TODO replace with coalesce ? + Dataset df = softwareDF.repartition(CONCURRENT_API_CALLS); + + log.info("Number of partitions: {}", df.rdd().getNumPartitions()); + + ObjectMapper objectMapper = new ObjectMapper(); + + List collectedRows = df + .javaRDD() + // max parallelism should be equal to the number of partitions here + .mapPartitions((FlatMapFunction, Row>) partition -> { + List resultRows = new ArrayList<>(); + while (partition.hasNext()) { + Row row = partition.next(); + String url = String.format(SWH_LATEST_VISIT_URL, row.getString(0)); + +// String snapshotId = null; +// String type = null; +// String date = null; + + String responseBody = makeAPICall(url); + TimeUnit.SECONDS.sleep(1); +// Thread.sleep(500); +// if (responseBody != null) { +// LastVisitResponse visitResponse = objectMapper.readValue(responseBody, LastVisitResponse.class); +// snapshotId = visitResponse.getSnapshot(); +// type = visitResponse.getType(); +// date = visitResponse.getDate(); +// } +// resultRows.add(RowFactory.create(url, snapshotId, type, date)); + + resultRows.add(RowFactory.create(url, responseBody)); + } + return resultRows.iterator(); + + }) + .collect(); + + StructType resultSchema = new StructType() + .add("codeRepositoryUrl", DataTypes.StringType) + .add("response", DataTypes.StringType); + +// .add("snapshotId", DataTypes.StringType) +// .add("type", DataTypes.StringType) +// .add("date", DataTypes.StringType); + + // create a DataFrame from the collected rows + return spark.createDataFrame(collectedRows, resultSchema); + } + + private static String makeAPICall(String url) throws IOException { + System.out.println(java.time.LocalDateTime.now()); + + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet(url); + httpGet + .setHeader( + "Authorization", + "Bearer eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtODQ2Ny05MzAyZjk3MTFkOGEifQ.eyJpYXQiOjE2OTQ2MzYwMjAsImp0aSI6IjkwZjdkNTNjLTQ5YTktNGFiMy1hY2E0LTcwMTViMjEyZTNjNiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zb2Z0d2FyZWhlcml0YWdlLm9yZy9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwiYXVkIjoiaHR0cHM6Ly9hdXRoLnNvZnR3YXJlaGVyaXRhZ2Uub3JnL2F1dGgvcmVhbG1zL1NvZnR3YXJlSGVyaXRhZ2UiLCJzdWIiOiIzMTY5OWZkNC0xNmE0LTQxOWItYTdhMi00NjI5MDY4ZjI3OWEiLCJ0eXAiOiJPZmZsaW5lIiwiYXpwIjoic3doLXdlYiIsInNlc3Npb25fc3RhdGUiOiIzMjYzMzEwMS00ZDRkLTQwMjItODU2NC1iMzNlMTJiNTE3ZDkiLCJzY29wZSI6Im9wZW5pZCBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIGVtYWlsIn0.XHj1VIZu1dZ4Ej32-oU84mFmaox9cLNjXosNxwZM0Xs"); + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + int statusCode = response.getStatusLine().getStatusCode(); +// if (statusCode != 200) +// return null; + Header[] headers = response.getHeaders("X-RateLimit-Remaining"); + for (Header header : headers) { + System.out + .println( + "Key : " + header.getName() + + " ,Value : " + header.getValue()); + } + HttpEntity entity = response.getEntity(); + if (entity != null) { + return EntityUtils.toString(entity); + } + } + } + return null; + } +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java new file mode 100644 index 000000000..435397590 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java @@ -0,0 +1,40 @@ + +package eu.dnetlib.dhp.swh.models; + +import com.cloudera.com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class LastVisitResponse { + + private String type; + + private String date; + + @JsonProperty("snapshot") + private String snapshotId; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public String getSnapshot() { + return snapshotId; + } + + public void setSnapshot(String snapshotId) { + this.snapshotId = snapshotId; + } +} diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json new file mode 100644 index 000000000..dd5432b93 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json @@ -0,0 +1,26 @@ +[ + { + "paramName": "issm", + "paramLongName": "isSparkSessionManaged", + "paramDescription": "when true will stop SparkSession after job execution", + "paramRequired": false + }, + { + "paramName": "ip", + "paramLongName": "softwareCodeRepositoryURLs", + "paramDescription": "the URL where to store software repository URLs", + "paramRequired": true + }, + { + "paramName": "db", + "paramLongName": "hiveDbName", + "paramDescription": "the target hive database name", + "paramRequired": true + }, + { + "paramName": "hmu", + "paramLongName": "hiveMetastoreUris", + "paramDescription": "the hive metastore uris", + "paramRequired": true + } +] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties new file mode 100644 index 000000000..a63343aed --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -0,0 +1,25 @@ +# hive +hiveDbName=openaire_prod_20230914 +hiveMetastoreUris=thrift://iis-cdh5-test-m3.ocean.icm.edu.pl:9083 + +# oozie +oozie.action.sharelib.for.spark=spark2 +oozie.use.system.libpath=true +oozie.wf.application.path=${oozieTopWfApplicationPath} +oozie.wf.application.path=${oozieTopWfApplicationPath} +oozieActionShareLibForSpark2=spark2 + +# spark +spark2EventLogDir=/user/spark/spark2ApplicationHistory +spark2ExtraListeners=com.cloudera.spark.lineage.NavigatorAppListener +spark2SqlQueryExecutionListeners=com.cloudera.spark.lineage.NavigatorQueryListener +spark2YarnHistoryServerAddress=http://iis-cdh5-test-gw.ocean.icm.edu.pl:18089 +sparkSqlWarehouseDir=/user/hive/warehouse + +# misc +wfAppPath=${oozieTopWfApplicationPath} +resourceManager=http://iis-cdh5-test-m2.ocean.icm.edu.pl:8088/cluster + +# custom params +softwareCodeRepositoryURLs=${workingDir}/code_repo_urls.csv +resume=collect-software-repository-urls diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml new file mode 100644 index 000000000..9832e5f26 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${jobTracker} + ${nameNode} + + + + + + Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}] + + + + + ${wf:conf('startFrom') eq 'collect-software-repository-urls'} + + + + + + + yarn + cluster + Collect software repository URLs + eu.dnetlib.dhp.swh.CollectSoftwareRepositoryURLs + dhp-swh-${projectVersion}.jar + + --executor-memory=${sparkExecutorMemory} + --executor-cores=${sparkExecutorCores} + --driver-memory=${sparkDriverMemory} + --conf spark.extraListeners=${spark2ExtraListeners} + --conf spark.sql.queryExecutionListeners=${spark2SqlQueryExecutionListeners} + --conf spark.yarn.historyServer.address=${spark2YarnHistoryServerAddress} + --conf spark.eventLog.dir=${nameNode}${spark2EventLogDir} + --conf spark.sql.warehouse.dir=${sparkSqlWarehouseDir} + + + --softwareCodeRepositoryURLs${softwareCodeRepositoryURLs} + --hiveDbName${hiveDbName} + --hiveMetastoreUris${hiveMetastoreUris} + + + + + + + + + \ No newline at end of file diff --git a/dhp-workflows/pom.xml b/dhp-workflows/pom.xml index d054ba39b..64f5f2d26 100644 --- a/dhp-workflows/pom.xml +++ b/dhp-workflows/pom.xml @@ -39,6 +39,7 @@ dhp-broker-events dhp-doiboost dhp-impact-indicators + dhp-swh From ed9c81a0b7a12ac2b337843ef42f63ca62b1f063 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Wed, 27 Sep 2023 19:00:54 +0300 Subject: [PATCH 02/16] Add steps to collect last visit data && archive not found repository URLs --- .../java/eu/dnetlib/dhp/common/Constants.java | 1 + .../common/collection/HttpClientParams.java | 37 +++- .../dhp/common/collection/HttpConnector2.java | 7 + dhp-workflows/dhp-swh/pom.xml | 6 + .../dhp/swh/ArchiveRepositoryURLs.java | 137 +++++++++++++++ .../swh/CollectLastVisitRepositoryData.java | 120 +++++++++++++ .../swh/CollectSoftwareRepositoryURLs.java | 158 ++---------------- ...tVisitResponse.java => LastVisitData.java} | 10 +- .../dnetlib/dhp/swh/utils/SWHConnection.java | 138 +++++++++++++++ .../dnetlib/dhp/swh/utils/SWHConstants.java | 13 ++ .../eu/dnetlib/dhp/swh/utils/SWHUtils.java | 94 +++++++++++ .../swh/input_archive_repository_urls.json | 26 +++ ...ut_collect_last_visit_repository_data.json | 38 +++++ ...put_collect_software_repository_urls.json} | 2 +- .../eu/dnetlib/dhp/swh/job.properties | 22 +-- .../dhp/swh/oozie_app/config-default.xml | 50 ++++++ .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 109 ++++++------ .../dhp/swh/ArchiveRepositoryURLsTest.java | 35 ++++ .../eu/dnetlib/dhp/swh/SWHConnectionTest.java | 57 +++++++ .../dhp/swh/lastVisitDataToArchive.csv | 6 + 20 files changed, 848 insertions(+), 218 deletions(-) create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java rename dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/{LastVisitResponse.java => LastVisitData.java} (81%) create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json rename dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/{input_parameters.json => input_collect_software_repository_urls.json} (96%) create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml create mode 100644 dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java create mode 100644 dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java create mode 100644 dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/common/Constants.java b/dhp-common/src/main/java/eu/dnetlib/dhp/common/Constants.java index 4f2c6341e..0477d6399 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/common/Constants.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/common/Constants.java @@ -51,6 +51,7 @@ public class Constants { public static final String RETRY_DELAY = "retryDelay"; public static final String CONNECT_TIMEOUT = "connectTimeOut"; public static final String READ_TIMEOUT = "readTimeOut"; + public static final String REQUEST_METHOD = "requestMethod"; public static final String FROM_DATE_OVERRIDE = "fromDateOverride"; public static final String UNTIL_DATE_OVERRIDE = "untilDateOverride"; diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java index 6fcec00dd..55f9ceb8b 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java @@ -1,6 +1,9 @@ package eu.dnetlib.dhp.common.collection; +import java.util.HashMap; +import java.util.Map; + /** * Bundles the http connection parameters driving the client behaviour. */ @@ -13,6 +16,8 @@ public class HttpClientParams { public static int _connectTimeOut = 10; // seconds public static int _readTimeOut = 30; // seconds + public static String _requestMethod = "GET"; + /** * Maximum number of allowed retires before failing */ @@ -38,17 +43,30 @@ public class HttpClientParams { */ private int readTimeOut; + /** + * Custom http headers + */ + private Map headers; + + /** + * Request method (i.e., GET, POST etc) + */ + private String requestMethod; + + public HttpClientParams() { - this(_maxNumberOfRetry, _requestDelay, _retryDelay, _connectTimeOut, _readTimeOut); + this(_maxNumberOfRetry, _requestDelay, _retryDelay, _connectTimeOut, _readTimeOut, new HashMap<>(), _requestMethod); } public HttpClientParams(int maxNumberOfRetry, int requestDelay, int retryDelay, int connectTimeOut, - int readTimeOut) { + int readTimeOut, Map headers, String requestMethod) { this.maxNumberOfRetry = maxNumberOfRetry; this.requestDelay = requestDelay; this.retryDelay = retryDelay; this.connectTimeOut = connectTimeOut; this.readTimeOut = readTimeOut; + this.headers = headers; + this.requestMethod = requestMethod; } public int getMaxNumberOfRetry() { @@ -91,4 +109,19 @@ public class HttpClientParams { this.readTimeOut = readTimeOut; } + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public String getRequestMethod() { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) { + this.requestMethod = requestMethod; + } } diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpConnector2.java b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpConnector2.java index dd46ab1f4..905457bcd 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpConnector2.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpConnector2.java @@ -107,7 +107,14 @@ public class HttpConnector2 { urlConn.setReadTimeout(getClientParams().getReadTimeOut() * 1000); urlConn.setConnectTimeout(getClientParams().getConnectTimeOut() * 1000); urlConn.addRequestProperty(HttpHeaders.USER_AGENT, userAgent); + urlConn.setRequestMethod(getClientParams().getRequestMethod()); + // if provided, add custom headers + if (!getClientParams().getHeaders().isEmpty()) { + for (Map.Entry headerEntry : getClientParams().getHeaders().entrySet()) { + urlConn.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); + } + } if (log.isDebugEnabled()) { logHeaderFields(urlConn); } diff --git a/dhp-workflows/dhp-swh/pom.xml b/dhp-workflows/dhp-swh/pom.xml index 501b2aef8..80fff4587 100644 --- a/dhp-workflows/dhp-swh/pom.xml +++ b/dhp-workflows/dhp-swh/pom.xml @@ -99,6 +99,12 @@ httpclient 4.5.13 + + org.datanucleus + datanucleus-core + 3.2.10 + compile + diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java new file mode 100644 index 000000000..7b3b74d9e --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java @@ -0,0 +1,137 @@ + +package eu.dnetlib.dhp.swh; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.swh.models.LastVisitData; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import eu.dnetlib.dhp.swh.utils.SWHUtils; +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.io.SequenceFile; +import org.apache.hadoop.io.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URL; +import java.util.Date; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import static eu.dnetlib.dhp.common.Constants.REQUEST_METHOD; +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; + +/** + * Sends archive requests to the SWH API for those software repository URLs that are missing from them + * + * @author Serafeim Chatzopoulos + */ +public class ArchiveRepositoryURLs { + + private static final Logger log = LoggerFactory.getLogger(ArchiveRepositoryURLs.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static SWHConnection swhConnection = null; + + public static void main(final String[] args) throws IOException, ParseException { + final ArgumentApplicationParser argumentParser = new ArgumentApplicationParser( + IOUtils + .toString( + CollectLastVisitRepositoryData.class + .getResourceAsStream( + "/eu/dnetlib/dhp/swh/input_archive_repository_urls.json"))); + argumentParser.parseArgument(args); + + final String hdfsuri = argumentParser.get("namenode"); + log.info("hdfsURI: {}", hdfsuri); + + final String inputPath = argumentParser.get("lastVisitsPath"); + log.info("inputPath: {}", inputPath); + + final String outputPath = argumentParser.get("archiveRequestsPath"); + log.info("outputPath: {}", outputPath); + + final Integer archiveThresholdInDays = Integer.parseInt(argumentParser.get("archiveThresholdInDays")); + log.info("archiveThresholdInDays: {}", archiveThresholdInDays); + + final HttpClientParams clientParams = SWHUtils.getClientParams(argumentParser); + + swhConnection = new SWHConnection(clientParams); + + final FileSystem fs = FileSystem.get(getHadoopConfiguration(hdfsuri)); + + archive(fs, inputPath, outputPath, archiveThresholdInDays); + + } + + private static void archive(FileSystem fs, String inputPath, String outputPath, Integer archiveThresholdInDays) throws IOException { + + SequenceFile.Reader fr = SWHUtils.getSequenceFileReader(fs, inputPath); + SequenceFile.Writer fw = SWHUtils.getSequenceFileWriter(fs, outputPath); + + // Create key and value objects to hold data + Text repoUrl = new Text(); + Text lastVisitData = new Text(); + + // Read key-value pairs from the SequenceFile and handle appropriately + while (fr.next(repoUrl, lastVisitData)) { + + String response = handleRecord(repoUrl.toString(), lastVisitData.toString(), archiveThresholdInDays); + + // response is equal to null when no need for request + if (response != null) { + SWHUtils.appendToSequenceFile(fw, repoUrl.toString(), response); + } + + } + + // Close readers + fw.close(); + fr.close(); + } + + public static String handleRecord(String repoUrl, String lastVisitData, Integer archiveThresholdInDays) throws IOException { + System.out.println("Key: " + repoUrl + ", Value: " + lastVisitData); + + LastVisitData lastVisit = OBJECT_MAPPER.readValue(lastVisitData, LastVisitData.class); + + // perform an archive request when no repoUrl was not found in previous step + if (lastVisit.getSnapshot() != null) { + + // OR last visit was before (now() - archiveThresholdInDays) + long diffInMillies = Math.abs((new Date()).getTime() - lastVisit.getDate().getTime()); + long diffInDays = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS); + + if (archiveThresholdInDays >= diffInDays) { + return null; + } + } + + // if last visit data are available, re-use version control type, else use the default one (i.e., git) + String visitType = Optional + .ofNullable(lastVisit.getType()) + .orElse(SWHConstants.DEFAULT_VISIT_TYPE); + + URL url = new URL(String.format(SWHConstants.SWH_ARCHIVE_URL, visitType, repoUrl.trim())); + System.out.println(url.toString()); + + String response; + try { + response = swhConnection.call(url.toString()); + } catch (CollectorException e) { + log.info("Error in request: {}", url); + response = "{}"; + } + + return response; + + } + + + +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java new file mode 100644 index 000000000..c4b6412b5 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -0,0 +1,120 @@ + +package eu.dnetlib.dhp.swh; + +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import eu.dnetlib.dhp.swh.utils.SWHUtils; +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.SequenceFile; +import org.apache.hadoop.io.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; + +/** + * Given a file with software repository URLs, this class + * collects last visit data from the Software Heritage API. + * + * @author Serafeim Chatzopoulos + */ +public class CollectLastVisitRepositoryData { + + private static final Logger log = LoggerFactory.getLogger(CollectLastVisitRepositoryData.class); + private static SWHConnection swhConnection = null; + + public static void main(final String[] args) + throws IOException, ParseException, InterruptedException, URISyntaxException, CollectorException { + final ArgumentApplicationParser argumentParser = new ArgumentApplicationParser( + IOUtils + .toString( + CollectLastVisitRepositoryData.class + .getResourceAsStream( + "/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json"))); + argumentParser.parseArgument(args); + + log.info("Java Xmx: {}m", Runtime.getRuntime().maxMemory() / (1024 * 1024)); + + final String hdfsuri = argumentParser.get("namenode"); + log.info("hdfsURI: {}", hdfsuri); + + final String inputPath = argumentParser.get("softwareCodeRepositoryURLs"); + log.info("inputPath: {}", inputPath); + + final String outputPath = argumentParser.get("lastVisitsPath"); + log.info("outputPath: {}", outputPath); + + final HttpClientParams clientParams = SWHUtils.getClientParams(argumentParser); + + swhConnection = new SWHConnection(clientParams); + + final FileSystem fs = FileSystem.get(getHadoopConfiguration(hdfsuri)); + + collect(fs, inputPath, outputPath); + + fs.close(); + } + + private static void collect(FileSystem fs, String inputPath, String outputPath) + throws IOException { + + SequenceFile.Writer fw = SWHUtils.getSequenceFileWriter(fs, outputPath); + + // Specify the HDFS directory path you want to read + Path directoryPath = new Path(inputPath); + + // List all files in the directory + FileStatus[] partStatuses = fs.listStatus(directoryPath); + + for (FileStatus partStatus : partStatuses) { + + // Check if it's a file (not a directory) + if (partStatus.isFile()) { + handleFile(fs, partStatus.getPath(), fw); + } + + } + + fw.close(); + } + + private static void handleFile(FileSystem fs, Path partInputPath, SequenceFile.Writer fw) + throws IOException { + + BufferedReader br = SWHUtils.getFileReader(fs, partInputPath); + + String repoUrl; + while ((repoUrl = br.readLine()) != null) { + + URL url = new URL(String.format(SWHConstants.SWH_LATEST_VISIT_URL, repoUrl.trim())); + + String response; + try { + response = swhConnection.call(url.toString()); + } catch (CollectorException e) { + log.info("Error in request: {}", url); + response = "{}"; + } + + SWHUtils.appendToSequenceFile(fw, repoUrl, response); + } + + br.close(); + } + +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java index c91f2bb8c..f93280b5e 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java @@ -1,60 +1,37 @@ package eu.dnetlib.dhp.swh; -import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; -import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession; - -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.schema.oaf.Result; import org.apache.commons.io.IOUtils; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; import org.apache.spark.SparkConf; -import org.apache.spark.api.java.function.FlatMapFunction; -import org.apache.spark.sql.*; import org.apache.spark.sql.Dataset; -import org.apache.spark.sql.types.DataTypes; -import org.apache.spark.sql.types.StructType; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SaveMode; +import org.apache.spark.sql.SparkSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.Serializable; +import java.util.Optional; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.schema.common.ModelConstants; -import eu.dnetlib.dhp.schema.oaf.*; +import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; /** - * Creates action sets for Crossref affiliation relations inferred by BIP! + * Collects unique software repository URLs in the Graph using Hive + * + * @author Serafeim Chatzopoulos */ public class CollectSoftwareRepositoryURLs implements Serializable { private static final Logger log = LoggerFactory.getLogger(CollectSoftwareRepositoryURLs.class); - // public static final String BIP_AFFILIATIONS_CLASSID = "result:organization:bipinference"; -// public static final String BIP_AFFILIATIONS_CLASSNAME = "Affiliation relation inferred by BIP!"; -// public static final String BIP_INFERENCE_PROVENANCE = "bip:affiliation:crossref"; - private static final String DEFAULT_VISIT_TYPE = "git"; - private static final int CONCURRENT_API_CALLS = 1; - - private static final String SWH_LATEST_VISIT_URL = "https://archive.softwareheritage.org/api/1/origin/%s/visit/latest/"; public static void main(String[] args) throws Exception { String jsonConfiguration = IOUtils .toString( CollectSoftwareRepositoryURLs.class - .getResourceAsStream("/eu/dnetlib/dhp/swh/input_parameters.json")); + .getResourceAsStream("/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json")); final ArgumentApplicationParser parser = new ArgumentApplicationParser(jsonConfiguration); parser.parseArgument(args); @@ -89,7 +66,10 @@ public class CollectSoftwareRepositoryURLs implements Serializable { String queryTemplate = "SELECT distinct coderepositoryurl.value " + "FROM %s.software " + - "WHERE coderepositoryurl.value IS NOT NULL"; + "WHERE coderepositoryurl.value IS NOT NULL " + + "AND datainfo.deletedbyinference = FALSE " + + "AND datainfo.invisible = FALSE " + + "LIMIT 1000"; // TODO remove String query = String.format(queryTemplate, hiveDbName); log.info("Hive query to fetch software code URLs: {}", query); @@ -100,112 +80,6 @@ public class CollectSoftwareRepositoryURLs implements Serializable { df .write() .mode(SaveMode.Overwrite) -// .option("compression", "gzip") .csv(outputPath); } - - private static Dataset readSoftware(SparkSession spark, String inputPath) { - return spark - .read() - .json(inputPath) - .select( - new Column("codeRepositoryUrl.value").as("codeRepositoryUrl"), - new Column("dataInfo.deletedbyinference"), - new Column("dataInfo.invisible")); - } - - private static Dataset filterSoftware(Dataset softwareDF, Integer limit) { - - Dataset df = softwareDF - .where(softwareDF.col("codeRepositoryUrl").isNotNull()) - .where("deletedbyinference = false") - .where("invisible = false") - .drop("deletedbyinference") - .drop("invisible"); - -// TODO remove when done - df = df.limit(limit); - - return df; - } - - public static Dataset makeParallelRequests(SparkSession spark, Dataset softwareDF) { - // TODO replace with coalesce ? - Dataset df = softwareDF.repartition(CONCURRENT_API_CALLS); - - log.info("Number of partitions: {}", df.rdd().getNumPartitions()); - - ObjectMapper objectMapper = new ObjectMapper(); - - List collectedRows = df - .javaRDD() - // max parallelism should be equal to the number of partitions here - .mapPartitions((FlatMapFunction, Row>) partition -> { - List resultRows = new ArrayList<>(); - while (partition.hasNext()) { - Row row = partition.next(); - String url = String.format(SWH_LATEST_VISIT_URL, row.getString(0)); - -// String snapshotId = null; -// String type = null; -// String date = null; - - String responseBody = makeAPICall(url); - TimeUnit.SECONDS.sleep(1); -// Thread.sleep(500); -// if (responseBody != null) { -// LastVisitResponse visitResponse = objectMapper.readValue(responseBody, LastVisitResponse.class); -// snapshotId = visitResponse.getSnapshot(); -// type = visitResponse.getType(); -// date = visitResponse.getDate(); -// } -// resultRows.add(RowFactory.create(url, snapshotId, type, date)); - - resultRows.add(RowFactory.create(url, responseBody)); - } - return resultRows.iterator(); - - }) - .collect(); - - StructType resultSchema = new StructType() - .add("codeRepositoryUrl", DataTypes.StringType) - .add("response", DataTypes.StringType); - -// .add("snapshotId", DataTypes.StringType) -// .add("type", DataTypes.StringType) -// .add("date", DataTypes.StringType); - - // create a DataFrame from the collected rows - return spark.createDataFrame(collectedRows, resultSchema); - } - - private static String makeAPICall(String url) throws IOException { - System.out.println(java.time.LocalDateTime.now()); - - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet httpGet = new HttpGet(url); - httpGet - .setHeader( - "Authorization", - "Bearer eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtODQ2Ny05MzAyZjk3MTFkOGEifQ.eyJpYXQiOjE2OTQ2MzYwMjAsImp0aSI6IjkwZjdkNTNjLTQ5YTktNGFiMy1hY2E0LTcwMTViMjEyZTNjNiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zb2Z0d2FyZWhlcml0YWdlLm9yZy9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwiYXVkIjoiaHR0cHM6Ly9hdXRoLnNvZnR3YXJlaGVyaXRhZ2Uub3JnL2F1dGgvcmVhbG1zL1NvZnR3YXJlSGVyaXRhZ2UiLCJzdWIiOiIzMTY5OWZkNC0xNmE0LTQxOWItYTdhMi00NjI5MDY4ZjI3OWEiLCJ0eXAiOiJPZmZsaW5lIiwiYXpwIjoic3doLXdlYiIsInNlc3Npb25fc3RhdGUiOiIzMjYzMzEwMS00ZDRkLTQwMjItODU2NC1iMzNlMTJiNTE3ZDkiLCJzY29wZSI6Im9wZW5pZCBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIGVtYWlsIn0.XHj1VIZu1dZ4Ej32-oU84mFmaox9cLNjXosNxwZM0Xs"); - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - int statusCode = response.getStatusLine().getStatusCode(); -// if (statusCode != 200) -// return null; - Header[] headers = response.getHeaders("X-RateLimit-Remaining"); - for (Header header : headers) { - System.out - .println( - "Key : " + header.getName() - + " ,Value : " + header.getValue()); - } - HttpEntity entity = response.getEntity(); - if (entity != null) { - return EntityUtils.toString(entity); - } - } - } - return null; - } } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java similarity index 81% rename from dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java rename to dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java index 435397590..b8cd6de6e 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitResponse.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java @@ -4,12 +4,14 @@ package eu.dnetlib.dhp.swh.models; import com.cloudera.com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.Date; + @JsonIgnoreProperties(ignoreUnknown = true) -public class LastVisitResponse { +public class LastVisitData { private String type; - private String date; + private Date date; @JsonProperty("snapshot") private String snapshotId; @@ -22,11 +24,11 @@ public class LastVisitResponse { this.type = type; } - public String getDate() { + public Date getDate() { return date; } - public void setDate(String date) { + public void setDate(Date date) { this.date = date; } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java new file mode 100644 index 000000000..46d512dcb --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java @@ -0,0 +1,138 @@ + +package eu.dnetlib.dhp.swh.utils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.http.Header; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.dnetlib.dhp.common.Constants; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.common.collection.HttpConnector2; + +public class SWHConnection { + + private static final Logger log = LoggerFactory.getLogger(SWHConnection.class); + + CloseableHttpClient httpClient; + + HttpClientParams clientParams; + + HttpConnector2 conn; + + public SWHConnection(HttpClientParams clientParams) { + +// // force http client to NOT transform double quotes (//) to single quote (/) +// RequestConfig requestConfig = RequestConfig.custom().setNormalizeUri(false).build(); +// +// // Create an HttpClient instance +// httpClient = HttpClientBuilder +// .create() +// .setDefaultRequestConfig(requestConfig) +// .build(); +// +// this.clientParams = clientParams; + // set custom headers + Map headers = new HashMap() { + { + put(HttpHeaders.ACCEPT, "application/json"); + put(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", SWHConstants.ACCESS_TOKEN)); + } + }; + + clientParams.setHeaders(headers); + + // create http connector + conn = new HttpConnector2(clientParams); + + } + + public String call(String url) throws CollectorException { + return conn.getInputSource(url); + } + + public String getLib(String url) throws IOException, CollectorException { + + // delay between requests + if (this.clientParams.getRequestDelay() > 0) { + log.info("Request delay: {}", this.clientParams.getRequestDelay()); + this.backOff(this.clientParams.getRequestDelay()); + } + + // Create an HttpGet request with the URL + HttpGet httpGet = new HttpGet(url); + httpGet.setHeader("Accept", "application/json"); + httpGet.setHeader("Authorization", String.format("Bearer %s", SWHConstants.ACCESS_TOKEN)); + + // Execute the request and get the response + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + + System.out.println(url); + + int responseCode = response.getStatusLine().getStatusCode(); + if (responseCode != HttpStatus.SC_OK) { + + } + + System.out.println(responseCode); + + List
httpHeaders = Arrays.asList(response.getAllHeaders()); + for (Header header : httpHeaders) { + System.out.println(header.getName() + ":\t" + header.getValue()); + } + + String rateRemaining = this.getRateRemaining(response); + + // back off when rate remaining limit is approaching + if (rateRemaining != null && (Integer.parseInt(rateRemaining) < 2)) { + int retryAfter = this.getRetryAfter(response); + + log.info("Rate Limit: {} - Backing off: {}", rateRemaining, retryAfter); + this.backOff(retryAfter); + } + + return EntityUtils.toString(response.getEntity()); + } + } + + private String getRateRemaining(CloseableHttpResponse response) { + Header header = response.getFirstHeader(Constants.HTTPHEADER_IETF_DRAFT_RATELIMIT_REMAINING); + if (header != null) { + return header.getValue(); + } + return null; + } + + private int getRetryAfter(CloseableHttpResponse response) { + Header header = response.getFirstHeader(HttpHeaders.RETRY_AFTER); + if (header != null) { + String retryAfter = header.getValue(); + if (NumberUtils.isCreatable(retryAfter)) { + return Integer.parseInt(retryAfter) + 10; + } + } + return 1000; + } + + private void backOff(int sleepTimeMs) throws CollectorException { + try { + Thread.sleep(sleepTimeMs); + } catch (InterruptedException e) { + throw new CollectorException(e); + } + } + +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java new file mode 100644 index 000000000..1299bc805 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java @@ -0,0 +1,13 @@ + +package eu.dnetlib.dhp.swh.utils; + +public class SWHConstants { + public static final String SWH_LATEST_VISIT_URL = "https://archive.softwareheritage.org/api/1/origin/%s/visit/latest/"; + + public static final String SWH_ARCHIVE_URL = "https://archive.softwareheritage.org/api/1/origin/save/%s/url/%s/"; + + public static final String ACCESS_TOKEN = ""; + + public static final String DEFAULT_VISIT_TYPE = "git"; + +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java new file mode 100644 index 000000000..8200e7b34 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java @@ -0,0 +1,94 @@ + +package eu.dnetlib.dhp.swh.utils; + +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import org.apache.hadoop.fs.FSDataInputStream; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.SequenceFile; +import org.apache.hadoop.io.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import static eu.dnetlib.dhp.common.Constants.*; + +public class SWHUtils { + + private static final Logger log = LoggerFactory.getLogger(SWHUtils.class); + + public static HttpClientParams getClientParams(ArgumentApplicationParser argumentParser) { + + final HttpClientParams clientParams = new HttpClientParams(); + clientParams + .setMaxNumberOfRetry( + Optional + .ofNullable(argumentParser.get(MAX_NUMBER_OF_RETRY)) + .map(Integer::parseInt) + .orElse(HttpClientParams._maxNumberOfRetry)); + log.info("maxNumberOfRetry is {}", clientParams.getMaxNumberOfRetry()); + + clientParams + .setRequestDelay( + Optional + .ofNullable(argumentParser.get(REQUEST_DELAY)) + .map(Integer::parseInt) + .orElse(HttpClientParams._requestDelay)); + log.info("requestDelay is {}", clientParams.getRequestDelay()); + + clientParams + .setRetryDelay( + Optional + .ofNullable(argumentParser.get(RETRY_DELAY)) + .map(Integer::parseInt) + .orElse(HttpClientParams._retryDelay)); + log.info("retryDelay is {}", clientParams.getRetryDelay()); + + clientParams + .setRequestMethod( + Optional + .ofNullable(argumentParser.get(REQUEST_METHOD)) + .orElse(HttpClientParams._requestMethod)); + log.info("requestMethod is {}", clientParams.getRequestMethod()); + + return clientParams; + } + + public static BufferedReader getFileReader(FileSystem fs, Path inputPath) throws IOException { + FSDataInputStream inputStream = fs.open(inputPath); + return new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + } + + public static SequenceFile.Writer getSequenceFileWriter(FileSystem fs, String outputPath) throws IOException { + return SequenceFile + .createWriter( + fs.getConf(), + SequenceFile.Writer.file(new Path(outputPath)), + SequenceFile.Writer.keyClass(Text.class), + SequenceFile.Writer.valueClass(Text.class)); + } + + public static SequenceFile.Reader getSequenceFileReader(FileSystem fs, String inputPath) throws IOException { + Path filePath = new Path(inputPath); + SequenceFile.Reader.Option fileOption = SequenceFile.Reader.file(filePath); + + return new SequenceFile.Reader(fs.getConf(), fileOption); + } + + public static void appendToSequenceFile(SequenceFile.Writer fw, String keyStr, String valueStr) throws IOException { + Text key = new Text(); + key.set(keyStr); + + Text value = new Text(); + value.set(valueStr); + + fw.append(key, value); + } +} diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json new file mode 100644 index 000000000..5ec481305 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json @@ -0,0 +1,26 @@ +[ + { + "paramName": "n", + "paramLongName": "namenode", + "paramDescription": "the Name Node URI", + "paramRequired": true + }, + { + "paramName": "lv", + "paramLongName": "lastVisitsPath", + "paramDescription": "the URL where to store last visits data", + "paramRequired": true + }, + { + "paramName": "rqd", + "paramLongName": "requestDelay", + "paramDescription": "the delay (ms) between requests", + "paramRequired": false + }, + { + "paramName": "atid", + "paramLongName": "archiveThresholdInDays", + "paramDescription": "the thershold (in days) required to issue an archive request", + "paramRequired": false + } +] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json new file mode 100644 index 000000000..6c59123be --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json @@ -0,0 +1,38 @@ +[ + { + "paramName": "n", + "paramLongName": "namenode", + "paramDescription": "the Name Node URI", + "paramRequired": true + }, + { + "paramName": "scr", + "paramLongName": "softwareCodeRepositoryURLs", + "paramDescription": "the URL from where to read software repository URLs", + "paramRequired": true + }, + { + "paramName": "lv", + "paramLongName": "lastVisitsPath", + "paramDescription": "the URL where to store last visits data", + "paramRequired": true + }, + { + "paramName": "mnr", + "paramLongName": "maxNumberOfRetry", + "paramDescription": "the maximum number of admitted connection retries", + "paramRequired": false + }, + { + "paramName": "rqd", + "paramLongName": "requestDelay", + "paramDescription": "the delay (ms) between requests", + "paramRequired": false + }, + { + "paramName": "rtd", + "paramLongName": "retryDelay", + "paramDescription": "the delay (ms) between retries", + "paramRequired": false + } +] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json similarity index 96% rename from dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json rename to dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json index dd5432b93..6e98c7673 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_parameters.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json @@ -6,7 +6,7 @@ "paramRequired": false }, { - "paramName": "ip", + "paramName": "scr", "paramLongName": "softwareCodeRepositoryURLs", "paramDescription": "the URL where to store software repository URLs", "paramRequired": true diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index a63343aed..e2c2af852 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -1,25 +1,11 @@ # hive hiveDbName=openaire_prod_20230914 -hiveMetastoreUris=thrift://iis-cdh5-test-m3.ocean.icm.edu.pl:9083 -# oozie -oozie.action.sharelib.for.spark=spark2 -oozie.use.system.libpath=true -oozie.wf.application.path=${oozieTopWfApplicationPath} -oozie.wf.application.path=${oozieTopWfApplicationPath} -oozieActionShareLibForSpark2=spark2 - -# spark -spark2EventLogDir=/user/spark/spark2ApplicationHistory -spark2ExtraListeners=com.cloudera.spark.lineage.NavigatorAppListener -spark2SqlQueryExecutionListeners=com.cloudera.spark.lineage.NavigatorQueryListener -spark2YarnHistoryServerAddress=http://iis-cdh5-test-gw.ocean.icm.edu.pl:18089 sparkSqlWarehouseDir=/user/hive/warehouse -# misc -wfAppPath=${oozieTopWfApplicationPath} -resourceManager=http://iis-cdh5-test-m2.ocean.icm.edu.pl:8088/cluster +# input/output files +softwareCodeRepositoryURLs=${workingDir}/1_code_repo_urls.csv +lastVisitsPath=${workingDir}/2_last_visits.seq +archiveRequestsPath=${workingDir}/3_archive_requests.seq -# custom params -softwareCodeRepositoryURLs=${workingDir}/code_repo_urls.csv resume=collect-software-repository-urls diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml new file mode 100644 index 000000000..7873d595e --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml @@ -0,0 +1,50 @@ + + + jobTracker + yarnRM + + + nameNode + hdfs://nameservice1 + + + oozie.use.system.libpath + true + + + oozie.action.sharelib.for.spark + spark2 + + + hiveMetastoreUris + thrift://iis-cdh5-test-m3.ocean.icm.edu.pl:9083 + + + spark2YarnHistoryServerAddress + http://iis-cdh5-test-gw.ocean.icm.edu.pl:18089 + + + spark2EventLogDir + /user/spark/spark2ApplicationHistory + + + spark2ExtraListeners + "com.cloudera.spark.lineage.NavigatorAppListener" + + + spark2SqlQueryExecutionListeners + "com.cloudera.spark.lineage.NavigatorQueryListener" + + + oozieActionShareLibForSpark2 + spark2 + + + resourceManager + http://iis-cdh5-test-m2.ocean.icm.edu.pl:8088/cluster + + + oozie.launcher.mapreduce.user.classpath.first + true + + \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index 9832e5f26..5062d562b 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -1,59 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + hiveDbName + The name of the Hive DB to be used + + + softwareCodeRepositoryURLs + The path in the HDSF to save the software repository URLs + + + resume + Variable that indicates the step to start from + + + ${jobTracker} ${nameNode} + + + oozie.action.sharelib.for.spark + ${oozieActionShareLibForSpark2} + + @@ -90,8 +62,43 @@ --softwareCodeRepositoryURLs${softwareCodeRepositoryURLs} --hiveDbName${hiveDbName} --hiveMetastoreUris${hiveMetastoreUris} - + + + + + + + eu.dnetlib.dhp.swh.CollectLastVisitRepositoryData + + --namenode${nameNode} + --softwareCodeRepositoryURLs${softwareCodeRepositoryURLs} + --lastVisitsPath${lastVisitsPath} + + --maxNumberOfRetry2 + --requestDelay0 + --retryDelay1 + --requestMethodGET + + + + + + + + + eu.dnetlib.dhp.swh.ArchiveRepositoryURLs + + --namenode${nameNode} + --lastVisitsPath${lastVisitsPath} + --archiveThresholdInDays365 + + --maxNumberOfRetry2 + --requestDelay0 + --retryDelay1 + --requestMethodPOST + + diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java new file mode 100644 index 000000000..06e40ae14 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java @@ -0,0 +1,35 @@ +package eu.dnetlib.dhp.swh; + +import eu.dnetlib.dhp.swh.utils.SWHUtils; +import org.apache.hadoop.fs.FileSystem; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; + +public class ArchiveRepositoryURLsTest { + + @Test + void testArchive() throws IOException { + String inputPath = getClass() + .getResource("/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv") + .getPath(); + + File file = new File(inputPath); + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); //creates a buffering character input stream + + String line; + while((line = br.readLine()) != null) { + String[] tokens = line.split("\t"); + + String response = ArchiveRepositoryURLs.handleRecord(tokens[0], tokens[1], 365); + System.out.println(tokens[0] + "\t" + response); + System.out.println(); + } + fr.close(); + } +} diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java new file mode 100644 index 000000000..d69f6ff1b --- /dev/null +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java @@ -0,0 +1,57 @@ + +package eu.dnetlib.dhp.swh; + +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +//import org.apache.hadoop.hdfs.MiniDFSCluster; + +public class SWHConnectionTest { + private static final Logger log = LoggerFactory.getLogger(SWHConnectionTest.class); + + @Test + void testGetCall() throws IOException { + + HttpClientParams clientParams = new HttpClientParams(); + clientParams.setRequestMethod("GET"); + + SWHConnection swhConnection = new SWHConnection(clientParams); + + String repoUrl = "https://github.com/stanford-futuredata/FAST"; + URL url = new URL(String.format(SWHConstants.SWH_LATEST_VISIT_URL, repoUrl)); + String response = null; + try { + response = swhConnection.call(url.toString()); + } catch (CollectorException e) { + System.out.println("Error in request: " + url); + } + System.out.println(response); + } + + @Test + void testPostCall() throws MalformedURLException { + HttpClientParams clientParams = new HttpClientParams(); + clientParams.setRequestMethod("POST"); + + SWHConnection swhConnection = new SWHConnection(clientParams); + + String repoUrl = "https://github.com/stanford-futuredata/FAST"; + URL url = new URL(String.format(SWHConstants.SWH_ARCHIVE_URL, SWHConstants.DEFAULT_VISIT_TYPE, repoUrl)); + String response = null; + try { + response = swhConnection.call(url.toString()); + } catch (CollectorException e) { + System.out.println("Error in request: " + url); + } + System.out.println(response); + } +} diff --git a/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv b/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv new file mode 100644 index 000000000..6477dd62a --- /dev/null +++ b/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv @@ -0,0 +1,6 @@ +https://github.com/bioinsilico/BIPSPI {"origin":"https://github.com/bioinsilico/BIPSPI","visit":1,"date":"2020-03-18T14:50:21.541822+00:00","status":"full","snapshot":"c6c69d2cd73ce89811448da5f031611df6f63bdb","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/bioinsilico/BIPSPI/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/c6c69d2cd73ce89811448da5f031611df6f63bdb/"} +https://github.com/mloop/kdiff-type1-error-rate/blob/master/analysis/simulation.R {} +https://github.com/schwanbeck/YSMR {"origin":"https://github.com/schwanbeck/YSMR","visit":6,"date":"2023-08-02T15:25:02.650676+00:00","status":"full","snapshot":"a9d1c5f0bca2def198b89f65bc9f7da3be8439ed","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/schwanbeck/YSMR/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/a9d1c5f0bca2def198b89f65bc9f7da3be8439ed/"} +https://github.com/lvclark/TASSELGBS_combine {"origin":"https://github.com/lvclark/TASSELGBS_combine","visit":1,"date":"2020-04-12T20:44:09.405589+00:00","status":"full","snapshot":"ffa6fefd3f5becefbea9fe0e6d5d93859c95c071","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/lvclark/TASSELGBS_combine/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/ffa6fefd3f5becefbea9fe0e6d5d93859c95c071/"} +https://github.com/PRIDE-Toolsuite/inspector-example-files {"origin":"https://github.com/PRIDE-Toolsuite/inspector-example-files","visit":12,"date":"2021-01-25T08:54:13.394674+00:00","status":"full","snapshot":"0b56eb0ad07cf778df6dabefc4b73636e0ae8b37","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/PRIDE-Toolsuite/inspector-example-files/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/0b56eb0ad07cf778df6dabefc4b73636e0ae8b37/"} +https://bitbucket.org/matwey/chelyabinsk {"origin":"https://bitbucket.org/matwey/chelyabinsk","visit":6,"date":"2021-09-24T19:32:43.322909+00:00","status":"full","snapshot":"215913858c3ee0e61e1aaea18241c5ee006da1b0","type":"hg","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://bitbucket.org/matwey/chelyabinsk/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/215913858c3ee0e61e1aaea18241c5ee006da1b0/"} \ No newline at end of file From ab0d70691cf4c21b886142b9a5d2b7327d6445d5 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Thu, 28 Sep 2023 20:56:18 +0300 Subject: [PATCH 03/16] Add step for archiving repoUrls to SWH --- .../common/collection/HttpClientParams.java | 4 +- .../dhp/swh/ArchiveRepositoryURLs.java | 99 +++++++++++++------ .../swh/CollectLastVisitRepositoryData.java | 31 +++--- .../swh/CollectSoftwareRepositoryURLs.java | 15 +-- .../dnetlib/dhp/swh/models/LastVisitData.java | 22 +++-- .../dnetlib/dhp/swh/utils/SWHConstants.java | 4 +- .../eu/dnetlib/dhp/swh/utils/SWHUtils.java | 39 ++++---- .../swh/input_archive_repository_urls.json | 24 +++++ ...ut_collect_last_visit_repository_data.json | 6 ++ .../eu/dnetlib/dhp/swh/job.properties | 4 + .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 35 +++++-- .../dhp/swh/ArchiveRepositoryURLsTest.java | 45 +++++---- .../eu/dnetlib/dhp/swh/SWHConnectionTest.java | 19 ++-- .../dhp/swh/lastVisitDataToArchive.csv | 1 + 14 files changed, 230 insertions(+), 118 deletions(-) diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java index 55f9ceb8b..d26d9c0e9 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/common/collection/HttpClientParams.java @@ -53,9 +53,9 @@ public class HttpClientParams { */ private String requestMethod; - public HttpClientParams() { - this(_maxNumberOfRetry, _requestDelay, _retryDelay, _connectTimeOut, _readTimeOut, new HashMap<>(), _requestMethod); + this(_maxNumberOfRetry, _requestDelay, _retryDelay, _connectTimeOut, _readTimeOut, new HashMap<>(), + _requestMethod); } public HttpClientParams(int maxNumberOfRetry, int requestDelay, int retryDelay, int connectTimeOut, diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java index 7b3b74d9e..38db27baf 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java @@ -1,14 +1,16 @@ package eu.dnetlib.dhp.swh; -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.common.collection.CollectorException; -import eu.dnetlib.dhp.common.collection.HttpClientParams; -import eu.dnetlib.dhp.swh.models.LastVisitData; -import eu.dnetlib.dhp.swh.utils.SWHConnection; -import eu.dnetlib.dhp.swh.utils.SWHConstants; -import eu.dnetlib.dhp.swh.utils.SWHUtils; +import static eu.dnetlib.dhp.common.Constants.REQUEST_METHOD; +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; + +import java.io.IOException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.apache.hadoop.fs.FileSystem; @@ -17,14 +19,17 @@ import org.apache.hadoop.io.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.URL; -import java.util.Date; -import java.util.Optional; -import java.util.concurrent.TimeUnit; +import com.fasterxml.jackson.databind.ObjectMapper; -import static eu.dnetlib.dhp.common.Constants.REQUEST_METHOD; -import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.schema.common.ModelSupport; +import eu.dnetlib.dhp.schema.oaf.utils.GraphCleaningFunctions; +import eu.dnetlib.dhp.swh.models.LastVisitData; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import eu.dnetlib.dhp.swh.utils.SWHUtils; /** * Sends archive requests to the SWH API for those software repository URLs that are missing from them @@ -69,7 +74,8 @@ public class ArchiveRepositoryURLs { } - private static void archive(FileSystem fs, String inputPath, String outputPath, Integer archiveThresholdInDays) throws IOException { + private static void archive(FileSystem fs, String inputPath, String outputPath, Integer archiveThresholdInDays) + throws IOException { SequenceFile.Reader fr = SWHUtils.getSequenceFileReader(fs, inputPath); SequenceFile.Writer fw = SWHUtils.getSequenceFileWriter(fs, outputPath); @@ -81,7 +87,13 @@ public class ArchiveRepositoryURLs { // Read key-value pairs from the SequenceFile and handle appropriately while (fr.next(repoUrl, lastVisitData)) { - String response = handleRecord(repoUrl.toString(), lastVisitData.toString(), archiveThresholdInDays); + String response = null; + try { + response = handleRecord(repoUrl.toString(), lastVisitData.toString(), archiveThresholdInDays); + } catch (java.text.ParseException e) { + log.error("Could not handle record with repo Url: {}", repoUrl.toString()); + throw new RuntimeException(e); + } // response is equal to null when no need for request if (response != null) { @@ -95,43 +107,68 @@ public class ArchiveRepositoryURLs { fr.close(); } - public static String handleRecord(String repoUrl, String lastVisitData, Integer archiveThresholdInDays) throws IOException { - System.out.println("Key: " + repoUrl + ", Value: " + lastVisitData); + public static String handleRecord(String repoUrl, String lastVisitData, Integer archiveThresholdInDays) + throws IOException, java.text.ParseException { + + log.info("{ Key: {}, Value: {} }", repoUrl, lastVisitData); LastVisitData lastVisit = OBJECT_MAPPER.readValue(lastVisitData, LastVisitData.class); - // perform an archive request when no repoUrl was not found in previous step + // a previous attempt for archival has been made, and repository URL was not found + // avoid performing the same archive request again + if (lastVisit.getType() != null && + lastVisit.getType().equals(SWHConstants.VISIT_STATUS_NOT_FOUND)) { + + log.info("Avoid request -- previous archive request returned NOT_FOUND"); + return null; + } + + // if we have last visit data if (lastVisit.getSnapshot() != null) { - // OR last visit was before (now() - archiveThresholdInDays) - long diffInMillies = Math.abs((new Date()).getTime() - lastVisit.getDate().getTime()); - long diffInDays = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS); + String cleanDate = GraphCleaningFunctions.cleanDate(lastVisit.getDate()); - if (archiveThresholdInDays >= diffInDays) { - return null; + // and the last visit date can be parsed + if (cleanDate != null) { + + SimpleDateFormat formatter = new SimpleDateFormat(ModelSupport.DATE_FORMAT); + Date lastVisitDate = formatter.parse(cleanDate); + + // OR last visit time < (now() - archiveThresholdInDays) + long diffInMillies = Math.abs((new Date()).getTime() - lastVisitDate.getTime()); + long diffInDays = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS); + log.info("Date diff from now (in days): {}", diffInDays); + + // do not perform a request, if the last visit date is no older than $archiveThresholdInDays + if (archiveThresholdInDays >= diffInDays) { + log.info("Avoid request -- no older than {} days", archiveThresholdInDays); + return null; + } } } - // if last visit data are available, re-use version control type, else use the default one (i.e., git) + // ELSE perform an archive request + log.info("Perform archive request for: {}", repoUrl); + + // if last visit data are available, re-use version control type, + // else use the default one (i.e., git) String visitType = Optional .ofNullable(lastVisit.getType()) .orElse(SWHConstants.DEFAULT_VISIT_TYPE); URL url = new URL(String.format(SWHConstants.SWH_ARCHIVE_URL, visitType, repoUrl.trim())); - System.out.println(url.toString()); + + log.info("Sending archive request: {}", url); String response; try { response = swhConnection.call(url.toString()); } catch (CollectorException e) { - log.info("Error in request: {}", url); + log.error("Error in request: {}", url); response = "{}"; } return response; - } - - } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java index c4b6412b5..9386b6876 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -1,12 +1,15 @@ package eu.dnetlib.dhp.swh; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.common.collection.CollectorException; -import eu.dnetlib.dhp.common.collection.HttpClientParams; -import eu.dnetlib.dhp.swh.utils.SWHConnection; -import eu.dnetlib.dhp.swh.utils.SWHConstants; -import eu.dnetlib.dhp.swh.utils.SWHUtils; +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.apache.hadoop.fs.FSDataInputStream; @@ -18,14 +21,12 @@ import org.apache.hadoop.io.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import eu.dnetlib.dhp.swh.utils.SWHUtils; /** * Given a file with software repository URLs, this class @@ -107,7 +108,7 @@ public class CollectLastVisitRepositoryData { try { response = swhConnection.call(url.toString()); } catch (CollectorException e) { - log.info("Error in request: {}", url); + log.error("Error in request: {}", url); response = "{}"; } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java index f93280b5e..c1a0fafa5 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java @@ -1,8 +1,11 @@ package eu.dnetlib.dhp.swh; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.schema.oaf.Result; +import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; + +import java.io.Serializable; +import java.util.Optional; + import org.apache.commons.io.IOUtils; import org.apache.spark.SparkConf; import org.apache.spark.sql.Dataset; @@ -12,10 +15,8 @@ import org.apache.spark.sql.SparkSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.Serializable; -import java.util.Optional; - -import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.schema.oaf.Result; /** * Collects unique software repository URLs in the Graph using Hive @@ -69,7 +70,7 @@ public class CollectSoftwareRepositoryURLs implements Serializable { "WHERE coderepositoryurl.value IS NOT NULL " + "AND datainfo.deletedbyinference = FALSE " + "AND datainfo.invisible = FALSE " + - "LIMIT 1000"; // TODO remove + "LIMIT 1000"; String query = String.format(queryTemplate, hiveDbName); log.info("Hive query to fetch software code URLs: {}", query); diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java index b8cd6de6e..eaff5ce02 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java @@ -1,21 +1,23 @@ package eu.dnetlib.dhp.swh.models; +import java.util.Date; + +import com.cloudera.com.fasterxml.jackson.annotation.JsonFormat; import com.cloudera.com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.util.Date; - @JsonIgnoreProperties(ignoreUnknown = true) public class LastVisitData { private String type; - - private Date date; + private String date; @JsonProperty("snapshot") private String snapshotId; + private String status; + public String getType() { return type; } @@ -24,11 +26,11 @@ public class LastVisitData { this.type = type; } - public Date getDate() { + public String getDate() { return date; } - public void setDate(Date date) { + public void setDate(String date) { this.date = date; } @@ -39,4 +41,12 @@ public class LastVisitData { public void setSnapshot(String snapshotId) { this.snapshotId = snapshotId; } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java index 1299bc805..f58705188 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java @@ -6,8 +6,10 @@ public class SWHConstants { public static final String SWH_ARCHIVE_URL = "https://archive.softwareheritage.org/api/1/origin/save/%s/url/%s/"; - public static final String ACCESS_TOKEN = ""; + public static final String ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtODQ2Ny05MzAyZjk3MTFkOGEifQ.eyJpYXQiOjE2OTQ2MzYwMjAsImp0aSI6IjkwZjdkNTNjLTQ5YTktNGFiMy1hY2E0LTcwMTViMjEyZTNjNiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zb2Z0d2FyZWhlcml0YWdlLm9yZy9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwiYXVkIjoiaHR0cHM6Ly9hdXRoLnNvZnR3YXJlaGVyaXRhZ2Uub3JnL2F1dGgvcmVhbG1zL1NvZnR3YXJlSGVyaXRhZ2UiLCJzdWIiOiIzMTY5OWZkNC0xNmE0LTQxOWItYTdhMi00NjI5MDY4ZjI3OWEiLCJ0eXAiOiJPZmZsaW5lIiwiYXpwIjoic3doLXdlYiIsInNlc3Npb25fc3RhdGUiOiIzMjYzMzEwMS00ZDRkLTQwMjItODU2NC1iMzNlMTJiNTE3ZDkiLCJzY29wZSI6Im9wZW5pZCBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIGVtYWlsIn0.XHj1VIZu1dZ4Ej32-oU84mFmaox9cLNjXosNxwZM0Xs"; public static final String DEFAULT_VISIT_TYPE = "git"; + public static final String VISIT_STATUS_NOT_FOUND = "not_found"; + } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java index 8200e7b34..405ce51e4 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHUtils.java @@ -1,8 +1,14 @@ package eu.dnetlib.dhp.swh.utils; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.common.collection.HttpClientParams; +import static eu.dnetlib.dhp.common.Constants.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; @@ -11,13 +17,8 @@ import org.apache.hadoop.io.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Optional; - -import static eu.dnetlib.dhp.common.Constants.*; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.HttpClientParams; public class SWHUtils { @@ -51,10 +52,10 @@ public class SWHUtils { log.info("retryDelay is {}", clientParams.getRetryDelay()); clientParams - .setRequestMethod( - Optional - .ofNullable(argumentParser.get(REQUEST_METHOD)) - .orElse(HttpClientParams._requestMethod)); + .setRequestMethod( + Optional + .ofNullable(argumentParser.get(REQUEST_METHOD)) + .orElse(HttpClientParams._requestMethod)); log.info("requestMethod is {}", clientParams.getRequestMethod()); return clientParams; @@ -63,16 +64,16 @@ public class SWHUtils { public static BufferedReader getFileReader(FileSystem fs, Path inputPath) throws IOException { FSDataInputStream inputStream = fs.open(inputPath); return new BufferedReader( - new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } public static SequenceFile.Writer getSequenceFileWriter(FileSystem fs, String outputPath) throws IOException { return SequenceFile - .createWriter( - fs.getConf(), - SequenceFile.Writer.file(new Path(outputPath)), - SequenceFile.Writer.keyClass(Text.class), - SequenceFile.Writer.valueClass(Text.class)); + .createWriter( + fs.getConf(), + SequenceFile.Writer.file(new Path(outputPath)), + SequenceFile.Writer.keyClass(Text.class), + SequenceFile.Writer.valueClass(Text.class)); } public static SequenceFile.Reader getSequenceFileReader(FileSystem fs, String inputPath) throws IOException { diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json index 5ec481305..ce80d6f4a 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json @@ -11,12 +11,36 @@ "paramDescription": "the URL where to store last visits data", "paramRequired": true }, + { + "paramName": "arp", + "paramLongName": "archiveRequestsPath", + "paramDescription": "the URL where to store the responses of the archive requests", + "paramRequired": true + }, + { + "paramName": "mnr", + "paramLongName": "maxNumberOfRetry", + "paramDescription": "the maximum number of admitted connection retries", + "paramRequired": false + }, { "paramName": "rqd", "paramLongName": "requestDelay", "paramDescription": "the delay (ms) between requests", "paramRequired": false }, + { + "paramName": "rtd", + "paramLongName": "retryDelay", + "paramDescription": "the delay (ms) between retries", + "paramRequired": false + }, + { + "paramName": "rm", + "paramLongName": "requestMethod", + "paramDescription": "the method of the requests to perform", + "paramRequired": false + }, { "paramName": "atid", "paramLongName": "archiveThresholdInDays", diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json index 6c59123be..8bf41f0ae 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json @@ -34,5 +34,11 @@ "paramLongName": "retryDelay", "paramDescription": "the delay (ms) between retries", "paramRequired": false + }, + { + "paramName": "rm", + "paramLongName": "requestMethod", + "paramDescription": "the method of the requests to perform", + "paramRequired": false } ] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index e2c2af852..4cc1c1e25 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -8,4 +8,8 @@ softwareCodeRepositoryURLs=${workingDir}/1_code_repo_urls.csv lastVisitsPath=${workingDir}/2_last_visits.seq archiveRequestsPath=${workingDir}/3_archive_requests.seq +maxNumberOfRetry=2 +retryDelay=1 +requestDelay=100 + resume=collect-software-repository-urls diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index 5062d562b..b89165fa2 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -8,7 +8,27 @@ softwareCodeRepositoryURLs - The path in the HDSF to save the software repository URLs + The path in the HDFS to save the software repository URLs + + + lastVisitsPath + The path in the HDFS to save the responses of the last visit requests + + + archiveRequestsPath + The path in the HDFS to save the responses of the archive requests + + + maxNumberOfRetry + Max number of retries for failed API calls + + + retryDelay + Retry delay for failed requests (in sec) + + + requestDelay + Delay between API requests (in ms) resume @@ -75,9 +95,9 @@ --softwareCodeRepositoryURLs${softwareCodeRepositoryURLs} --lastVisitsPath${lastVisitsPath} - --maxNumberOfRetry2 - --requestDelay0 - --retryDelay1 + --maxNumberOfRetry${maxNumberOfRetry} + --requestDelay${requestDelay} + --retryDelay${retryDelay} --requestMethodGET @@ -91,11 +111,12 @@ --namenode${nameNode} --lastVisitsPath${lastVisitsPath} + --archiveRequestsPath${archiveRequestsPath} --archiveThresholdInDays365 - --maxNumberOfRetry2 - --requestDelay0 - --retryDelay1 + --maxNumberOfRetry${maxNumberOfRetry} + --requestDelay${requestDelay} + --retryDelay${retryDelay} --requestMethodPOST diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java index 06e40ae14..e069e9655 100644 --- a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLsTest.java @@ -1,35 +1,38 @@ -package eu.dnetlib.dhp.swh; -import eu.dnetlib.dhp.swh.utils.SWHUtils; -import org.apache.hadoop.fs.FileSystem; -import org.junit.jupiter.api.Test; +package eu.dnetlib.dhp.swh; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.text.ParseException; import java.util.Arrays; +import org.apache.hadoop.fs.FileSystem; +import org.junit.jupiter.api.Test; + +import eu.dnetlib.dhp.swh.utils.SWHUtils; + public class ArchiveRepositoryURLsTest { - @Test - void testArchive() throws IOException { - String inputPath = getClass() - .getResource("/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv") - .getPath(); + @Test + void testArchive() throws IOException, ParseException { + String inputPath = getClass() + .getResource("/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv") + .getPath(); - File file = new File(inputPath); - FileReader fr = new FileReader(file); - BufferedReader br = new BufferedReader(fr); //creates a buffering character input stream + File file = new File(inputPath); + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); // creates a buffering character input stream - String line; - while((line = br.readLine()) != null) { - String[] tokens = line.split("\t"); + String line; + while ((line = br.readLine()) != null) { + String[] tokens = line.split("\t"); - String response = ArchiveRepositoryURLs.handleRecord(tokens[0], tokens[1], 365); - System.out.println(tokens[0] + "\t" + response); - System.out.println(); - } - fr.close(); - } + String response = ArchiveRepositoryURLs.handleRecord(tokens[0], tokens[1], 365); + System.out.println(tokens[0] + "\t" + response); + System.out.println(); + } + fr.close(); + } } diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java index d69f6ff1b..28210f1b3 100644 --- a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java @@ -1,17 +1,18 @@ package eu.dnetlib.dhp.swh; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import eu.dnetlib.dhp.common.collection.CollectorException; import eu.dnetlib.dhp.common.collection.HttpClientParams; import eu.dnetlib.dhp.swh.utils.SWHConnection; import eu.dnetlib.dhp.swh.utils.SWHConstants; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; //import org.apache.hadoop.hdfs.MiniDFSCluster; @@ -24,7 +25,7 @@ public class SWHConnectionTest { HttpClientParams clientParams = new HttpClientParams(); clientParams.setRequestMethod("GET"); - SWHConnection swhConnection = new SWHConnection(clientParams); + SWHConnection swhConnection = new SWHConnection(clientParams); String repoUrl = "https://github.com/stanford-futuredata/FAST"; URL url = new URL(String.format(SWHConstants.SWH_LATEST_VISIT_URL, repoUrl)); @@ -42,7 +43,7 @@ public class SWHConnectionTest { HttpClientParams clientParams = new HttpClientParams(); clientParams.setRequestMethod("POST"); - SWHConnection swhConnection = new SWHConnection(clientParams); + SWHConnection swhConnection = new SWHConnection(clientParams); String repoUrl = "https://github.com/stanford-futuredata/FAST"; URL url = new URL(String.format(SWHConstants.SWH_ARCHIVE_URL, SWHConstants.DEFAULT_VISIT_TYPE, repoUrl)); diff --git a/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv b/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv index 6477dd62a..568ccf482 100644 --- a/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv +++ b/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/lastVisitDataToArchive.csv @@ -1,3 +1,4 @@ +https://bitbucket.org/samskillman/yt-stokes {"origin":"https://bitbucket.org/samskillman/yt-stokes","visit":43,"date":"2021-09-13T21:59:27.125171+00:00","status":"failed","snapshot":null,"type":"hg","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://bitbucket.org/samskillman/yt-stokes/get/","snapshot_url":null} https://github.com/bioinsilico/BIPSPI {"origin":"https://github.com/bioinsilico/BIPSPI","visit":1,"date":"2020-03-18T14:50:21.541822+00:00","status":"full","snapshot":"c6c69d2cd73ce89811448da5f031611df6f63bdb","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/bioinsilico/BIPSPI/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/c6c69d2cd73ce89811448da5f031611df6f63bdb/"} https://github.com/mloop/kdiff-type1-error-rate/blob/master/analysis/simulation.R {} https://github.com/schwanbeck/YSMR {"origin":"https://github.com/schwanbeck/YSMR","visit":6,"date":"2023-08-02T15:25:02.650676+00:00","status":"full","snapshot":"a9d1c5f0bca2def198b89f65bc9f7da3be8439ed","type":"git","metadata":{},"origin_url":"https://archive.softwareheritage.org/api/1/origin/https://github.com/schwanbeck/YSMR/get/","snapshot_url":"https://archive.softwareheritage.org/api/1/snapshot/a9d1c5f0bca2def198b89f65bc9f7da3be8439ed/"} From 839a8524e70790440bb570c5093f0ab43e0df323 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Mon, 2 Oct 2023 23:50:38 +0300 Subject: [PATCH 04/16] Add action for creating actionsets --- .../dhp/swh/ArchiveRepositoryURLs.java | 5 +- .../swh/CollectLastVisitRepositoryData.java | 2 +- .../swh/CollectSoftwareRepositoryURLs.java | 9 +- .../dnetlib/dhp/swh/PrepareSWHActionsets.java | 177 ++++++++++++++++++ .../dnetlib/dhp/swh/models/LastVisitData.java | 25 ++- .../dnetlib/dhp/swh/utils/SWHConnection.java | 100 ---------- .../dnetlib/dhp/swh/utils/SWHConstants.java | 4 + .../dhp/swh/input_prepare_swh_actionsets.json | 26 +++ .../eu/dnetlib/dhp/swh/job.properties | 2 + .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 27 +++ .../dhp/swh/PrepareSWHActionsetsTest.java | 97 ++++++++++ .../eu/dnetlib/dhp/swh/last_visits_data.seq | Bin 0 -> 253188 bytes .../eu/dnetlib/dhp/swh/software.json.gz | Bin 0 -> 16127 bytes 13 files changed, 362 insertions(+), 112 deletions(-) create mode 100644 dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java create mode 100644 dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_prepare_swh_actionsets.json create mode 100644 dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/PrepareSWHActionsetsTest.java create mode 100644 dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/last_visits_data.seq create mode 100644 dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/software.json.gz diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java index 38db27baf..f02861953 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java @@ -1,7 +1,6 @@ package eu.dnetlib.dhp.swh; -import static eu.dnetlib.dhp.common.Constants.REQUEST_METHOD; import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; import java.io.IOException; @@ -116,8 +115,8 @@ public class ArchiveRepositoryURLs { // a previous attempt for archival has been made, and repository URL was not found // avoid performing the same archive request again - if (lastVisit.getType() != null && - lastVisit.getType().equals(SWHConstants.VISIT_STATUS_NOT_FOUND)) { + if (lastVisit.getStatus() != null && + lastVisit.getStatus().equals(SWHConstants.VISIT_STATUS_NOT_FOUND)) { log.info("Avoid request -- previous archive request returned NOT_FOUND"); return null; diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java index 9386b6876..296a4cce1 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -40,7 +40,7 @@ public class CollectLastVisitRepositoryData { private static SWHConnection swhConnection = null; public static void main(final String[] args) - throws IOException, ParseException, InterruptedException, URISyntaxException, CollectorException { + throws IOException, ParseException { final ArgumentApplicationParser argumentParser = new ArgumentApplicationParser( IOUtils .toString( diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java index c1a0fafa5..6232fa322 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java @@ -3,7 +3,6 @@ package eu.dnetlib.dhp.swh; import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkHiveSession; -import java.io.Serializable; import java.util.Optional; import org.apache.commons.io.IOUtils; @@ -23,7 +22,7 @@ import eu.dnetlib.dhp.schema.oaf.Result; * * @author Serafeim Chatzopoulos */ -public class CollectSoftwareRepositoryURLs implements Serializable { +public class CollectSoftwareRepositoryURLs { private static final Logger log = LoggerFactory.getLogger(CollectSoftwareRepositoryURLs.class); @@ -44,10 +43,10 @@ public class CollectSoftwareRepositoryURLs implements Serializable { log.info("isSparkSessionManaged: {}", isSparkSessionManaged); final String hiveDbName = parser.get("hiveDbName"); - log.info("hiveDbName {}: ", hiveDbName); + log.info("hiveDbName: {}", hiveDbName); final String outputPath = parser.get("softwareCodeRepositoryURLs"); - log.info("softwareCodeRepositoryURLs {}: ", outputPath); + log.info("softwareCodeRepositoryURLs: {}", outputPath); final String hiveMetastoreUris = parser.get("hiveMetastoreUris"); log.info("hiveMetastoreUris: {}", hiveMetastoreUris); @@ -70,7 +69,7 @@ public class CollectSoftwareRepositoryURLs implements Serializable { "WHERE coderepositoryurl.value IS NOT NULL " + "AND datainfo.deletedbyinference = FALSE " + "AND datainfo.invisible = FALSE " + - "LIMIT 1000"; + "LIMIT 5000"; String query = String.format(queryTemplate, hiveDbName); log.info("Hive query to fetch software code URLs: {}", query); diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java new file mode 100644 index 000000000..c0ab11bc4 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java @@ -0,0 +1,177 @@ + +package eu.dnetlib.dhp.swh; + +import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession; +import static org.apache.spark.sql.functions.col; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapred.SequenceFileOutputFormat; +import org.apache.spark.SparkConf; +import org.apache.spark.api.java.JavaPairRDD; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaSparkContext; +import org.apache.spark.api.java.function.MapFunction; +import org.apache.spark.sql.*; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.types.DataTypes; +import org.apache.spark.sql.types.StructField; +import org.apache.spark.sql.types.StructType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.schema.action.AtomicAction; +import eu.dnetlib.dhp.schema.common.ModelConstants; +import eu.dnetlib.dhp.schema.oaf.*; +import eu.dnetlib.dhp.schema.oaf.utils.OafMapperUtils; +import eu.dnetlib.dhp.swh.models.LastVisitData; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import scala.Tuple2; + +/** + * Creates action sets for Software Heritage data + * + * @author Serafeim Chatzopoulos + */ +public class PrepareSWHActionsets { + + private static final Logger log = LoggerFactory.getLogger(PrepareSWHActionsets.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public static void main(String[] args) throws Exception { + + String jsonConfiguration = IOUtils + .toString( + PrepareSWHActionsets.class + .getResourceAsStream( + "/eu/dnetlib/dhp/swh/input_prepare_swh_actionsets.json")); + + final ArgumentApplicationParser parser = new ArgumentApplicationParser(jsonConfiguration); + parser.parseArgument(args); + + final Boolean isSparkSessionManaged = Optional + .ofNullable(parser.get("isSparkSessionManaged")) + .map(Boolean::valueOf) + .orElse(Boolean.TRUE); + log.info("isSparkSessionManaged: {}", isSparkSessionManaged); + + final String inputPath = parser.get("lastVisitsPath"); + log.info("inputPath: {}", inputPath); + + final String softwareInputPath = parser.get("softwareInputPath"); + log.info("softwareInputPath: {}", softwareInputPath); + + final String outputPath = parser.get("actionsetsPath"); + log.info("outputPath: {}", outputPath); + + SparkConf conf = new SparkConf(); + + runWithSparkSession( + conf, + isSparkSessionManaged, + spark -> { + JavaPairRDD softwareRDD = prepareActionsets(spark, inputPath, softwareInputPath); + softwareRDD + .saveAsHadoopFile( + outputPath, Text.class, Text.class, SequenceFileOutputFormat.class); +// , GzipCodec.class); + }); + } + + private static Dataset loadSWHData(SparkSession spark, String inputPath) { + + JavaSparkContext sc = JavaSparkContext.fromSparkContext(spark.sparkContext()); + + // read from file and transform to tuples + // Note: snapshot id is the SWH id for us + JavaRDD swhRDD = sc + .sequenceFile(inputPath, Text.class, Text.class) + .map(t -> t._2().toString()) + .map(t -> OBJECT_MAPPER.readValue(t, LastVisitData.class)) + .filter(t -> t.getOrigin() != null && t.getSnapshot() != null) // response from SWH API is empty if repo URL + // was not found + .map(item -> RowFactory.create(item.getOrigin(), item.getSnapshot())); + + // convert RDD to 2-column DF + List fields = Arrays + .asList( + DataTypes.createStructField("repoUrl", DataTypes.StringType, true), + DataTypes.createStructField("swhId", DataTypes.StringType, true)); + StructType schema = DataTypes.createStructType(fields); + + return spark.createDataFrame(swhRDD, schema); + } + + private static Dataset loadGraphSoftwareData(SparkSession spark, String softwareInputPath) { + return spark + .read() + .textFile(softwareInputPath) + .map( + (MapFunction) t -> OBJECT_MAPPER.readValue(t, Software.class), + Encoders.bean(Software.class)) + .filter(t -> t.getCodeRepositoryUrl() != null) + .select(col("id"), col("codeRepositoryUrl.value").as("repoUrl")); + } + + private static JavaPairRDD prepareActionsets(SparkSession spark, String inputPath, + String softwareInputPath) { + + Dataset swhDF = loadSWHData(spark, inputPath); +// swhDF.show(false); + + Dataset graphSoftwareDF = loadGraphSoftwareData(spark, softwareInputPath); +// graphSoftwareDF.show(5); + + Dataset joinedDF = graphSoftwareDF.join(swhDF, "repoUrl").select("id", "swhid"); +// joinedDF.show(false); + + return joinedDF.map((MapFunction) row -> { + + Software s = new Software(); + + // set openaire id + s.setId(row.getString(row.fieldIndex("id"))); + + // set swh id + Qualifier qualifier = OafMapperUtils + .qualifier( + SWHConstants.SWHID, + SWHConstants.SWHID_CLASSNAME, + ModelConstants.DNET_PID_TYPES, + ModelConstants.DNET_PID_TYPES); + + DataInfo dataInfo = OafMapperUtils + .dataInfo( + false, + null, + false, + false, + ModelConstants.PROVENANCE_ACTION_SET_QUALIFIER, + ""); + + s + .setPid( + Arrays + .asList( + OafMapperUtils + .structuredProperty( + row.getString(row.fieldIndex("swhid")), + qualifier, + dataInfo))); + return s; + }, Encoders.bean(Software.class)) + .toJavaRDD() + .map(p -> new AtomicAction(Software.class, p)) + .mapToPair( + aa -> new Tuple2<>(new Text(aa.getClazz().getCanonicalName()), + new Text(OBJECT_MAPPER.writeValueAsString(aa)))); + } +} diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java index eaff5ce02..5e705716c 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/models/LastVisitData.java @@ -1,15 +1,15 @@ package eu.dnetlib.dhp.swh.models; -import java.util.Date; +import java.io.Serializable; -import com.cloudera.com.fasterxml.jackson.annotation.JsonFormat; import com.cloudera.com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) -public class LastVisitData { +public class LastVisitData implements Serializable { + private String origin; private String type; private String date; @@ -49,4 +49,23 @@ public class LastVisitData { public void setStatus(String status) { this.status = status; } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + @Override + public String toString() { + return "LastVisitData{" + + "origin='" + origin + '\'' + + ", type='" + type + '\'' + + ", date='" + date + '\'' + + ", snapshotId='" + snapshotId + '\'' + + ", status='" + status + '\'' + + '}'; + } } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java index 46d512dcb..9c145fc19 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java @@ -1,50 +1,21 @@ package eu.dnetlib.dhp.swh.utils; -import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.http.Header; import org.apache.http.HttpHeaders; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import eu.dnetlib.dhp.common.Constants; import eu.dnetlib.dhp.common.collection.CollectorException; import eu.dnetlib.dhp.common.collection.HttpClientParams; import eu.dnetlib.dhp.common.collection.HttpConnector2; public class SWHConnection { - private static final Logger log = LoggerFactory.getLogger(SWHConnection.class); - - CloseableHttpClient httpClient; - - HttpClientParams clientParams; - HttpConnector2 conn; public SWHConnection(HttpClientParams clientParams) { -// // force http client to NOT transform double quotes (//) to single quote (/) -// RequestConfig requestConfig = RequestConfig.custom().setNormalizeUri(false).build(); -// -// // Create an HttpClient instance -// httpClient = HttpClientBuilder -// .create() -// .setDefaultRequestConfig(requestConfig) -// .build(); -// -// this.clientParams = clientParams; // set custom headers Map headers = new HashMap() { { @@ -64,75 +35,4 @@ public class SWHConnection { return conn.getInputSource(url); } - public String getLib(String url) throws IOException, CollectorException { - - // delay between requests - if (this.clientParams.getRequestDelay() > 0) { - log.info("Request delay: {}", this.clientParams.getRequestDelay()); - this.backOff(this.clientParams.getRequestDelay()); - } - - // Create an HttpGet request with the URL - HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("Accept", "application/json"); - httpGet.setHeader("Authorization", String.format("Bearer %s", SWHConstants.ACCESS_TOKEN)); - - // Execute the request and get the response - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - - System.out.println(url); - - int responseCode = response.getStatusLine().getStatusCode(); - if (responseCode != HttpStatus.SC_OK) { - - } - - System.out.println(responseCode); - - List
httpHeaders = Arrays.asList(response.getAllHeaders()); - for (Header header : httpHeaders) { - System.out.println(header.getName() + ":\t" + header.getValue()); - } - - String rateRemaining = this.getRateRemaining(response); - - // back off when rate remaining limit is approaching - if (rateRemaining != null && (Integer.parseInt(rateRemaining) < 2)) { - int retryAfter = this.getRetryAfter(response); - - log.info("Rate Limit: {} - Backing off: {}", rateRemaining, retryAfter); - this.backOff(retryAfter); - } - - return EntityUtils.toString(response.getEntity()); - } - } - - private String getRateRemaining(CloseableHttpResponse response) { - Header header = response.getFirstHeader(Constants.HTTPHEADER_IETF_DRAFT_RATELIMIT_REMAINING); - if (header != null) { - return header.getValue(); - } - return null; - } - - private int getRetryAfter(CloseableHttpResponse response) { - Header header = response.getFirstHeader(HttpHeaders.RETRY_AFTER); - if (header != null) { - String retryAfter = header.getValue(); - if (NumberUtils.isCreatable(retryAfter)) { - return Integer.parseInt(retryAfter) + 10; - } - } - return 1000; - } - - private void backOff(int sleepTimeMs) throws CollectorException { - try { - Thread.sleep(sleepTimeMs); - } catch (InterruptedException e) { - throw new CollectorException(e); - } - } - } diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java index f58705188..08400f28b 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java @@ -12,4 +12,8 @@ public class SWHConstants { public static final String VISIT_STATUS_NOT_FOUND = "not_found"; + public static final String SWHID = "swhid"; + + public static final String SWHID_CLASSNAME = "Software Heritage Identifier"; + } diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_prepare_swh_actionsets.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_prepare_swh_actionsets.json new file mode 100644 index 000000000..07ab0b1f4 --- /dev/null +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_prepare_swh_actionsets.json @@ -0,0 +1,26 @@ +[ + { + "paramName": "issm", + "paramLongName": "isSparkSessionManaged", + "paramDescription": "when true will stop SparkSession after job execution", + "paramRequired": false + }, + { + "paramName": "lv", + "paramLongName": "lastVisitsPath", + "paramDescription": "the URL where to store last visits data", + "paramRequired": true + }, + { + "paramName": "ap", + "paramLongName": "actionsetsPath", + "paramDescription": "the URL path where to store actionsets", + "paramRequired": true + }, + { + "paramName": "sip", + "paramLongName": "softwareInputPath", + "paramDescription": "the URL path of the software in the graph", + "paramRequired": true + } +] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index 4cc1c1e25..651bae337 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -7,6 +7,8 @@ sparkSqlWarehouseDir=/user/hive/warehouse softwareCodeRepositoryURLs=${workingDir}/1_code_repo_urls.csv lastVisitsPath=${workingDir}/2_last_visits.seq archiveRequestsPath=${workingDir}/3_archive_requests.seq +actionsetsPath=${workingDir}/4_actionsets +graphPath=/tmp/prod_provision/graph/18_graph_blacklisted maxNumberOfRetry=2 retryDelay=1 diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index b89165fa2..7aa667a4a 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -57,6 +57,7 @@ ${wf:conf('startFrom') eq 'collect-software-repository-urls'} + ${wf:conf('startFrom') eq 'create-swh-actionsets'} @@ -120,6 +121,32 @@ --requestMethodPOST + + + + + + + yarn + cluster + Create actionsets for SWH data + eu.dnetlib.dhp.swh.PrepareSWHActionsets + dhp-swh-${projectVersion}.jar + + --executor-memory=${sparkExecutorMemory} + --executor-cores=${sparkExecutorCores} + --driver-memory=${sparkDriverMemory} + --conf spark.extraListeners=${spark2ExtraListeners} + --conf spark.sql.queryExecutionListeners=${spark2SqlQueryExecutionListeners} + --conf spark.yarn.historyServer.address=${spark2YarnHistoryServerAddress} + --conf spark.eventLog.dir=${nameNode}${spark2EventLogDir} + --conf spark.sql.warehouse.dir=${sparkSqlWarehouseDir} + + + --lastVisitsPath${lastVisitsPath} + --actionsetsPath${actionsetsPath} + --softwareInputPath${graphPath}/software + diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/PrepareSWHActionsetsTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/PrepareSWHActionsetsTest.java new file mode 100644 index 000000000..ffcb7aaee --- /dev/null +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/PrepareSWHActionsetsTest.java @@ -0,0 +1,97 @@ + +package eu.dnetlib.dhp.swh; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.io.FileUtils; +import org.apache.hadoop.io.Text; +import org.apache.spark.SparkConf; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaSparkContext; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.Encoders; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SparkSession; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.dnetlib.dhp.schema.action.AtomicAction; +import eu.dnetlib.dhp.schema.common.ModelConstants; +import eu.dnetlib.dhp.schema.oaf.Relation; +import eu.dnetlib.dhp.schema.oaf.utils.CleaningFunctions; +import eu.dnetlib.dhp.schema.oaf.utils.IdentifierFactory; + +public class PrepareSWHActionsetsTest { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static SparkSession spark; + + private static Path workingDir; + + private static final Logger log = LoggerFactory + .getLogger(PrepareSWHActionsetsTest.class); + + @BeforeAll + public static void beforeAll() throws IOException { + workingDir = Files.createTempDirectory(PrepareSWHActionsetsTest.class.getSimpleName()); + + log.info("Using work dir {}", workingDir); + + SparkConf conf = new SparkConf(); + conf.setAppName(PrepareSWHActionsetsTest.class.getSimpleName()); + + conf.setMaster("local[*]"); + conf.set("spark.driver.host", "localhost"); + conf.set("hive.metastore.local", "true"); + conf.set("spark.ui.enabled", "false"); + conf.set("spark.sql.warehouse.dir", workingDir.toString()); + conf.set("hive.metastore.warehouse.dir", workingDir.resolve("warehouse").toString()); + + spark = SparkSession + .builder() + .appName(PrepareSWHActionsetsTest.class.getSimpleName()) + .config(conf) + .getOrCreate(); + } + + @AfterAll + public static void afterAll() throws IOException { + FileUtils.deleteDirectory(workingDir.toFile()); + spark.stop(); + } + + @Test + void testRun() throws Exception { + + String lastVisitsPath = getClass() + .getResource("/eu/dnetlib/dhp/swh/last_visits_data.seq") + .getPath(); + + String outputPath = workingDir.toString() + "/actionSet"; + + String softwareInputPath = getClass() + .getResource("/eu/dnetlib/dhp/swh/software.json.gz") + .getPath(); + + PrepareSWHActionsets + .main( + new String[] { + "-isSparkSessionManaged", Boolean.FALSE.toString(), + "-lastVisitsPath", lastVisitsPath, + "-softwareInputPath", softwareInputPath, + "-actionsetsPath", outputPath + }); + + } +} diff --git a/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/last_visits_data.seq b/dhp-workflows/dhp-swh/src/test/resources/eu/dnetlib/dhp/swh/last_visits_data.seq new file mode 100644 index 0000000000000000000000000000000000000000..683fc0e693c3174f4d863d95a31361c6651565ca GIT binary patch literal 253188 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 n%aclTvpNM%y7;aA(0%AKgUBHGS%+htt1zt z7ou{;B%p4*c;eg`Q+$vdJ5hx+_3!_p}cLoPYbe6c9`CBi%}Yyvkc z0^w$=oN^{zjs zdSUixw2Yh<#f?!eHoKzo=WgNUC5j?Bk_&a8+gYllJxp_?69UUO<%k}f%3eaWNMdqu`Vq6{ z2l{AGke=jV$LnHeC*+I)&$c7e8@V?xJe88yl&)Y! zUf(yKma&(E$}UL`8A}IAl%>#+LdX&ZV`*j#!zfE7LWG3K)?zK&pbViw%GzehTF9Ec zq9n@i{*3cIr{8($zxn%qedd0i=f1D&eH~U;!A6Kyinegr==ul=9wGHk+fH^o5n!=m za{Nl3rf;(OTKx8?orkAtD1B6qdg_S~W@n1l?dFYT5?s(`NImkK0xSAfE+TaC^Opb^ zHartdf?DuGE!I@yI8gxWM52%(kA>*r<%M=2z#0b;2!m-GL?j?WEX<#NU*E+#_JRIn zqkIg}n$H;NG*{=x))aC}tAqXs`}B=Fn22``8=XU)F4R-J4re)~CV zL9M<#biOTYu14>#z4bx~#m;Rt?tNQy9^B1JkF#$tF#cB5|9#?yQr@^50U1^N>dfP)Q$$|foKEY_jz&yf$_Xb2+uSwJXKX+J(H|NC zG4%Eg)7XX>nd%QX%ca&}p69u@yY<)g?TYa;3GVU#oJPR0DMh=FDxP^<>us*YoKMV0 z({LsA0;>oG?ii(!-XYajrT$qG$HZ2QerRL~zAm9SoJdxm6&$@wweC+U@chC4s3+04 zAlwK(p%ELaw1SwFK%b3an4?53X&}4cfJ~8T(`uwzoJ<>WvWj$I%8oDnNh0kKZ z?fXgvIM7b=w|@~F5e_q1``BlkRBH+r>|Md>??i%!1QEK8t1&~2cv_w>P=;7B-ln=bntT5+V5GS4)!{Tt0_uA$yZJ)8W6 zeL|^b#>P+*tO&^dZOd0CoAEz!&(@H{+@?2rIqhR zoNxCR(x!28zJwe@j!GWx-n)Vnb3rRSKJs<3VE%rm@Pl8iY(;L?o?)3==LmbO3+O)M zQ!@tg(1w%u`s&Z}JtqVLe`maQW7z>N_coMn3@M`X_sn}9ghw?)e*>!1KB;7y6e^+ zt@!cJwZH7Ebh9jWBo(7zUqzU7Q>)g$QagaIeAYSbv=FEK3X%hFiYuU{Y5RuR{ygz< zlk9eDf{|+@CHIVk)yYAil5B(6p8W~E7eqqv2a}{iiz=}%VrI?PKf(*(RB~wb-spVm zUL8xk-^&|QW@%Eq=M6gPdkZfIiJPGKvR)-jblUIBeqZ-AL-uKG?p#+}#ZlQZ-o|t0 z*BXNoce*pcBp;&&oR(L55qc8>ojB|_!N1XO6QUtFO~5A`O64t_kuyb?;q&tx#e14q)u!`uRZCCbrQ((n z_KeDlC0bX@pMSKiN@Nxncg?kW*D0Sj6)6@7G z%>pieh)!Jftfx>B00JZ@w)6VWzG}Ls=@^B&v#?i&h|T87OA$3MAC(G#fta0Yj=^dF z`LyR^Cc>0Dn)P`ZhAY@O|7rzu4CTL=KXQI?BMV) z<`VN}n|ovSZ0fJ4?t-HC*2BVSKz4dV zs>c?f8!zs!`h%8RfsJNvyH{Y|HRHF_!S&?yJq~-K!GSw!Cld#o$+OLY`%{pe9>PaG z`wUF{wcOzreh+X-x)9p>IPy|sH#usv3u^#;Dki-(79q@wE2VH1o^s(TC3#DP!&`}g zg;;agr-$|RF~)g<7zNbEz=*(+?ZKMiusZBpz0n|gln7!>*FnwBE9%8Tf>xMGuxQ=@ zHLbMuDvt321ko(5#*^1eiQL3qa*;yWh4Y$qM!9aWTJrGy`m9$C#ghHZIbl>~TSuV- zY=GaVVM+1+Bb(SB)nLVM*_7>VI3{TsnL4#7o~D_1LQEfj|3L-i7Io1sf!p|9Ew5lF7TfK8pPT(|qUyUF{X$q0;56+)uh9z-B57}?>ixf92Ln@1N* z#L4NBNhG2%;w=0nhZGtxhA_GQQmg4WJ%ZT1t?T;rIw^_ONb$tgNby>=(JJ6CUj10Z zIkOA=mkBQGQ1wGS!}+)0H{U{!*>HXky9bb z6Dunq)_IJGb*SG$s-Y?pP?a^G2YMFAF-HwgXd3Iw9XaQ90@wyXee*KGIWum3$cLlD z5HfYS8B6vRX$5Qx9%>M+yn~Qlwk2yh3xU9ef^b-YD+yuVkl8%g9lMA z1_IU06Iq?D9QZO{detMkm5tilCnE>Yp?ZI8P*;7$M&p#?YAd$e)`+tqOF0F;8Xi2V zm}G9*U@SXPe*Y!1%G>=%Yux9lGZRbNeFHbk#i-blz1H1Lw1Okp7U?#=yS!&Ty-Ji+ zsFuN69f@hzKD|zueU6=GnS;1AH(T&u8WbJke&SMINWlr1LC6zD7Ns@YB<5SK5D*}` z+0cgx;_ZjseaA&vS18Q171U3mNT1)_co4PInkTzGg^b6kD9gcsolLSN|NS)oWxmc= z$oWmETXzG0wR8d)Ex10_RgMI zw40;*e`B@lH2J3B%!;hx%LO~iu5h6CNPfWnJv#)<3LpT%1lGalf9modJah3J0bA6j zX%n5|910vHyYp#ee%cN;4>qYn`5frZ&FxH?(nbkgm3p{|4#i`c<(4!?8V%5%U{c|6 zd(6+qMZYbn{|fDFi8=?SGp80+glEn7>PQpGBxc5(X*cdQ_=Kxi7@_PJV&KLQ1mKfN zkuhe@@E^Q}1<)j&!`;XV@;>JIEgOi8NQMfPbHE#0UNJ>@V|-7id2TcYQ=uNeRZkL~ zkQ_piryPbtaMdD_7}4~AhH84BXDBP6VPCX}YENGFC zQ?O8WQAhC#f4D)4JzcmB9#vs9m8E6ft|(L{p)Cq+);?*rC*RSe^Rjkm&a_peK=S#l zZ%FcP@cz7bsVzIOvYmtGL+IcU!(fQodw@v)Q+*+r)e+^ZfF%Duafst|yPI9c9t0xhV@CeHrqN9wD>7q~g ztU(<^8vL3&3_asxnr`UhWM?_XFQmUSMzf3t7FNILVL#hdbU&KQPjT1k7O<5KL>fp8 zo3!3Lri#T3pTGK-%9hlMB+gAbXC;bTrjNe$CHGpj$oc*JR_S+hF58aE)7({~d)y#_ zUohGr4fE%b&3i+?9{KY$>*;+*dgCd3W|nkX*r(ECQK!6JSmCr|o_2Ag2H8UB zoX-Hz0MV@{eN5O2p6(e-ntOU!%hI7|8usjy{0S=G9cy#SrNevlPQED0QO#^h&k=mq zYbc=+5Kbt7Q4cYTO@8BM__Ct2m{Igz_rhRH>@=;2f#)J2PRS_u!<8_FO;j1Dah&P zxUA28JLtTULLH4)&++&@X@~~}^0y9ABHn8;V{!EHtmhoKOAN(M-^5*ak=nLnSNLRC z`637IWz86?ZZ)^$^E_*z<9n$?fXQfz{^{O zn>CzB3!|Oyi7(n>nVq(`y(z^VY&qa0dfxDIO{poH`%y$MxBa=;xxno4mD%s&?@e)b z3#5wI!QY`CvhZJ8leVu*AlYLHhHzc-)Px%)q_;VEQ6St2#)eqH8acSwk^xnh`b$pl z{aCN@the5Sbzw`xUWS7APv<-pzT--*#(%QRSf3%x)VSU1=kiKb|Lj58omsC#V+-x= zZb4A*hAmffD^zNR_=Ka5s~GiVyRjGu%Y_Ae+A2Bhrm{JQq9Wo4@GGf&su_A;mwUCkxmH+!FIJ;$@Tlg~NFbACAxiNY$%R?QM|-+Tu& zz8C2meC^A^=Fa{Q<-V|l;_5tpdQU>_Z|(;O(|XOB8txp#r63d|Ie6IOAtMBu8=9fn zutk79p$M=?@$+pNynhsZ%QF+92a20%t!{BPSmti2u|m%8U6(Xi=8sm*0&H?84Aq`L z3BT^sxUHI5q?7LbiDiDqMRjNjdu5!M8tu@7PUFsw-oV~#Hn=sBEicpTz@s;s6f@vz}19NX?ba!ELWiD!SZ*BnXeQR>#IJV~hy9$IOZgf?&De);$ znwpMq%66u2*-p3RteU>v6%LRDC6q~m#fNQm-inw-%mOB2e$3R5`8BhH+{vtD&H<^1 zW!aW9oigo2cV(tXf*^2ka9;Ru@NtWl#aLup{l9RZId@#@FWyj3}yVByv;pJbwT4F;pe*)vqR^AoP;*hP&t;BocDlB2`Ig=5 z1&-CVSW?F4S%z}GHmRN{v`E?R{UQ7(Jwi=<%JMMVv~$hcP)lKApQ33n#bi%XW# z5v#5OTF?U^Ez%`m5ram06QDKASmHyOkVZM%-kRM>Gk86r^4Zo_{UBq(Ldi>>b0pmK z&iobXR}8Q?1opqW4BmuzWt-qGw5hz6g$Ju>r8?Z{89 z9-iB!V2p~a%z=JedaH+*&;%wz9LTh&dizn)>JC~>S<=*vuBN-1Zp|rJv%c&K6fiAK z?`eAH%rN?f)i?B(Mhrg2eL@o&K__#3C5B$X zeG$X`d@PDlnUI|QRsv~xG9uVSH-O|jDp=S<=w|hc)@guN;$KjrwK2&jbP>+ECc}gok|99wQ)o2_@x#xAJ{$3qd08&QZ1 z2y{u4Le9%N3Q&axZpS5lW*(twIt?N$b>jBZ<*pzN2E1VRhbeBClFiJ^sJPvvd>$8B zY)gK3xTLD-9I6tL81Xy80Jlp)BkJZzD&?j3EAyW~XS`yD`>mu=Eoc^73&QhCWXn;T z`Q7CP)ucYJY6zzxtOrK$;kOp1#L2?qLjL7yPK{{;#&kGa15yL}DhBjuzR*`e4GLaF zOiAF!4{l6(LMrMl8PAIk?o{753#`=LRgs0k+o{aQV=y1VM*f$N>q2sl7?_Gz+vG?@ z5k@wrx9iLMa5vSfa`NBzg&#!@Hp>OyCcE^K2lH=CFPFYyE`DSBvI6)hfR6(BY;={@ z1>aUP?ezdY2Kv8l?@b9nllywDFk8o2R8@!7QH-W{H-#Up5nC*)`mb!dNPdWkc{H3S;?5PBUA&V>_Kr zSFxJHSSo;0vsg@FEI)_G6vk3vER_gT7|X4D*O)#NWI6T4Rv;`NjK(|)VR^bVqNg{< zae2fP#&S~_%Ylevngkjy%aEE07rU|Qxx!g~+f4UzAY3&jkC7Z0FW)91qXoutWP*lR zfW%bj%U3eeJO<|i3lMglvW(+mz8c$CF}6n{0xJbvN5T^@uPPeDY))x0zM_+AHbbqL zzeUaG*D>aA74Y^62KE_%w{zIfBT%=)GDVthlY_zV_@tWFTQgwn&71c@aO-*=#Nd9x z_so^wQ)pa;##LzC&E`xpHDgUQuGM$UR;TkwFmBIKVBF6boCWDgIM`<7ig_|}Ak*q$ z!o6v*%Acj-^TC4SI5hC9%xOfRips2m@;oEH0E*?QfKtimcIlZRh+?lwyvN8De1-rK zgkLEEk{4wFRz`~)3;q?`wzUwGLgNn+6NCn zepgjvOY24ujQIugjDdxi_IHAv$=SSb(`G9K@@F#}hE@CRHW-RYP0d!CPyBI<`Yk_c z7pR&zjat9v=>`%7ISY|U7aS12}>DNv!<8bd(^0Z}Nnjq{j7u_+Xr5@8C(wsl`@_H#jO z3+~t|AlpMhnMVQHmd5nvKrRoNg0XE1#->sQDhS(S81Cf&A&!7?RZh9rGMc-`I|S}e zVv(g|p2uofU&XM#{ds37*B2*hKwrgxD#+otH|BdY+%Szuz;ZsS7WQl0fI=d^G$i5z zjPFrs#H!dfd0Pr?FlNb!{H>(P9<&sl+q%%|hxdUK>#skE6?}iPcT9%wKb#KsmT2`p zWeFMH^iLF8@LQT)3OMn(sefF}So3gRy>E9~J?l|$LECOw9b3T#A3r*~KoKYea`cpD zj0{*5k+Vt4YB1jro;qlk>}S6OZP=sx}|KM z5lw#OG#Pm!FGSKtG7lnrSta#UNj)1yrD+bvJF%Irp&O8wPsMv;!9S z4(nN4>tZuEW}Yc@<_qG#n?}aBfXV< z^(Zi_%F5VKI4f}QMIq9AAg!xQC|K*a&3G>dY$F%+V1B?8g_(ODLn@bN*=%`Mjq0ly z)iZi65)n@n{OyIo-xe3^A9DaUG^l_-EZZh;xEP2eDE&fYrv zDpx7<6bMd%;5MB-$k2@LdLTHvZ9yK^;W1f?=lAr`%2)Uc(w43Lf35+ zU3VvBtmk_)@Lapw)y=LST6)K1j%gYl)^n^LbGFunjHz^fit+AO$OO0G z!i{a+7jFMd*x2VpuWtHq3OUxC0Kn6o=SvQ`m(gsp)SdgvDHPea&4e!poH9G#cY55C zLaDqkl*;1b*W-XH15lG0lOUCt1;8f}!J(E5)IO4C4pUkE{63&cRk;GGD3FQ*scd>p z%+ky?K`K_?vRZD>eH2Q?X&D`Lu;XWp&R{kYh&R~*Cq+Br&{B_sXgvWom#7ta=X-Uz{@W|7vzDF)wj|N^#NYh)XnQd8=+^_d6U@`UO^&` z=|2nbSX*=T0p%io0S%CbB@x+(COnU~36AO|FDBb7Yd{1}HeyAM<4Lsoa{7KQ>3Vch z^~a|vzJwi;r)Ey+*dixY`+0rvWWHG%oHp;Y7Y(72quJ5k#Z)}YhBfwel6 z%21JO4RQ_tdUy|Pj0UdBdJgURUE4MN(6aoHIX!CWjM{of-%_wK1si)B6Zui0zN6=Q)4KoPy-71v~eC&z$XQKQX)(NpSJE@!g(s> z(;5Mo%6WSX;t4~EHwU!3qfk!`Kt1hF*O;(xG+OG!?WYRi^ldZT%Rx9D2^hEe3ZV+m zzlu@)iDxSF+3Ok3VN9cl#MPOh>ZHgQ2SOa|9GyHiOI8$!ga%@p>>-|oEu0KFzj1#m zthxquAf$pBDwyGB*HBGE(;aX@*Mb@9-M((OjBamzm?6B;ZFTE66wL5Zqq9S*v)oAS zl9TT+UsBdeC*RjFJx~KDlYtnqq##kXYego#LNMD{#L}|Ts-hVK34=MX5L%Zrlqho` zp+vrsVt&}`3|!zv$M4>U`HJdsQB+8b@MGic2uUPwVC4uWO|b7w}D6} zM>lNUwG`4xA)T7BYO9b=kLEO^qZ>U1bW%Vkg%W5Oiz%R!0y-(6Q^VLxG1p54I^75& zKNZkvjlfmyygdr(RK?4dz)sVf3h&eaywlGjy9gt3MQ*2YdidzTN58DlQr|Yaz8oM1 zBK-*Ue=C9PlDrCE^BRWrElWgu5x0RgwET+adHa$B#dDt~55S=WG??;? zRSR~3<#qj@Vfle>GcRPm&U(zZOyBE-f#doM9?Dx@yemB^$nL7l3j@9D=oY2a0At?p z7z?@q^Lw@#TA{sA*>nw$yki+J(2##Vj8(vbGSw=3OM}*OJhH6F7}GATCvZ3CGg5wN ziGL32+KD$I$V)p1r+Z&(Cb|!Rh79A(F#CF^uN$pS&t3yGwCar|(9leD?wlR&ydfcv zSU!WI?CuW+XtHO5`eV#)m4ka(;bewm>d}N?&Z&reXX=Lg{Wdu+{fKd&?D#y$$te^^ zq4u?bDhu!nIfUm|;km9G{l0GLu5CD>WqCTdv7V!MZO@@yC$xRfm$Ro~@Qb@q7Ed{q z@4yOtx#(#QP+9QYgksA@wyX`!)-(n(t|=vtlk_rim9usgA8fD9$%<)}0R5^0G*6kY zA%l)W-&fqGL}Y_Sq~cX288;^xDfmwGl8tCPVPz)rNm7ipI8=bliVCxrmEMP00vu@aC?7LbZ*m8^UQ zS?O`%iEEa(v(Ue>goEqcTD>l*m{3X1=1R^*7FMjP(;wkCdvXIjRZ72ZN-7&PbEnkEJ^R@ZpFMxxPD#XI=0%L;xi>An=K?2mnvzv0ci-d z_bxe}ah~G5uN*foL!;Nz+m=F&E2Z3QrL1^WNyg1d#x&;F?bH3i zO+N;1=(^EW9*h!}%@&q3_EmhU1m$x=FKTDTa81RDiW4{KMyF*IwGG=*e5d&CtMc9W zpA*fedHZO8FjUN@%)$nng^E>`Tx>`dQ((}L(L$b)sW?$_;zoRwwkM+EW=Kc3G?@{* z%J!YB(2ioqXLgxyaHp>Eq{u{2O7>LTskrlb)%K0#<89q^ds-oQs^NJX7m7C(Z$7)^ zyfj+6(&~ygH_e+#<}Uyru@XFnMnomHyh1=?1qolW}KEwaRatK*8zS50l{ zR;FW6?=Ylr+NkKyW2&y?JwI~c)!GvxzX4%32wDD zV8FmJPgt;`!P9D1H+KH?cl=^rlxaWnLv z#F)`s=B1WbmCUEnK~P<%y#A3fAO=keta*B;8kE}{@!|N#q$+%~y{n$zNxho|R+6qN^80*(BhQj4RftGPHRB0SNM5F? z$Ow+R5Zz71ySDa4+e+a>yx2RBkDzr&?F#xVaR>Xl7iGk({2#$PdVA2 zQVm|tXf~0YwBv(m2~sASkTdFEL=`I~6Y@p`6S9X|Gy~fCWLRXSk2&>$`2>74Za6XH za2;A6(rm;uBqSq8tf1Nsh~r7lbA0-)jA;V515P8DfFdd2-vPYJuE{AVD;ni6J9$bo z_&S@>7Ye=4Ml|97l#JPIGVSgU)*#b6MvIuH+S`%`Fvs2i-+^@!kX^=7ph$9AXB;KR zmDQL_-rC2RH-X8Ey$x&g0;|Re`ZJG$W|JmfZ#wQJfmR3&{vkgBb%b^%v|-AaKPJOT z0$oLJDs!AzZ&|{!s>M6OCChS}A!|*E)@*WQYfUJeL?_@)HtJrn*(M z`)u017SiFLJ~>DVHo{2;REVckLKRud^+JdU|0X;JdSa_*O~Tf3kD>2D!JVrNfL(Ewkh5`_1T5>5GagH;pup2na(ulG3gG)EbRy|77}F$Sugk*Rt63Ty zRaT5eP&peo#Sa3uWbwQYLv4UCZgAA*XLd|Q!r?TwJta`H>Eo?MpIs37Tx!7G;~f&9 zhnBP7N|yLiNJ8!Z@4x=HG4xz3#+fKbTeFx-LOZdE!WC*w&(Ujq@&poe3cMgn8G z)rldO(9_ylsF{Ib(J{Nmay%dhsfY!mpjZm{e@?PG0>TSaGj#v}!%AbGH($%HN2j5P z=!lPKKIfQ2kuWlKy++GyQ_k7Oo3Ty$TG(b#O9JTZK@^psfanUzfzo&^LudHC*U;}b z(7|y`vkUAc-0(#VLX+0PKG|(;E+x_5n5m_NT^HJ@j{Cr~B~jdL>a3@bcUev|G|2^M z8!)r0NPLbLC6vSz+A$>}twLYmcqe(vGa3lOz*32TPkN0f!#c}0C{#b>)Z-B^Cerx0 zf??0cJbe}2n=dfzG9Qd6xgLNlEj{ToP$U645fWuDqvKf)bK^VeK|zp&-#42E)_QSp zYnSX(P>e?+VA2EK&jffI9DB>iDZRoqnS|^%er99(fVIMEF=%qKFGP`ok1cN=3ZF_2 zKS*%-tv0n2xoV_=hG00S3w-n{+>HUKc9zRdPJOMH>|m`m)34Fut2``zjTSH{dfY?P z?B3zY^K10(edYU2#hEWfH5htw75$A65rY|gBBBYJcp$=t6I5Vzz!2YrG0!U74%osc zLsry|XtT+)=HPz-n-1oB4BDE|G!xf6rau4le-Z+;IQsNQAwY5O9_*47s6c+)J3VN8 zx7P_Ml}SKxMx&RPp{-H?zvCkrC!iBhYHwI1t_Zzr?vwBKt_zmn`sh!f4nSZuT<5LH z5Yw;{1_UZB$|(z2h~OzKx(^o!AP8O<0%bkEID?6x?eYp@vKVZ8f#IVy$NR@bTw$NC z#$cWog3$b;53j)lA^5eRjA}0kze?t(jmn@JSb+oz`4_mv0*t{itbia)xPZ>qi{E+5 z{8^Z;;aX))K0(OQWvxUCA@ORHnfO@dlRg=M0Lh@k6+~nk;YzhO+9sLsV3s8;&$mfF z0nZZwIMaeS3uz*fNem`f^H=QFgS!pO?2=8e#G6R?7c%dLT*#G@xb~>}Ab9g)n@G%C zQdFeaFC`@DECa#Yd`4P_8mBP!llkI&AG|FZAXT(5V+^c~JeMJCX^!ku9%28X4t(#` z5I_PTc@(jrPj(Ul25E-p|&F*x#9*Ag%9F}W^L=~&E%gbLc#Oeryw%5 zGZ8TffI)r1lDy1sAim$tcV5E_xbwQ%bii~3>v-_3EOi;mzWGZSuiRn1&0YH|qrp7xxFqz&9wY5*IcO65JS9FOnm! zo1C%{ZbiX(`6;XSq>Oobzm!2LuOz^Y2b+*9TfhJE`@ekLqGd4_S+$|c!yRo6*vlEP zQdm6NsqtA*7 zy0lAct9~iNU8O}<%1SPwgcr@Lo!3@DwwCrAPI4aO9PP^wE%Dz$T|4n61bK1i;B;>V zBhEjuQ=!6Ea^vk%zzYS2L7{}+a`6&(X^OCB5ZTx*Q#UP5?`ei{W|)1w)7OobZgu{B zYg$@rm)ZK+18=aI9+HeV9dT(Uf!xgmN$Vvy!qLVd|JXt{(yFbJWH%G3C$9goRf%&N zV2pa9QW7$XVU)Z@4hmU=#3m3lzkna4(X?26_OU>k@=zJr>|++Cs6`njJ76_{41L** zfPt1t$bD6b$7*1i{0PFa^KIglJSvFXoR(ZbjZ%o$A6K=a&-~-g@izH)u>S(FO?HPVF5H6sv-e?l=j=~8d1DU8I9+D#%v*BC1Yl9l zJW(lba5^y1fIJ-G{wFX+ryF=-W}%vjPT6c;)VH&G^5A7P%++tne~$hil5x+k!gF0W z`hA^Q9pCa<&ky~c)9tu4=ozMA25{RnJNDLmOSt5KCP-zq!~Rw7u>TtBQ)Puv`E{C` zUuS{i6{jlc`ux&$4~CyB$*TCZQT(cSP4U|E%f?lf=Rm}1#IDbWh2UTah?o`GLltxw(vdI!sajcS(FCr#% z#$uTvql7Wpox&;4-vWOZfprn22vfI_-wniEB0O|VRxN1!{awHaOL&kb(oAB z!So_#&EB3~pn*?=x*E$~p3d+5&m{Kh(cr>O2#u~Kxr~mosf5pU`l#q^D5$f#i2UE1qW=DT(`@J!}0oq3iKbS&F7^w8T<85NYzzM-Yb zTjE$niBwFpxiL+}uS!BbvxIE(JTF<^wk%ymC{={=1w<$-zFqI6auHykKmBitF%=Li z1qM};QFWpUCRd}0n(<_kY`B+)ZO7w+u&tBEcg+QT!H)G7DHZzcCta6ttZzBIUC zwv686l4H)_xFb2{5l?tbBf=4rJU>~WUJ&~0hl7D!3KGz=XG{BNQAPtAdWLUP+tw}5 z3~ZY@y4~$`oWNB<3T1+C$^^e9)s@euVie7dQ7Cp*@^Pc`F~w653#JLb5NVmu81Dv9 zT5+Qio-aUn-o?v`ZIuvx79px)yeh`~0%E*Vc3ms3iJPHOFemcZ%K4e$|D44k&c+#| zMNZD}j3F5SJ``8lA@B^IYd4fZUsgk`1~iu^4T0U;?8xsUXtam#+=$AAAb9cITfC;# z0bH_lULAV+YI=^*r#^WvZ{RU@c+@x|=8Bpl@Vv*Jv%&kLSJUG&=8qG3geQiYv!YK9 zZpGz>73LVRgsdRPW$dwxRL8bJZPjU@C$t#L;N7dC%6oN7`Sb39P zIVp2_V(29H1gI#ocg_6r!Sf%VVL{{`QoNcak&upQ0Dr>h*gNV=^^4ug|8JL9#& zI9k=w@^Vk{K*+!lG#8<;9xJXGh~0&Q=J)G^o0}aCt7h;zQNq)m{r!Uh+4*qxZtwW) zU~q8uH?niQbNIL6!H~S!8Sd?p_s3-S;O)WL&LP=9IDWf#dUASjd`5P6&UVQAeR8~a z_VfGGAIZt-`?L3h_lM-*=;Uzk2x{9oJ9vNG@aXy4lp2=VC7Z~g@fp1qiHId^dMCJK zS&nF)M%YBaVm0Vj;*m--%W1P(R`y56S z{?KTW^ZY#JX%(kAuPOpDoeC9LRDs1O2rQb`V*`uz8+Tx>7noRzq$v|mMHcF^e{A78 zraz&H@T!$ikWqdl{0m&@^DlA|^B|xTlysN;vQ8m^zl%)#W9!*4+Fgrw&A_2yCt$2Y zySmkNdLHXC+txc>(@=p;<+yLiaeq2BqTGJv_BYqxpLlCo=ef1vqws8@;FD28fiQL89 zA5#{^G%F_U4+-C8{*AobhX;ebu>j=p|e?#3I>5EtuF&$e_O z_|NRp&~i-2bAS`gjuYyJ!$K7oQ(oAHys&kJwDP-@-_=~dYXui8j#ZL#(~?tp^2(Ec z0iJxttZR7a`!L_gf;=QM$%9y{6Fd!uSBE>Cu(SYMnH)hk$S?(BWYrPInn!c+7KYV| zM6(e@YtyAFIN0rzT?7hUim04!#Kg_YBRuD!sN5Cp7DdP|G~ksGVmLn=2*#Pq&Brhq za`1-=M({+f5pL0i_rYDH1m+J++KN-a_d`V~kN*G$%9)=rS#q{@nPvAM0rCvNojVD$ z9JKkk6_!y{SNfLv+vJ#oB|uTY)OY7M=JJ*%7a+J%pk(uRB@Gs;+x?Y>G~?T3C*s=XG3W zdX~#<>iVqfhj!TU%#P`FJ2xzucQn#=f#E<{-Hrt<)aY2Y*X;zB=}rSlLUc!i}UZ0dICfXQXhz$lbnwdT3#cq zq1DBw{{_|$`R>pheoy`rfys)9PF{Ta1J7#Wxi%~VqE)|%VUX`WoEXOU+JM0T(1;Do z^wS@}f7J~}j^%)XXsd?tltI(MOJUWR7a);-D;fE2aC|!az8=b$GcWuncW{;q2k@+jHr+)QcFiWrZ4OUF`SeERPs zqZ7FOU&wc(Pk)H`(;q0g@Yq0n{(Xy_WrE*qU@q`HJrK_PW?TDcM*muI+cwFtCNaN* zYH`s$v36U}1_cfrC+u{+PzTG+GJA$^1)kIOEz9)`+cey@mpnFPicWnjb`Vs$o+<0> zt~qyye>SGka&vYLNt=*&-~?u5Co8xgF*KqN@kmv9=3!0g6|Lm~24LcT6zW6{|gx)n+p7 z986-xZEJCxu2}5zq?zKe^?9tNxa@I)ECw#im1(9#*b_1r!W$`H?DoeR@3_0Tn)b1Z zC(TKKu(c1Thg&6eJV3eN+-z4iO=OE^$m!V$r_{GaLc%(a$q?rh#dW#^``PGyTY zROu_{<0^iof-iRmUod_*7z}qSlES5Q7we!`PC<-@%Fy;4 zBCZJbh$RY8?N=gO-Z?S9yWF6f%;!}N;WWVdfQ)$ft%V`|`|#wTH9UBGymR==UIGe} zF`0057Ipr6ItKds`7iAu!V@EjZ$60WXc6=*nPR?GK(WNyEb$DmZ;_7D@=V0y?a2Yr z$SDg6qCQDb!iawQLkm9rk$(EW1=%?{A%@{}Oc0RdyQGZC!S3#{9Q83Y4!>8Bfaj@5 zaA>~2jT)z6$Rl1alSpLE9QXZi^n#JS5sN&QVSv59y>&l;{9$l6ZiQPSrQGweBVTxm zwc53D-In80#%$krdtKWxeO)(g+j0zM2R$R~^cKwff8RTPzx)2{Zo{8?L$7aced(&^ zDR;NaQegU5Il@7q} z-p*;C?D9}{y1<+;d)H}Jonc6Jrnyl;(4V&8k}#U{Xo4umkdMj?!6oGMct^|GZv;$) zi!c&bauRpmD5FO}RX#(bDe1hHbY25M z@>#@h7!u^09{D?501zij&DrH64w8+$myR4ccw(fQp|>2^QxCzY})CuIt$> z=+SQAhh|7+*|6idj@`BA!DThOnmxOq8>5WIz>1^8mE613(whxhZi1AmpSzau$_9U? z_$1L+EEs&#)VX77@H{WsvbdbxyffxhFSFhga*`$PFm=?*&4X!IzA%8wdEW0&J)tTJ zEZg*@>*7%F5>Ym}qqp0!%=_Y~OagKjTX)0)dCvT>A}o2wXaJITl_#}C-X(>KeyixWihh5E_==_J zov#@EcKf#8a`mo?ey<#WQ&6}3L`0KB#3(&4^;V~4nB<4_FF#N+&RF=L|FJ3+;ao7b zm#S#}8nvNB3U*k#s?BJ%T6Mku*!l|!Ym#$lUHviRhc^Ao5AF0X&lepw7;`$z>$zRm z@dN5wU<;eR+p%5KG8{)mhn4htSbC}G@arG>hQ_a4y<>H|&I^bRPg~l==&)`Xo$gX} z_}+N%v@!A`H^`6gv{&vU&l^QhyPh85^mNCU%nEENxaDf3CigH=4ZK~rOs@!*+p zqvFAjOT(?$wgK_NdtKE2)YE-jJkX>01x+6j3*3I7J{YTivMbK~3wmFz=C+D3stBWs zFn+F&?r6qCM|*v4gwfK$foOGlUz{)Xl@7oU>0gcoSs)B1j@q#<<$rMX3-Ee_*=96?Kr>{iDZncrcvI0dvd6m zG6{zY?ld;I)9u-AuVcS}VCl4_O$?SAhHE5B`NE(Rjo>6qqh7n`2_ z1eOcS`UsxX9~UYe@(~5^^m0Xr^r5Kek1#ZJn;c|(;qbcp>b}6JeDA@S>ySaiw#gn; zFt5G7TB_YztX;nMaP4CTy`EgS*6r%-<_B))NB8!a}3qIZDLk(Cx;P)(Ax2`NmQp za@vZ!LUD5F?g*5*wAa;b!#B}dH^Po-hutovjvxB25wHdY%GN#k`m=cZF~j@n!$QyJ zjy>-TZ>kSw>0P&HH6x~x-yhVdoBB*ajaxB_pq}7s3%k4eqb5azC&i60!WzDG102ek zI#CYgqC@$pgYTcglVWJ4^?L1-@xjTE{dPWGuLK_o2ld=A9_L5xvR@f+Cfg>b0$V-z zS!UPwK~SczKj^spL?gzpx5*LBxLEkg?D{^>t9tptZw$s1njA%+{@5l%9xr}odU>A* zHh=vEKwy+7^%XrSOVbTZYJDH10}7aDhSS%Hc1g1d=J0$s+`+PVe(NR6WYL1*2;eE;6CfM+ z@trZeU(kZ&tnOxu{NQDOA;$M4h8N{7au~U?O03CX)>Y*d!zPx=?WTWMoDTQDui9c_TYxvu>yF=Qh@{Tq=iy7TcHk#y z2i&;T%{rBsKL!e=Q%_`@Lq%;F217Qn2K|RYLRI9l$(j@GTF|--V`QYIQ?79?;TlmKbd@Jm9Eakr6 zmMJUU_l#L6gln>Pd#>K?x_;LUJ=+iUka~vYGt+Q-x}^t;hku5Al#kI6YLWxLfa_S! zz3pdArUL{P1)ZGVggAJUw%yX(5e7q%msK#J5q?icckOoEYfX8rWg4z-Ij+^ZG@2R} zxVOzs4G%mhOdsr+nlCS5zN`!wEK2fqLk3cs-ijH_?>_k|G+MFJ6T7296{KgmLG!yC zZ_tKU=_@6#f+{a5s4@Uc<9yjRe<+UfO+yQ2#}rfah)!9~@GvHF0QM&@N(?7`NWjL+ z^4cl=YstXC16>%%xv>zmIpWX)N)AEos$&4Q)HWrxP3i0GDljzz|NBbYl+0e==(Vhl ztF|ev9DsX)5zn$eiJBJmZMPeCb?OF7LCM57>QtDseNz;hbLR3>(AUF)_05?oJG!oU6(Fxr0<)bkz z$Q8|DqCyd1%z#|+VoXL%ROeXZ0oLgZ?In+I082O6Cag7T{o@~4m*!E#vgM5DG56bV zPfkf8Vl;}Oift&4x~Fj9ODLSIRORjk^1FtKn1TuB^t|MQK!UlFKqJ2fzafhycx0%=+9eXC2b>-ubts9!kl~syGDc$RkAXZDq2--9FG(^3 zuU$ch3MT6r{Q1vSYsNr6*|#eg`WY(?CeV@!JeX!E36Q7CoK@l6uN=7b!ydTb9h+Hh z*RpM+W9nAdFnez3xjke3!0q!WjSZvjj7OpFe8=qSUDlx;uVeWR1swn$0qdB)@A`~& zw{9*OllPz{l19ZV@4?;Ol~rhzM%wf>g|=%#SOx>+R@)aX-O+WUqc<32x2mc4#5q|q zT59TEGQz;nR|s+HHxA$YScy|$S0HmDXH^M4(4$%X)=_KKcelR$Y_+E_{p%3Y3*@|GLyEw9{Sb2DtS?k1ZmymV^`dg1odE<)GA_NMG=G1RV>M;zRK$AE#5 zx)Ry)h|TXVw`>FFk?+y07?UBcU;6UNOKSX}T3cE=O1(Kt@n^FYs>OAi<;5yov#1sm zNjP3}7;l*oKg#|ZT1w?euS{y*Ik#;OI!!F(~F9PB=g z?#IBl27gS=wz|63>vd}twbn^l-}IQGnhlCE%Fn~lEuVJ(+6rcy_nf#*?LFo zn1;PDn{KA#s>WE&sjoYyj?q9EmWfhHFK`msde7Eu+cg&^k)>492B*&uoP6?gi|n?@ z8Bl%Ew3|OtGwOxRC<`VN{OY;7zA&Tg#TiwPvYJ<4cV7J)kICPJP-^MrO(Iisb<@#I z$L%gmB1fsEO9p>xEg=*;dJERC1a}@7nl=r~1tm zrtVivPUH5SZmZ)uDml$vXJ}vQEu*F5iP_|bGJ?M%{uhv;FmDTj@`GGf37r4Q0`dcH znD|Rl#_T%HerU@pnRA#$k>*V_{C8f+qtha|U1EfZ2}zzZsHfg(f*VHiAKLIi@o9$oTc*}UQ;Xj}Gy4Pb) z;Cm`1&P$n3DkaX>R--Bt>9(V8> z)E5AKFdYK?BZsMeGQXkF<&6yjTbikN4Xx`KcY@C95ni#PvW%Ug3Df>2#Kkrqj|W`F zwieIFt8XY~Xe=|Bn$zocwXWsdiJ6)YvP$Qq(m6dUos*$Il+MYN`$Y_lb=&KubGq}5 z8`1F%gj|`;Lf7@(uEW4JwoJd{cEiwf4a+bbeeICzw~)+ckq_>SGS1uJIJ)h4fk``# zZuG*yWu2~V(5~ZHJ;!$!kC6M2TqNQOMdr+CR1wGL7Yhvvi`cGZcG^4;ek*49w+{_% zM4a(^r*iz8K57a%1hKzW1byxC1e+^3D%os`~{r>e0Eo=AR^lld@|c8L-35e#wKK_CUY(V2$FdOX`Ju}^*X&w0 z^#jxDu)yejQXXu0|28Kgs1=j literal 0 HcmV?d00001 From 9f73d93e624ae08ca61288503171226477fd87fd Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Tue, 3 Oct 2023 14:39:08 +0300 Subject: [PATCH 05/16] Add param for limiting repo Urls --- .../swh/CollectLastVisitRepositoryData.java | 34 ++++++++----------- .../swh/CollectSoftwareRepositoryURLs.java | 16 ++++++--- .../dnetlib/dhp/swh/PrepareSWHActionsets.java | 1 - ...nput_collect_software_repository_urls.json | 6 ++++ .../eu/dnetlib/dhp/swh/job.properties | 2 ++ .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 1 + 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java index 296a4cce1..e602266a8 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -1,32 +1,26 @@ package eu.dnetlib.dhp.swh; -import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.cli.ParseException; -import org.apache.commons.io.IOUtils; -import org.apache.hadoop.fs.FSDataInputStream; -import org.apache.hadoop.fs.FileStatus; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.io.SequenceFile; -import org.apache.hadoop.io.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import eu.dnetlib.dhp.application.ArgumentApplicationParser; import eu.dnetlib.dhp.common.collection.CollectorException; import eu.dnetlib.dhp.common.collection.HttpClientParams; import eu.dnetlib.dhp.swh.utils.SWHConnection; import eu.dnetlib.dhp.swh.utils.SWHConstants; import eu.dnetlib.dhp.swh.utils.SWHUtils; +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.SequenceFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URL; + +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; /** * Given a file with software repository URLs, this class diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java index 6232fa322..abd51bc5b 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectSoftwareRepositoryURLs.java @@ -51,6 +51,9 @@ public class CollectSoftwareRepositoryURLs { final String hiveMetastoreUris = parser.get("hiveMetastoreUris"); log.info("hiveMetastoreUris: {}", hiveMetastoreUris); + final Integer softwareLimit = Integer.parseInt(parser.get("softwareLimit")); + log.info("softwareLimit: {}", softwareLimit); + SparkConf conf = new SparkConf(); conf.set("hive.metastore.uris", hiveMetastoreUris); @@ -58,18 +61,23 @@ public class CollectSoftwareRepositoryURLs { conf, isSparkSessionManaged, spark -> { - doRun(spark, hiveDbName, outputPath); + doRun(spark, hiveDbName, softwareLimit, outputPath); }); } - private static void doRun(SparkSession spark, String hiveDbName, String outputPath) { + private static void doRun(SparkSession spark, String hiveDbName, Integer limit, + String outputPath) { String queryTemplate = "SELECT distinct coderepositoryurl.value " + "FROM %s.software " + "WHERE coderepositoryurl.value IS NOT NULL " + "AND datainfo.deletedbyinference = FALSE " + - "AND datainfo.invisible = FALSE " + - "LIMIT 5000"; + "AND datainfo.invisible = FALSE "; + + if (limit != null) { + queryTemplate += String.format("LIMIT %s", limit); + } + String query = String.format(queryTemplate, hiveDbName); log.info("Hive query to fetch software code URLs: {}", query); diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java index c0ab11bc4..c54e10d3e 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java @@ -82,7 +82,6 @@ public class PrepareSWHActionsets { softwareRDD .saveAsHadoopFile( outputPath, Text.class, Text.class, SequenceFileOutputFormat.class); -// , GzipCodec.class); }); } diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json index 6e98c7673..4459fe9df 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_software_repository_urls.json @@ -22,5 +22,11 @@ "paramLongName": "hiveMetastoreUris", "paramDescription": "the hive metastore uris", "paramRequired": true + }, + { + "paramName": "slim", + "paramLongName": "softwareLimit", + "paramDescription": "limit on the number of software repo URL to fetch", + "paramRequired": false } ] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index 651bae337..8dd0689a3 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -14,4 +14,6 @@ maxNumberOfRetry=2 retryDelay=1 requestDelay=100 +softwareLimit=500 + resume=collect-software-repository-urls diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index 7aa667a4a..e0763414f 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -83,6 +83,7 @@ --softwareCodeRepositoryURLs${softwareCodeRepositoryURLs} --hiveDbName${hiveDbName} --hiveMetastoreUris${hiveMetastoreUris} + --softwareLimit${softwareLimit} From 24c43e0c602254f506e81f1e3d361645b1a9d36e Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Tue, 3 Oct 2023 15:11:58 +0300 Subject: [PATCH 06/16] Restructure workflow parameters --- .../main/resources/eu/dnetlib/dhp/swh/job.properties | 2 -- .../eu/dnetlib/dhp/swh/oozie_app/config-default.xml | 4 ++++ .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index 8dd0689a3..114181944 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -1,8 +1,6 @@ # hive hiveDbName=openaire_prod_20230914 -sparkSqlWarehouseDir=/user/hive/warehouse - # input/output files softwareCodeRepositoryURLs=${workingDir}/1_code_repo_urls.csv lastVisitsPath=${workingDir}/2_last_visits.seq diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml index 7873d595e..3e45a53fa 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/config-default.xml @@ -47,4 +47,8 @@ oozie.launcher.mapreduce.user.classpath.first true + + sparkSqlWarehouseDir + /user/hive/warehouse + \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index e0763414f..e29e5b43d 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -18,6 +18,14 @@ archiveRequestsPath The path in the HDFS to save the responses of the archive requests + + actionsetsPath + The path in the HDFS to save the action sets + + + graphPath + The path in the HDFS to the base folder of the graph + maxNumberOfRetry Max number of retries for failed API calls @@ -30,6 +38,10 @@ requestDelay Delay between API requests (in ms) + + softwareLimit + Limit on the number of repo URLs to use (Optional); for debug purposes + resume Variable that indicates the step to start from From b49a3ac9b21233edb889b2e0e60dfa3ed8c02734 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Tue, 3 Oct 2023 15:43:38 +0300 Subject: [PATCH 07/16] Add actionsetsPath as a global WF param --- .../swh/CollectLastVisitRepositoryData.java | 23 ++++++++++--------- .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 4 ++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java index e602266a8..ce1b0bb46 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -1,12 +1,12 @@ package eu.dnetlib.dhp.swh; -import eu.dnetlib.dhp.application.ArgumentApplicationParser; -import eu.dnetlib.dhp.common.collection.CollectorException; -import eu.dnetlib.dhp.common.collection.HttpClientParams; -import eu.dnetlib.dhp.swh.utils.SWHConnection; -import eu.dnetlib.dhp.swh.utils.SWHConstants; -import eu.dnetlib.dhp.swh.utils.SWHUtils; +import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URL; + import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.apache.hadoop.fs.FileStatus; @@ -16,11 +16,12 @@ import org.apache.hadoop.io.SequenceFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.IOException; -import java.net.URL; - -import static eu.dnetlib.dhp.utils.DHPUtils.getHadoopConfiguration; +import eu.dnetlib.dhp.application.ArgumentApplicationParser; +import eu.dnetlib.dhp.common.collection.CollectorException; +import eu.dnetlib.dhp.common.collection.HttpClientParams; +import eu.dnetlib.dhp.swh.utils.SWHConnection; +import eu.dnetlib.dhp.swh.utils.SWHConstants; +import eu.dnetlib.dhp.swh.utils.SWHUtils; /** * Given a file with software repository URLs, this class diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index e29e5b43d..c69ecd74d 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -57,6 +57,10 @@ oozie.action.sharelib.for.spark ${oozieActionShareLibForSpark2} + + actionsetsPath + ${actionsetsPath} + From cae75fc75da4410289e1d5107073c5b112520003 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Tue, 3 Oct 2023 16:55:10 +0300 Subject: [PATCH 08/16] Add SWH in the collectedFrom field --- .../java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java | 8 ++++++++ .../main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java index c54e10d3e..93d111039 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java @@ -165,6 +165,14 @@ public class PrepareSWHActionsets { row.getString(row.fieldIndex("swhid")), qualifier, dataInfo))); + + // add SWH in the `collectedFrom` field + KeyValue kv = new KeyValue(); + kv.setKey(SWHConstants.SWH_ID); + kv.setValue(SWHConstants.SWH_NAME); + + s.setCollectedfrom(Arrays.asList(kv)); + return s; }, Encoders.bean(Software.class)) .toJavaRDD() diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java index 08400f28b..efd0e708b 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java @@ -16,4 +16,8 @@ public class SWHConstants { public static final String SWHID_CLASSNAME = "Software Heritage Identifier"; + public static final String SWH_ID = "10|openaire____::dbfd07503aaa1ed31beed7dec942f3f4"; + + public static final String SWH_NAME = "Software Heritage"; + } From e9f24df21cce089aec7945b2f50f20cbe1acc062 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Tue, 3 Oct 2023 20:57:57 +0300 Subject: [PATCH 09/16] Move SWH API Key from constants to workflow param --- .../java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java | 5 ++++- .../dhp/swh/CollectLastVisitRepositoryData.java | 5 ++++- .../java/eu/dnetlib/dhp/swh/utils/SWHConnection.java | 6 ++++-- .../java/eu/dnetlib/dhp/swh/utils/SWHConstants.java | 2 -- .../dnetlib/dhp/swh/input_archive_repository_urls.json | 6 ++++++ .../swh/input_collect_last_visit_repository_data.json | 6 ++++++ .../main/resources/eu/dnetlib/dhp/swh/job.properties | 2 ++ .../eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 10 ++++++++++ .../java/eu/dnetlib/dhp/swh/SWHConnectionTest.java | 4 ++-- 9 files changed, 38 insertions(+), 8 deletions(-) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java index f02861953..baa510346 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/ArchiveRepositoryURLs.java @@ -63,9 +63,12 @@ public class ArchiveRepositoryURLs { final Integer archiveThresholdInDays = Integer.parseInt(argumentParser.get("archiveThresholdInDays")); log.info("archiveThresholdInDays: {}", archiveThresholdInDays); + final String apiAccessToken = argumentParser.get("apiAccessToken"); + log.info("apiAccessToken: {}", apiAccessToken); + final HttpClientParams clientParams = SWHUtils.getClientParams(argumentParser); - swhConnection = new SWHConnection(clientParams); + swhConnection = new SWHConnection(clientParams, apiAccessToken); final FileSystem fs = FileSystem.get(getHadoopConfiguration(hdfsuri)); diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java index ce1b0bb46..ebb9176ff 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/CollectLastVisitRepositoryData.java @@ -55,9 +55,12 @@ public class CollectLastVisitRepositoryData { final String outputPath = argumentParser.get("lastVisitsPath"); log.info("outputPath: {}", outputPath); + final String apiAccessToken = argumentParser.get("apiAccessToken"); + log.info("apiAccessToken: {}", apiAccessToken); + final HttpClientParams clientParams = SWHUtils.getClientParams(argumentParser); - swhConnection = new SWHConnection(clientParams); + swhConnection = new SWHConnection(clientParams, apiAccessToken); final FileSystem fs = FileSystem.get(getHadoopConfiguration(hdfsuri)); diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java index 9c145fc19..80249e816 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConnection.java @@ -14,13 +14,15 @@ public class SWHConnection { HttpConnector2 conn; - public SWHConnection(HttpClientParams clientParams) { + public SWHConnection(HttpClientParams clientParams, String accessToken) { // set custom headers Map headers = new HashMap() { { put(HttpHeaders.ACCEPT, "application/json"); - put(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", SWHConstants.ACCESS_TOKEN)); + if (accessToken != null) { + put(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", accessToken)); + } } }; diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java index efd0e708b..eae839cfd 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/utils/SWHConstants.java @@ -6,8 +6,6 @@ public class SWHConstants { public static final String SWH_ARCHIVE_URL = "https://archive.softwareheritage.org/api/1/origin/save/%s/url/%s/"; - public static final String ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtODQ2Ny05MzAyZjk3MTFkOGEifQ.eyJpYXQiOjE2OTQ2MzYwMjAsImp0aSI6IjkwZjdkNTNjLTQ5YTktNGFiMy1hY2E0LTcwMTViMjEyZTNjNiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zb2Z0d2FyZWhlcml0YWdlLm9yZy9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwiYXVkIjoiaHR0cHM6Ly9hdXRoLnNvZnR3YXJlaGVyaXRhZ2Uub3JnL2F1dGgvcmVhbG1zL1NvZnR3YXJlSGVyaXRhZ2UiLCJzdWIiOiIzMTY5OWZkNC0xNmE0LTQxOWItYTdhMi00NjI5MDY4ZjI3OWEiLCJ0eXAiOiJPZmZsaW5lIiwiYXpwIjoic3doLXdlYiIsInNlc3Npb25fc3RhdGUiOiIzMjYzMzEwMS00ZDRkLTQwMjItODU2NC1iMzNlMTJiNTE3ZDkiLCJzY29wZSI6Im9wZW5pZCBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIGVtYWlsIn0.XHj1VIZu1dZ4Ej32-oU84mFmaox9cLNjXosNxwZM0Xs"; - public static final String DEFAULT_VISIT_TYPE = "git"; public static final String VISIT_STATUS_NOT_FOUND = "not_found"; diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json index ce80d6f4a..e8671f71b 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_archive_repository_urls.json @@ -46,5 +46,11 @@ "paramLongName": "archiveThresholdInDays", "paramDescription": "the thershold (in days) required to issue an archive request", "paramRequired": false + }, + { + "paramName": "aat", + "paramLongName": "apiAccessToken", + "paramDescription": "the API access token of the SWH API", + "paramRequired": false } ] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json index 8bf41f0ae..662582dfe 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/input_collect_last_visit_repository_data.json @@ -40,5 +40,11 @@ "paramLongName": "requestMethod", "paramDescription": "the method of the requests to perform", "paramRequired": false + }, + { + "paramName": "aat", + "paramLongName": "apiAccessToken", + "paramDescription": "the API access token of the SWH API", + "paramRequired": false } ] \ No newline at end of file diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties index 114181944..35c068286 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/job.properties @@ -8,6 +8,8 @@ archiveRequestsPath=${workingDir}/3_archive_requests.seq actionsetsPath=${workingDir}/4_actionsets graphPath=/tmp/prod_provision/graph/18_graph_blacklisted +apiAccessToken=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtODQ2Ny05MzAyZjk3MTFkOGEifQ.eyJpYXQiOjE2OTQ2MzYwMjAsImp0aSI6IjkwZjdkNTNjLTQ5YTktNGFiMy1hY2E0LTcwMTViMjEyZTNjNiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zb2Z0d2FyZWhlcml0YWdlLm9yZy9hdXRoL3JlYWxtcy9Tb2Z0d2FyZUhlcml0YWdlIiwiYXVkIjoiaHR0cHM6Ly9hdXRoLnNvZnR3YXJlaGVyaXRhZ2Uub3JnL2F1dGgvcmVhbG1zL1NvZnR3YXJlSGVyaXRhZ2UiLCJzdWIiOiIzMTY5OWZkNC0xNmE0LTQxOWItYTdhMi00NjI5MDY4ZjI3OWEiLCJ0eXAiOiJPZmZsaW5lIiwiYXpwIjoic3doLXdlYiIsInNlc3Npb25fc3RhdGUiOiIzMjYzMzEwMS00ZDRkLTQwMjItODU2NC1iMzNlMTJiNTE3ZDkiLCJzY29wZSI6Im9wZW5pZCBvZmZsaW5lX2FjY2VzcyBwcm9maWxlIGVtYWlsIn0.XHj1VIZu1dZ4Ej32-oU84mFmaox9cLNjXosNxwZM0Xs + maxNumberOfRetry=2 retryDelay=1 requestDelay=100 diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index c69ecd74d..64dc0d2aa 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -38,6 +38,10 @@ requestDelay Delay between API requests (in ms) + + apiAccessToken + The API Key of the SWH API + softwareLimit Limit on the number of repo URLs to use (Optional); for debug purposes @@ -61,6 +65,10 @@ actionsetsPath ${actionsetsPath} + + apiAccessToken + ${apiAccessToken} + @@ -117,6 +125,7 @@ --requestDelay${requestDelay} --retryDelay${retryDelay} --requestMethodGET + --apiAccessToken${apiAccessToken} @@ -136,6 +145,7 @@ --requestDelay${requestDelay} --retryDelay${retryDelay} --requestMethodPOST + --apiAccessToken${apiAccessToken} diff --git a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java index 28210f1b3..b19e0e7ac 100644 --- a/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java +++ b/dhp-workflows/dhp-swh/src/test/java/eu/dnetlib/dhp/swh/SWHConnectionTest.java @@ -25,7 +25,7 @@ public class SWHConnectionTest { HttpClientParams clientParams = new HttpClientParams(); clientParams.setRequestMethod("GET"); - SWHConnection swhConnection = new SWHConnection(clientParams); + SWHConnection swhConnection = new SWHConnection(clientParams, null); String repoUrl = "https://github.com/stanford-futuredata/FAST"; URL url = new URL(String.format(SWHConstants.SWH_LATEST_VISIT_URL, repoUrl)); @@ -43,7 +43,7 @@ public class SWHConnectionTest { HttpClientParams clientParams = new HttpClientParams(); clientParams.setRequestMethod("POST"); - SWHConnection swhConnection = new SWHConnection(clientParams); + SWHConnection swhConnection = new SWHConnection(clientParams, null); String repoUrl = "https://github.com/stanford-futuredata/FAST"; URL url = new URL(String.format(SWHConstants.SWH_ARCHIVE_URL, SWHConstants.DEFAULT_VISIT_TYPE, repoUrl)); From ee8a39e7d2dcd121d88c1bb84eb4794024fe6a16 Mon Sep 17 00:00:00 2001 From: Claudio Atzori Date: Wed, 4 Oct 2023 12:32:05 +0200 Subject: [PATCH 10/16] cleanup and refinements --- .../CreateActionSetSparkJob.java | 63 ++++++++++++------- .../opencitations/GetOpenCitationsRefs.java | 3 +- .../actionmanager/opencitations/ReadCOCI.java | 9 +-- .../opencitations/as_parameters.json | 18 +++--- .../opencitations/oozie_app/workflow.xml | 3 +- 5 files changed, 55 insertions(+), 41 deletions(-) diff --git a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/CreateActionSetSparkJob.java b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/CreateActionSetSparkJob.java index e3a9833b3..2db756a94 100644 --- a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/CreateActionSetSparkJob.java +++ b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/CreateActionSetSparkJob.java @@ -32,18 +32,28 @@ import eu.dnetlib.dhp.schema.oaf.*; import eu.dnetlib.dhp.schema.oaf.utils.CleaningFunctions; import eu.dnetlib.dhp.schema.oaf.utils.IdentifierFactory; import eu.dnetlib.dhp.schema.oaf.utils.OafMapperUtils; +import eu.dnetlib.dhp.schema.oaf.utils.PidType; import eu.dnetlib.dhp.utils.DHPUtils; import scala.Tuple2; public class CreateActionSetSparkJob implements Serializable { public static final String OPENCITATIONS_CLASSID = "sysimport:crosswalk:opencitations"; public static final String OPENCITATIONS_CLASSNAME = "Imported from OpenCitations"; + + // DOI-to-DOI citations + public static final String COCI = "COCI"; + + // PMID-to-PMID citations + public static final String POCI = "POCI"; + private static final String DOI_PREFIX = "50|doi_________::"; private static final String PMID_PREFIX = "50|pmid________::"; + private static final String TRUST = "0.91"; private static final Logger log = LoggerFactory.getLogger(CreateActionSetSparkJob.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static void main(final String[] args) throws IOException, ParseException { @@ -67,7 +77,7 @@ public class CreateActionSetSparkJob implements Serializable { log.info("isSparkSessionManaged: {}", isSparkSessionManaged); final String inputPath = parser.get("inputPath"); - log.info("inputPath {}", inputPath.toString()); + log.info("inputPath {}", inputPath); final String outputPath = parser.get("outputPath"); log.info("outputPath {}", outputPath); @@ -81,19 +91,16 @@ public class CreateActionSetSparkJob implements Serializable { runWithSparkSession( conf, isSparkSessionManaged, - spark -> { - extractContent(spark, inputPath, outputPath, shouldDuplicateRels); - }); + spark -> extractContent(spark, inputPath, outputPath, shouldDuplicateRels)); } private static void extractContent(SparkSession spark, String inputPath, String outputPath, boolean shouldDuplicateRels) { - getTextTextJavaPairRDD(spark, inputPath, shouldDuplicateRels, "COCI") - .union(getTextTextJavaPairRDD(spark, inputPath, shouldDuplicateRels, "POCI")) + getTextTextJavaPairRDD(spark, inputPath, shouldDuplicateRels, COCI) + .union(getTextTextJavaPairRDD(spark, inputPath, shouldDuplicateRels, POCI)) .saveAsHadoopFile(outputPath, Text.class, Text.class, SequenceFileOutputFormat.class); - } private static JavaPairRDD getTextTextJavaPairRDD(SparkSession spark, String inputPath, @@ -109,7 +116,7 @@ public class CreateActionSetSparkJob implements Serializable { value, shouldDuplicateRels, prefix) .iterator(), Encoders.bean(Relation.class)) - .filter((FilterFunction) value -> value != null) + .filter((FilterFunction) Objects::nonNull) .toJavaRDD() .map(p -> new AtomicAction(p.getClass(), p)) .mapToPair( @@ -123,20 +130,28 @@ public class CreateActionSetSparkJob implements Serializable { String prefix; String citing; String cited; - if (p.equals("COCI")) { - prefix = DOI_PREFIX; - citing = prefix - + IdentifierFactory.md5(CleaningFunctions.normalizePidValue("doi", value.getCiting())); - cited = prefix - + IdentifierFactory.md5(CleaningFunctions.normalizePidValue("doi", value.getCited())); - - } else { - prefix = PMID_PREFIX; - citing = prefix - + IdentifierFactory.md5(CleaningFunctions.normalizePidValue("pmid", value.getCiting())); - cited = prefix - + IdentifierFactory.md5(CleaningFunctions.normalizePidValue("pmid", value.getCited())); + switch (p) { + case COCI: + prefix = DOI_PREFIX; + citing = prefix + + IdentifierFactory + .md5(CleaningFunctions.normalizePidValue(PidType.doi.toString(), value.getCiting())); + cited = prefix + + IdentifierFactory + .md5(CleaningFunctions.normalizePidValue(PidType.doi.toString(), value.getCited())); + break; + case POCI: + prefix = PMID_PREFIX; + citing = prefix + + IdentifierFactory + .md5(CleaningFunctions.normalizePidValue(PidType.pmid.toString(), value.getCiting())); + cited = prefix + + IdentifierFactory + .md5(CleaningFunctions.normalizePidValue(PidType.pmid.toString(), value.getCited())); + break; + default: + throw new IllegalStateException("Invalid prefix: " + p); } if (!citing.equals(cited)) { @@ -162,7 +177,7 @@ public class CreateActionSetSparkJob implements Serializable { public static Relation getRelation( String source, String target, - String relclass) { + String relClass) { return OafMapperUtils .getRelation( @@ -170,7 +185,7 @@ public class CreateActionSetSparkJob implements Serializable { target, ModelConstants.RESULT_RESULT, ModelConstants.CITATION, - relclass, + relClass, Arrays .asList( OafMapperUtils.keyValue(ModelConstants.OPENOCITATIONS_ID, ModelConstants.OPENOCITATIONS_NAME)), @@ -183,6 +198,6 @@ public class CreateActionSetSparkJob implements Serializable { ModelConstants.DNET_PROVENANCE_ACTIONS, ModelConstants.DNET_PROVENANCE_ACTIONS), TRUST), null); - } + } diff --git a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/GetOpenCitationsRefs.java b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/GetOpenCitationsRefs.java index 60dc998ef..600cf7df1 100644 --- a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/GetOpenCitationsRefs.java +++ b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/GetOpenCitationsRefs.java @@ -3,6 +3,7 @@ package eu.dnetlib.dhp.actionmanager.opencitations; import java.io.*; import java.io.Serializable; +import java.util.Arrays; import java.util.Objects; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; @@ -37,7 +38,7 @@ public class GetOpenCitationsRefs implements Serializable { parser.parseArgument(args); final String[] inputFile = parser.get("inputFile").split(";"); - log.info("inputFile {}", inputFile.toString()); + log.info("inputFile {}", Arrays.asList(inputFile)); final String workingPath = parser.get("workingPath"); log.info("workingPath {}", workingPath); diff --git a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/ReadCOCI.java b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/ReadCOCI.java index 3d384de9d..b9c24df3b 100644 --- a/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/ReadCOCI.java +++ b/dhp-workflows/dhp-aggregation/src/main/java/eu/dnetlib/dhp/actionmanager/opencitations/ReadCOCI.java @@ -7,6 +7,7 @@ import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession; import java.io.IOException; import java.io.Serializable; +import java.util.Arrays; import java.util.Optional; import org.apache.commons.io.IOUtils; @@ -42,7 +43,7 @@ public class ReadCOCI implements Serializable { log.info("outputPath: {}", outputPath); final String[] inputFile = parser.get("inputFile").split(";"); - log.info("inputFile {}", inputFile.toString()); + log.info("inputFile {}", Arrays.asList(inputFile)); Boolean isSparkSessionManaged = isSparkSessionManaged(parser); log.info("isSparkSessionManaged: {}", isSparkSessionManaged); @@ -74,10 +75,10 @@ public class ReadCOCI implements Serializable { private static void doRead(SparkSession spark, String workingPath, String[] inputFiles, String outputPath, - String delimiter, String format) throws IOException { + String delimiter, String format) { for (String inputFile : inputFiles) { - String p_string = workingPath + "/" + inputFile + ".gz"; + String pString = workingPath + "/" + inputFile + ".gz"; Dataset cociData = spark .read() @@ -86,7 +87,7 @@ public class ReadCOCI implements Serializable { .option("inferSchema", "true") .option("header", "true") .option("quotes", "\"") - .load(p_string) + .load(pString) .repartition(100); cociData.map((MapFunction) row -> { diff --git a/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/as_parameters.json b/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/as_parameters.json index e25d1f4b8..5244a6fe4 100644 --- a/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/as_parameters.json +++ b/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/as_parameters.json @@ -16,15 +16,11 @@ "paramLongName": "isSparkSessionManaged", "paramDescription": "the hdfs name node", "paramRequired": false - }, { - "paramName": "sdr", - "paramLongName": "shouldDuplicateRels", - "paramDescription": "the hdfs name node", - "paramRequired": false -},{ - "paramName": "p", - "paramLongName": "prefix", - "paramDescription": "the hdfs name node", - "paramRequired": true -} + }, + { + "paramName": "sdr", + "paramLongName": "shouldDuplicateRels", + "paramDescription": "activates/deactivates the construction of bidirectional relations Cites/IsCitedBy", + "paramRequired": false + } ] diff --git a/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/oozie_app/workflow.xml b/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/oozie_app/workflow.xml index d87dfa2ba..deb32459b 100644 --- a/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-aggregation/src/main/resources/eu/dnetlib/dhp/actionmanager/opencitations/oozie_app/workflow.xml @@ -34,6 +34,7 @@ Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}] + ${jobTracker} @@ -54,6 +55,7 @@ + eu.dnetlib.dhp.actionmanager.opencitations.GetOpenCitationsRefs @@ -112,7 +114,6 @@ --inputPath${workingPath} --outputPath${outputPath} - --prefix${prefix} From 1bb83b91880933cee1a39aaa59ad0c70f5086156 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Wed, 4 Oct 2023 20:31:45 +0300 Subject: [PATCH 11/16] Add prefix in SWH ID --- .../src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java index 93d111039..2239244d6 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java @@ -162,7 +162,7 @@ public class PrepareSWHActionsets { .asList( OafMapperUtils .structuredProperty( - row.getString(row.fieldIndex("swhid")), + String.format("swh:1:snp:%s", row.getString(row.fieldIndex("swhid"))), qualifier, dataInfo))); From 13f332ce77e3ef7a03ba6e0b31419cf7caf3ea8e Mon Sep 17 00:00:00 2001 From: Sandro La Bruzzo Date: Fri, 6 Oct 2023 10:40:05 +0200 Subject: [PATCH 12/16] ignored jenv prop --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 73d9179fa..14cd4d345 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ spark-warehouse /**/*.log /**/.factorypath /**/.scalafmt.conf +/.java-version From eed9fe090220b93789f0d051303fb718814af23d Mon Sep 17 00:00:00 2001 From: Claudio Atzori Date: Fri, 6 Oct 2023 12:31:17 +0200 Subject: [PATCH 13/16] code formatting --- .../dhp/oa/merge/GroupEntitiesSparkJob.java | 4 +- .../dhp/oa/dedup/SparkPropagateRelation.java | 72 +++---- .../group/GroupEntitiesSparkJobTest.java | 190 +++++++++--------- 3 files changed, 133 insertions(+), 133 deletions(-) diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/oa/merge/GroupEntitiesSparkJob.java b/dhp-common/src/main/java/eu/dnetlib/dhp/oa/merge/GroupEntitiesSparkJob.java index 87510c108..99981bf6a 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/oa/merge/GroupEntitiesSparkJob.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/oa/merge/GroupEntitiesSparkJob.java @@ -33,7 +33,7 @@ import scala.Tuple2; public class GroupEntitiesSparkJob { private static final Logger log = LoggerFactory.getLogger(GroupEntitiesSparkJob.class); - private static final Encoder OAFENTITY_KRYO_ENC = Encoders.kryo(OafEntity.class); + private static final Encoder OAFENTITY_KRYO_ENC = Encoders.kryo(OafEntity.class); public static void main(String[] args) throws Exception { @@ -114,7 +114,7 @@ public class GroupEntitiesSparkJob { Encoders.tuple(Encoders.STRING(), OAFENTITY_KRYO_ENC)); // pivot on "_1" (classname of the entity) - // created columns containing only entities of the same class + // created columns containing only entities of the same class for (Map.Entry e : ModelSupport.entityTypes.entrySet()) { String entity = e.getKey().name(); Class entityClass = e.getValue(); diff --git a/dhp-workflows/dhp-dedup-openaire/src/main/java/eu/dnetlib/dhp/oa/dedup/SparkPropagateRelation.java b/dhp-workflows/dhp-dedup-openaire/src/main/java/eu/dnetlib/dhp/oa/dedup/SparkPropagateRelation.java index 739295c91..cb1c70059 100644 --- a/dhp-workflows/dhp-dedup-openaire/src/main/java/eu/dnetlib/dhp/oa/dedup/SparkPropagateRelation.java +++ b/dhp-workflows/dhp-dedup-openaire/src/main/java/eu/dnetlib/dhp/oa/dedup/SparkPropagateRelation.java @@ -67,60 +67,60 @@ public class SparkPropagateRelation extends AbstractSparkAction { log.info("graphOutputPath: '{}'", graphOutputPath); Dataset mergeRels = spark - .read() - .load(DedupUtility.createMergeRelPath(workingPath, "*", "*")) - .as(REL_BEAN_ENC); + .read() + .load(DedupUtility.createMergeRelPath(workingPath, "*", "*")) + .as(REL_BEAN_ENC); // Dataset idsToMerge = mergeRels - .where(col("relClass").equalTo(ModelConstants.MERGES)) - .select(col("source").as("dedupID"), col("target").as("mergedObjectID")) - .distinct(); + .where(col("relClass").equalTo(ModelConstants.MERGES)) + .select(col("source").as("dedupID"), col("target").as("mergedObjectID")) + .distinct(); Dataset allRels = spark - .read() - .schema(REL_BEAN_ENC.schema()) - .json(graphBasePath + "/relation"); + .read() + .schema(REL_BEAN_ENC.schema()) + .json(graphBasePath + "/relation"); Dataset dedupedRels = allRels - .joinWith(idsToMerge, allRels.col("source").equalTo(idsToMerge.col("mergedObjectID")), "left_outer") - .joinWith(idsToMerge, col("_1.target").equalTo(idsToMerge.col("mergedObjectID")), "left_outer") - .select("_1._1", "_1._2.dedupID", "_2.dedupID") - .as(Encoders.tuple(REL_BEAN_ENC, Encoders.STRING(), Encoders.STRING())) - .map((MapFunction, Relation>) t -> { - Relation rel = t._1(); - String newSource = t._2(); - String newTarget = t._3(); + .joinWith(idsToMerge, allRels.col("source").equalTo(idsToMerge.col("mergedObjectID")), "left_outer") + .joinWith(idsToMerge, col("_1.target").equalTo(idsToMerge.col("mergedObjectID")), "left_outer") + .select("_1._1", "_1._2.dedupID", "_2.dedupID") + .as(Encoders.tuple(REL_BEAN_ENC, Encoders.STRING(), Encoders.STRING())) + .map((MapFunction, Relation>) t -> { + Relation rel = t._1(); + String newSource = t._2(); + String newTarget = t._3(); - if (rel.getDataInfo() == null) { - rel.setDataInfo(new DataInfo()); - } + if (rel.getDataInfo() == null) { + rel.setDataInfo(new DataInfo()); + } - if (newSource != null || newTarget != null) { - rel.getDataInfo().setDeletedbyinference(false); + if (newSource != null || newTarget != null) { + rel.getDataInfo().setDeletedbyinference(false); - if (newSource != null) - rel.setSource(newSource); + if (newSource != null) + rel.setSource(newSource); - if (newTarget != null) - rel.setTarget(newTarget); - } + if (newTarget != null) + rel.setTarget(newTarget); + } - return rel; - }, REL_BEAN_ENC); + return rel; + }, REL_BEAN_ENC); // ids of records that are both not deletedbyinference and not invisible Dataset ids = validIds(spark, graphBasePath); // filter relations that point to valid records, can force them to be visible Dataset cleanedRels = dedupedRels - .join(ids, col("source").equalTo(ids.col("id")), "leftsemi") - .join(ids, col("target").equalTo(ids.col("id")), "leftsemi") - .as(REL_BEAN_ENC) - .map((MapFunction) r -> { - r.getDataInfo().setInvisible(false); - return r; - }, REL_KRYO_ENC); + .join(ids, col("source").equalTo(ids.col("id")), "leftsemi") + .join(ids, col("target").equalTo(ids.col("id")), "leftsemi") + .as(REL_BEAN_ENC) + .map((MapFunction) r -> { + r.getDataInfo().setInvisible(false); + return r; + }, REL_KRYO_ENC); Dataset distinctRels = cleanedRels .groupByKey( diff --git a/dhp-workflows/dhp-graph-mapper/src/test/java/eu/dnetlib/dhp/oa/graph/group/GroupEntitiesSparkJobTest.java b/dhp-workflows/dhp-graph-mapper/src/test/java/eu/dnetlib/dhp/oa/graph/group/GroupEntitiesSparkJobTest.java index b878e778e..0887adf45 100644 --- a/dhp-workflows/dhp-graph-mapper/src/test/java/eu/dnetlib/dhp/oa/graph/group/GroupEntitiesSparkJobTest.java +++ b/dhp-workflows/dhp-graph-mapper/src/test/java/eu/dnetlib/dhp/oa/graph/group/GroupEntitiesSparkJobTest.java @@ -1,14 +1,14 @@ package eu.dnetlib.dhp.oa.graph.group; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.dnetlib.dhp.common.HdfsSupport; -import eu.dnetlib.dhp.oa.merge.GroupEntitiesSparkJob; -import eu.dnetlib.dhp.schema.common.ModelSupport; -import eu.dnetlib.dhp.schema.oaf.OafEntity; -import eu.dnetlib.dhp.schema.oaf.Result; -import eu.dnetlib.dhp.utils.DHPUtils; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.apache.commons.io.FileUtils; import org.apache.spark.SparkConf; import org.apache.spark.api.java.function.FilterFunction; @@ -18,108 +18,108 @@ import org.apache.spark.sql.Encoders; import org.apache.spark.sql.SparkSession; import org.junit.jupiter.api.*; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; -import static org.junit.jupiter.api.Assertions.assertEquals; +import eu.dnetlib.dhp.common.HdfsSupport; +import eu.dnetlib.dhp.oa.merge.GroupEntitiesSparkJob; +import eu.dnetlib.dhp.schema.common.ModelSupport; +import eu.dnetlib.dhp.schema.oaf.OafEntity; +import eu.dnetlib.dhp.schema.oaf.Result; +import eu.dnetlib.dhp.utils.DHPUtils; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class GroupEntitiesSparkJobTest { - private static SparkSession spark; + private static SparkSession spark; - private static ObjectMapper mapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static ObjectMapper mapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - private static Path workingDir; - private Path dataInputPath; + private static Path workingDir; + private Path dataInputPath; - private Path checkpointPath; + private Path checkpointPath; - private Path outputPath; + private Path outputPath; - @BeforeAll - public static void beforeAll() throws IOException { - workingDir = Files.createTempDirectory(GroupEntitiesSparkJob.class.getSimpleName()); + @BeforeAll + public static void beforeAll() throws IOException { + workingDir = Files.createTempDirectory(GroupEntitiesSparkJob.class.getSimpleName()); - SparkConf conf = new SparkConf(); - conf.setAppName(GroupEntitiesSparkJob.class.getSimpleName()); - conf.setMaster("local"); - conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer"); - conf.registerKryoClasses(ModelSupport.getOafModelClasses()); - spark = SparkSession.builder().config(conf).getOrCreate(); - } + SparkConf conf = new SparkConf(); + conf.setAppName(GroupEntitiesSparkJob.class.getSimpleName()); + conf.setMaster("local"); + conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer"); + conf.registerKryoClasses(ModelSupport.getOafModelClasses()); + spark = SparkSession.builder().config(conf).getOrCreate(); + } - @BeforeEach - public void beforeEach() throws IOException, URISyntaxException { - dataInputPath = Paths.get(ClassLoader.getSystemResource("eu/dnetlib/dhp/oa/graph/group").toURI()); - checkpointPath = workingDir.resolve("grouped_entity"); - outputPath = workingDir.resolve("dispatched_entity"); - } + @BeforeEach + public void beforeEach() throws IOException, URISyntaxException { + dataInputPath = Paths.get(ClassLoader.getSystemResource("eu/dnetlib/dhp/oa/graph/group").toURI()); + checkpointPath = workingDir.resolve("grouped_entity"); + outputPath = workingDir.resolve("dispatched_entity"); + } - @AfterAll - public static void afterAll() throws IOException { - spark.stop(); - FileUtils.deleteDirectory(workingDir.toFile()); - } + @AfterAll + public static void afterAll() throws IOException { + spark.stop(); + FileUtils.deleteDirectory(workingDir.toFile()); + } - @Test - @Order(1) - void testGroupEntities() throws Exception { - GroupEntitiesSparkJob.main(new String[]{ - "-isSparkSessionManaged", - Boolean.FALSE.toString(), - "-graphInputPath", - dataInputPath.toString(), - "-checkpointPath", - checkpointPath.toString(), - "-outputPath", - outputPath.toString(), - "-filterInvisible", - Boolean.FALSE.toString() - }); + @Test + @Order(1) + void testGroupEntities() throws Exception { + GroupEntitiesSparkJob.main(new String[] { + "-isSparkSessionManaged", + Boolean.FALSE.toString(), + "-graphInputPath", + dataInputPath.toString(), + "-checkpointPath", + checkpointPath.toString(), + "-outputPath", + outputPath.toString(), + "-filterInvisible", + Boolean.FALSE.toString() + }); - Dataset checkpointTable = spark - .read() - .load(checkpointPath.toString()) - .selectExpr("COALESCE(*)") - .as(Encoders.kryo(OafEntity.class)); + Dataset checkpointTable = spark + .read() + .load(checkpointPath.toString()) + .selectExpr("COALESCE(*)") + .as(Encoders.kryo(OafEntity.class)); + assertEquals( + 1, + checkpointTable + .filter( + (FilterFunction) r -> "50|doi_________::09821844208a5cd6300b2bfb13bca1b9" + .equals(r.getId()) && + r.getCollectedfrom().stream().anyMatch(kv -> kv.getValue().equalsIgnoreCase("zenodo"))) + .count()); - assertEquals( - 1, - checkpointTable - .filter( - (FilterFunction) r -> "50|doi_________::09821844208a5cd6300b2bfb13bca1b9" - .equals(r.getId()) && - r.getCollectedfrom().stream().anyMatch(kv -> kv.getValue().equalsIgnoreCase("zenodo"))) - .count()); + Dataset output = spark + .read() + .textFile( + DHPUtils + .toSeq( + HdfsSupport + .listFiles(outputPath.toString(), spark.sparkContext().hadoopConfiguration()))) + .map((MapFunction) s -> mapper.readValue(s, Result.class), Encoders.bean(Result.class)); - - Dataset output = spark - .read() - .textFile( - DHPUtils - .toSeq( - HdfsSupport - .listFiles(outputPath.toString(), spark.sparkContext().hadoopConfiguration()))) - .map((MapFunction) s -> mapper.readValue(s, Result.class), Encoders.bean(Result.class)); - - assertEquals(3, output.count()); - assertEquals( - 2, - output - .map((MapFunction) r -> r.getResulttype().getClassid(), Encoders.STRING()) - .filter((FilterFunction) s -> s.equals("publication")) - .count()); - assertEquals( - 1, - output - .map((MapFunction) r -> r.getResulttype().getClassid(), Encoders.STRING()) - .filter((FilterFunction) s -> s.equals("dataset")) - .count()); - } -} \ No newline at end of file + assertEquals(3, output.count()); + assertEquals( + 2, + output + .map((MapFunction) r -> r.getResulttype().getClassid(), Encoders.STRING()) + .filter((FilterFunction) s -> s.equals("publication")) + .count()); + assertEquals( + 1, + output + .map((MapFunction) r -> r.getResulttype().getClassid(), Encoders.STRING()) + .filter((FilterFunction) s -> s.equals("dataset")) + .count()); + } +} From f759b18bca5260ff61a4603a023aa484585e05fe Mon Sep 17 00:00:00 2001 From: Claudio Atzori Date: Fri, 6 Oct 2023 13:43:20 +0200 Subject: [PATCH 14/16] [SWH] aligned parameter name --- .../resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml index 64dc0d2aa..c625fcb5b 100644 --- a/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml +++ b/dhp-workflows/dhp-swh/src/main/resources/eu/dnetlib/dhp/swh/oozie_app/workflow.xml @@ -47,7 +47,7 @@ Limit on the number of repo URLs to use (Optional); for debug purposes - resume + resumeFrom Variable that indicates the step to start from @@ -80,8 +80,8 @@ - ${wf:conf('startFrom') eq 'collect-software-repository-urls'} - ${wf:conf('startFrom') eq 'create-swh-actionsets'} + ${wf:conf('resumeFrom') eq 'collect-software-repository-urls'} + ${wf:conf('resumeFrom') eq 'create-swh-actionsets'} From 858931ccb63ee47849129686cbfe3406cee4f997 Mon Sep 17 00:00:00 2001 From: Claudio Atzori Date: Fri, 6 Oct 2023 14:03:33 +0200 Subject: [PATCH 15/16] [SWH] compress the output actionset --- .../src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java index 2239244d6..2691d4b7e 100644 --- a/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java +++ b/dhp-workflows/dhp-swh/src/main/java/eu/dnetlib/dhp/swh/PrepareSWHActionsets.java @@ -11,6 +11,7 @@ import java.util.Optional; import org.apache.commons.io.IOUtils; import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.compress.GzipCodec; import org.apache.hadoop.mapred.SequenceFileOutputFormat; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaPairRDD; @@ -81,7 +82,7 @@ public class PrepareSWHActionsets { JavaPairRDD softwareRDD = prepareActionsets(spark, inputPath, softwareInputPath); softwareRDD .saveAsHadoopFile( - outputPath, Text.class, Text.class, SequenceFileOutputFormat.class); + outputPath, Text.class, Text.class, SequenceFileOutputFormat.class, GzipCodec.class); }); } From 2f3cf6d0e700b3a96c493e1d81cb2274bb45bad2 Mon Sep 17 00:00:00 2001 From: Giambattista Bloisi Date: Fri, 6 Oct 2023 12:35:54 +0200 Subject: [PATCH 16/16] Fix cleaning of Pmid where parsing of numbers stopped at first not leading 0' character --- .../eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRule.java | 4 ++-- .../dnetlib/dhp/schema/oaf/utils/PmidCleaningRuleTest.java | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dhp-common/src/main/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRule.java b/dhp-common/src/main/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRule.java index d0f5a3b27..c0c451b88 100644 --- a/dhp-common/src/main/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRule.java +++ b/dhp-common/src/main/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRule.java @@ -7,7 +7,7 @@ import java.util.regex.Pattern; // https://researchguides.stevens.edu/c.php?g=442331&p=6577176 public class PmidCleaningRule { - public static final Pattern PATTERN = Pattern.compile("[1-9]{1,8}"); + public static final Pattern PATTERN = Pattern.compile("0*(\\d{1,8})"); public static String clean(String pmid) { String s = pmid @@ -17,7 +17,7 @@ public class PmidCleaningRule { final Matcher m = PATTERN.matcher(s); if (m.find()) { - return m.group(); + return m.group(1); } return ""; } diff --git a/dhp-common/src/test/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRuleTest.java b/dhp-common/src/test/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRuleTest.java index 9562adf7e..295eac85f 100644 --- a/dhp-common/src/test/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRuleTest.java +++ b/dhp-common/src/test/java/eu/dnetlib/dhp/schema/oaf/utils/PmidCleaningRuleTest.java @@ -9,10 +9,16 @@ class PmidCleaningRuleTest { @Test void testCleaning() { + // leading zeros are removed assertEquals("1234", PmidCleaningRule.clean("01234")); + // tolerant to spaces in the middle assertEquals("1234567", PmidCleaningRule.clean("0123 4567")); + // stop parsing at first not numerical char assertEquals("123", PmidCleaningRule.clean("0123x4567")); + // invalid id leading to empty result assertEquals("", PmidCleaningRule.clean("abc")); + // valid id with zeroes in the number + assertEquals("20794075", PmidCleaningRule.clean("20794075")); } }