Revize e21b7441
Přidáno uživatelem Jakub Šmíd před více než 2 roky(ů)
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/CatalogItem.java | ||
---|---|---|
115 | 115 |
) |
116 | 116 |
private Set<Type> types = new HashSet<>(); |
117 | 117 |
|
118 |
public CatalogItem(final List<String> csvFields) {
|
|
118 |
public CatalogItem(List<String> csvFields) { |
|
119 | 119 |
|
120 | 120 |
this.name = csvFields.get(1); |
121 | 121 |
Set<String> stringList = processListField(csvFields.get(2)); |
backend/src/main/java/cz/zcu/kiv/backendapi/catalog/ICatalogItemService.java | ||
---|---|---|
6 | 6 |
import java.util.UUID; |
7 | 7 |
|
8 | 8 |
/** |
9 |
* Catalog service interface |
|
9 |
* Catalog item service interface
|
|
10 | 10 |
*/ |
11 | 11 |
public interface ICatalogItemService { |
12 | 12 |
/** |
backend/src/main/java/cz/zcu/kiv/backendapi/external/ExternalCatalogController.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
import lombok.RequiredArgsConstructor; |
|
4 |
import org.springframework.web.bind.annotation.PostMapping; |
|
5 |
import org.springframework.web.bind.annotation.RequestMapping; |
|
6 |
import org.springframework.web.bind.annotation.RestController; |
|
7 |
|
|
8 |
/** |
|
9 |
* Controller for external catalog |
|
10 |
*/ |
|
11 |
@RestController |
|
12 |
@RequiredArgsConstructor |
|
13 |
@RequestMapping("/external-catalog-items") |
|
14 |
public class ExternalCatalogController { |
|
15 |
|
|
16 |
/** |
|
17 |
* External catalog service |
|
18 |
*/ |
|
19 |
private final IExternalCatalogService externalCatalogService; |
|
20 |
|
|
21 |
@PostMapping("") |
|
22 |
public void updateCatalog() { |
|
23 |
externalCatalogService.updateCatalog(); |
|
24 |
} |
|
25 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/ExternalCatalogItem.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
import lombok.EqualsAndHashCode; |
|
4 |
import lombok.Getter; |
|
5 |
import lombok.NoArgsConstructor; |
|
6 |
import lombok.Setter; |
|
7 |
|
|
8 |
import javax.persistence.*; |
|
9 |
import java.util.*; |
|
10 |
import java.util.regex.Matcher; |
|
11 |
import java.util.regex.Pattern; |
|
12 |
import java.util.stream.Collectors; |
|
13 |
|
|
14 |
/** |
|
15 |
* Class representing external catalog items |
|
16 |
*/ |
|
17 |
@Getter |
|
18 |
@Setter |
|
19 |
@EqualsAndHashCode(onlyExplicitlyIncluded = true) |
|
20 |
@NoArgsConstructor |
|
21 |
@Entity |
|
22 |
@Table(name = "external_catalog_items") |
|
23 |
public class ExternalCatalogItem { |
|
24 |
/** |
|
25 |
* Map where key is country code and value is name of country |
|
26 |
*/ |
|
27 |
protected static final Map<String, String> COUNTRY_CODES; |
|
28 |
|
|
29 |
/** |
|
30 |
* Integer pattern for finding int value in string |
|
31 |
*/ |
|
32 |
private static final Pattern INTEGER_PATTERN = Pattern.compile("\\d+"); |
|
33 |
|
|
34 |
/** |
|
35 |
* Double pattern for finding double value in string |
|
36 |
*/ |
|
37 |
private static final Pattern DOUBLE_PATTERN = Pattern.compile("(\\d+[.]\\d+)|(\\d+)"); |
|
38 |
|
|
39 |
static { |
|
40 |
COUNTRY_CODES = new LinkedHashMap<>(); |
|
41 |
COUNTRY_CODES.put("IQ", "Iraq"); |
|
42 |
COUNTRY_CODES.put("IR", "Iran"); |
|
43 |
COUNTRY_CODES.put("SY", "Syria"); |
|
44 |
COUNTRY_CODES.put("TR", "Turkey"); |
|
45 |
COUNTRY_CODES.put("JO", "Jordan"); |
|
46 |
COUNTRY_CODES.put("LB", "Lebanon"); |
|
47 |
COUNTRY_CODES.put("IL", "Israel"); |
|
48 |
COUNTRY_CODES.put("PS", "Palestine"); |
|
49 |
COUNTRY_CODES.put("CY", "Cyprus"); |
|
50 |
COUNTRY_CODES.put("EG", "Egypt"); |
|
51 |
COUNTRY_CODES.put("SD", "Sudan"); |
|
52 |
COUNTRY_CODES.put("BH", "Bahrain"); |
|
53 |
COUNTRY_CODES.put("KW", "Kuwait"); |
|
54 |
COUNTRY_CODES.put("OM", "Oman"); |
|
55 |
COUNTRY_CODES.put("SA", "Saudi Arabia"); |
|
56 |
COUNTRY_CODES.put("AE", "United Arab Emirates"); |
|
57 |
COUNTRY_CODES.put("YE", "Yemen"); |
|
58 |
COUNTRY_CODES.put("AF", "Afghanistan"); |
|
59 |
COUNTRY_CODES.put("AM", "Armenia"); |
|
60 |
COUNTRY_CODES.put("GE", "Georgia"); |
|
61 |
} |
|
62 |
|
|
63 |
/** |
|
64 |
* Id |
|
65 |
*/ |
|
66 |
@Id |
|
67 |
@EqualsAndHashCode.Include |
|
68 |
UUID id = UUID.randomUUID(); |
|
69 |
|
|
70 |
/** |
|
71 |
* External source |
|
72 |
*/ |
|
73 |
@Enumerated(EnumType.STRING) |
|
74 |
private ExternalSource externalSource; |
|
75 |
|
|
76 |
/** |
|
77 |
* Latitude |
|
78 |
*/ |
|
79 |
private Double latitude; |
|
80 |
|
|
81 |
/** |
|
82 |
* Longitude |
|
83 |
*/ |
|
84 |
private Double longitude; |
|
85 |
|
|
86 |
/** |
|
87 |
* Precision of location |
|
88 |
*/ |
|
89 |
private String locationPrecision; |
|
90 |
|
|
91 |
/** |
|
92 |
* Max date |
|
93 |
*/ |
|
94 |
private Integer maxDate; |
|
95 |
|
|
96 |
/** |
|
97 |
* Min date |
|
98 |
*/ |
|
99 |
private Integer minDate; |
|
100 |
|
|
101 |
/** |
|
102 |
* Keys for time periods |
|
103 |
*/ |
|
104 |
@Column(length = 1000) |
|
105 |
private String timePeriodKeys; |
|
106 |
|
|
107 |
/** |
|
108 |
* Pid |
|
109 |
*/ |
|
110 |
private String pid; |
|
111 |
|
|
112 |
/** |
|
113 |
* Set of names |
|
114 |
*/ |
|
115 |
@Convert(converter = SetToStringConverter.class) |
|
116 |
@Column(length = 20000) |
|
117 |
private Set<String> names = new HashSet<>(); |
|
118 |
|
|
119 |
/** |
|
120 |
* Feature code |
|
121 |
*/ |
|
122 |
private String featureCode; |
|
123 |
|
|
124 |
/** |
|
125 |
* Country |
|
126 |
*/ |
|
127 |
private String country; |
|
128 |
|
|
129 |
/** |
|
130 |
* Accuracy |
|
131 |
*/ |
|
132 |
private Integer accuracy; |
|
133 |
|
|
134 |
/** |
|
135 |
* GeoName ID |
|
136 |
*/ |
|
137 |
private Long geonameId; |
|
138 |
|
|
139 |
/** |
|
140 |
* Pleiades ID |
|
141 |
*/ |
|
142 |
private Long pleiadesId; |
|
143 |
|
|
144 |
/** |
|
145 |
* Osm ID |
|
146 |
*/ |
|
147 |
private Long osmId; |
|
148 |
|
|
149 |
/** |
|
150 |
* Creates new external catalog item from list of string containing information and catalog source type |
|
151 |
* |
|
152 |
* @param fieldList list of string containing information about the catalog item |
|
153 |
* @param externalSource external source type |
|
154 |
*/ |
|
155 |
public ExternalCatalogItem(List<String> fieldList, ExternalSource externalSource) { |
|
156 |
this.externalSource = externalSource; |
|
157 |
if (externalSource == ExternalSource.PLEIADES) { |
|
158 |
this.locationPrecision = fieldList.get(8); |
|
159 |
this.maxDate = processIntField(fieldList.get(9)); |
|
160 |
this.minDate = processIntField(fieldList.get(10)); |
|
161 |
this.pid = fieldList.get(16); |
|
162 |
this.latitude = processDoubleField(fieldList.get(17)); |
|
163 |
this.longitude = processDoubleField(fieldList.get(18)); |
|
164 |
this.timePeriodKeys = fieldList.get(22); |
|
165 |
this.names.add(fieldList.get(24)); |
|
166 |
this.names.addAll(processListField(fieldList.get(12))); |
|
167 |
this.names.addAll(processListField(fieldList.get(14))); |
|
168 |
} else if (externalSource == ExternalSource.GEONAMES) { |
|
169 |
this.geonameId = processLongField(fieldList.get(0)); |
|
170 |
this.names.add(fieldList.get(1)); |
|
171 |
this.names.add(fieldList.get(2)); |
|
172 |
this.names.addAll(processListField(fieldList.get(3))); |
|
173 |
this.latitude = processDoubleField(fieldList.get(4)); |
|
174 |
this.longitude = processDoubleField(fieldList.get(5)); |
|
175 |
this.featureCode = fieldList.get(7); |
|
176 |
this.country = convertCountryCode(fieldList.get(8)); |
|
177 |
} else if (externalSource == ExternalSource.CIGS) { |
|
178 |
this.accuracy = processIntField(fieldList.get(1)); |
|
179 |
this.names.add(fieldList.get(3)); |
|
180 |
this.names.add(fieldList.get(4)); |
|
181 |
this.names.add(fieldList.get(5)); |
|
182 |
this.names.add(fieldList.get(6)); |
|
183 |
this.names.add(fieldList.get(7)); |
|
184 |
this.names.add(fieldList.get(8)); |
|
185 |
this.names.add(fieldList.get(9)); |
|
186 |
this.names.add(fieldList.get(10)); |
|
187 |
this.names.add(fieldList.get(11)); |
|
188 |
this.pleiadesId = processLongField(fieldList.get(12)); |
|
189 |
this.osmId = processLongField(fieldList.get(13)); |
|
190 |
this.geonameId = processLongField(fieldList.get(15)); |
|
191 |
this.longitude = processDoubleField(fieldList.get(23)); |
|
192 |
this.latitude = processDoubleField(fieldList.get(24)); |
|
193 |
} |
|
194 |
} |
|
195 |
|
|
196 |
/** |
|
197 |
* Converts country code to country name |
|
198 |
* |
|
199 |
* @param countryCode country code |
|
200 |
* @return country name |
|
201 |
*/ |
|
202 |
private String convertCountryCode(String countryCode) { |
|
203 |
if (COUNTRY_CODES.containsKey(countryCode)) { |
|
204 |
return COUNTRY_CODES.get(countryCode); |
|
205 |
} |
|
206 |
return countryCode; |
|
207 |
} |
|
208 |
|
|
209 |
/** |
|
210 |
* Processes field of type long |
|
211 |
* |
|
212 |
* @param field field |
|
213 |
* @return long if long found in field, null otherwise |
|
214 |
*/ |
|
215 |
private Long processLongField(String field) { |
|
216 |
Matcher matcher = INTEGER_PATTERN.matcher(field); |
|
217 |
if (matcher.find()) { |
|
218 |
return Long.parseLong(matcher.group()); |
|
219 |
} else { |
|
220 |
return null; |
|
221 |
} |
|
222 |
} |
|
223 |
|
|
224 |
/** |
|
225 |
* Processes field of type int |
|
226 |
* |
|
227 |
* @param field field |
|
228 |
* @return int if int found in field, null otherwise |
|
229 |
*/ |
|
230 |
private Integer processIntField(String field) { |
|
231 |
Matcher matcher = INTEGER_PATTERN.matcher(field); |
|
232 |
if (matcher.find()) { |
|
233 |
return Integer.parseInt(matcher.group()); |
|
234 |
} else { |
|
235 |
return null; |
|
236 |
} |
|
237 |
} |
|
238 |
|
|
239 |
/** |
|
240 |
* Processes field of type double |
|
241 |
* |
|
242 |
* @param field field |
|
243 |
* @return double if double found in field, null otherwise |
|
244 |
*/ |
|
245 |
private Double processDoubleField(String field) { |
|
246 |
Matcher matcher = DOUBLE_PATTERN.matcher(field); |
|
247 |
if (matcher.find()) { |
|
248 |
return Double.parseDouble(matcher.group()); |
|
249 |
} else { |
|
250 |
return null; |
|
251 |
} |
|
252 |
} |
|
253 |
|
|
254 |
/** |
|
255 |
* Processes list field |
|
256 |
* |
|
257 |
* @param field list field (string seperated by commas) |
|
258 |
* @return set from list field |
|
259 |
*/ |
|
260 |
private Set<String> processListField(String field) { |
|
261 |
if (field.isEmpty()) { |
|
262 |
return new HashSet<>(); |
|
263 |
} |
|
264 |
return Arrays.stream(field.split(",")).map(String::trim).filter(item -> !item.isEmpty()).collect(Collectors.toSet()); |
|
265 |
} |
|
266 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/ExternalCatalogRepository.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
import org.springframework.data.jpa.repository.JpaRepository; |
|
4 |
import org.springframework.stereotype.Repository; |
|
5 |
|
|
6 |
import java.util.UUID; |
|
7 |
|
|
8 |
/** |
|
9 |
* External catalog repository |
|
10 |
*/ |
|
11 |
@Repository |
|
12 |
public interface ExternalCatalogRepository extends JpaRepository<ExternalCatalogItem, UUID> { |
|
13 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/ExternalCatalogServiceImpl.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
import com.zaxxer.hikari.HikariDataSource; |
|
4 |
import lombok.RequiredArgsConstructor; |
|
5 |
import lombok.extern.slf4j.Slf4j; |
|
6 |
import org.apache.commons.csv.CSVFormat; |
|
7 |
import org.apache.commons.csv.CSVParser; |
|
8 |
import org.apache.commons.csv.CSVRecord; |
|
9 |
import org.apache.tomcat.util.http.fileupload.FileUtils; |
|
10 |
import org.springframework.stereotype.Service; |
|
11 |
import org.springframework.transaction.annotation.Transactional; |
|
12 |
|
|
13 |
import javax.persistence.Table; |
|
14 |
import java.io.*; |
|
15 |
import java.net.URL; |
|
16 |
import java.nio.charset.StandardCharsets; |
|
17 |
import java.nio.file.Files; |
|
18 |
import java.nio.file.Paths; |
|
19 |
import java.sql.Connection; |
|
20 |
import java.sql.PreparedStatement; |
|
21 |
import java.util.ArrayList; |
|
22 |
import java.util.List; |
|
23 |
import java.util.concurrent.Callable; |
|
24 |
import java.util.concurrent.ExecutorService; |
|
25 |
import java.util.concurrent.Executors; |
|
26 |
import java.util.stream.Collectors; |
|
27 |
import java.util.zip.GZIPInputStream; |
|
28 |
import java.util.zip.ZipEntry; |
|
29 |
import java.util.zip.ZipInputStream; |
|
30 |
|
|
31 |
/** |
|
32 |
* External catalog service implementation |
|
33 |
*/ |
|
34 |
@Service |
|
35 |
@Transactional |
|
36 |
@RequiredArgsConstructor |
|
37 |
@Slf4j |
|
38 |
public class ExternalCatalogServiceImpl implements IExternalCatalogService { |
|
39 |
/** |
|
40 |
* Buffer size |
|
41 |
*/ |
|
42 |
public static final int BUFFER_SIZE = 1024; |
|
43 |
/** |
|
44 |
* Directory where files for external directory will be stored |
|
45 |
*/ |
|
46 |
private static final String DIRECTORY_FOR_EXTERNAL_FILES = "sources"; |
|
47 |
/** |
|
48 |
* URL for Pleiades file |
|
49 |
*/ |
|
50 |
private static final String PLEIADES_FILE_URL = "https://atlantides.org/downloads/pleiades/dumps/pleiades-names-latest.csv.gz"; |
|
51 |
/** |
|
52 |
* Name of Pleiades file |
|
53 |
*/ |
|
54 |
private static final String PLEIADES_FILE_NAME = "pleiades-names-latest.csv"; |
|
55 |
/** |
|
56 |
* URL for Pleiades file – needs to be formatted (more sources) |
|
57 |
*/ |
|
58 |
private static final String GEONAMES_FILE_URL = "https://download.geonames.org/export/dump/%s.zip"; |
|
59 |
/** |
|
60 |
* Name of GeoNames file – needs to be formatted (more sources) |
|
61 |
*/ |
|
62 |
private static final String GEONAMES_FILE_NAME = "%s.txt"; |
|
63 |
/** |
|
64 |
* URL for CIGS file |
|
65 |
*/ |
|
66 |
private static final String CIGS_FILE_URL = "https://zenodo.org/record/5642899/files/CIGS_v1_4_20211101.csv"; |
|
67 |
/** |
|
68 |
* Name of CIGS file |
|
69 |
*/ |
|
70 |
private static final String CIGS_FILE_NAME = "CIGS_v1_4_20211101.csv"; |
|
71 |
/** |
|
72 |
* Batch size for saving items |
|
73 |
*/ |
|
74 |
private static final int BATCH_SIZE = 5000; |
|
75 |
|
|
76 |
/** |
|
77 |
* External catalog repository |
|
78 |
*/ |
|
79 |
private final ExternalCatalogRepository externalCatalogRepository; |
|
80 |
|
|
81 |
/** |
|
82 |
* Hikari data source |
|
83 |
*/ |
|
84 |
private final HikariDataSource hikariDataSource; |
|
85 |
|
|
86 |
|
|
87 |
@Override |
|
88 |
public void updateCatalog() { |
|
89 |
log.info("Updating external catalog"); |
|
90 |
try { |
|
91 |
Files.createDirectories(Paths.get(DIRECTORY_FOR_EXTERNAL_FILES)); // creates directory if not exists |
|
92 |
FileUtils.cleanDirectory(new File(DIRECTORY_FOR_EXTERNAL_FILES)); // cleans the directory |
|
93 |
externalCatalogRepository.deleteAll(); // clears database – updated list will be stored later |
|
94 |
addPleiadesSource(); |
|
95 |
addGeonamesSources(); |
|
96 |
addCigsSources(); |
|
97 |
} catch (IOException e) { |
|
98 |
e.printStackTrace(); |
|
99 |
} |
|
100 |
log.info("External catalog updated"); |
|
101 |
} |
|
102 |
|
|
103 |
/** |
|
104 |
* Downloads, extracts and reads Pleiades sources and saves them to database |
|
105 |
*/ |
|
106 |
private void addPleiadesSource() { |
|
107 |
List<ExternalCatalogItem> externalCatalogItems = new ArrayList<>(); |
|
108 |
byte[] buffer = new byte[BUFFER_SIZE]; |
|
109 |
File pleiadesFile = new File(new File(DIRECTORY_FOR_EXTERNAL_FILES), PLEIADES_FILE_NAME); |
|
110 |
try (InputStream fileIn = new URL(PLEIADES_FILE_URL).openStream(); |
|
111 |
GZIPInputStream gZIPInputStream = new GZIPInputStream(fileIn); |
|
112 |
FileOutputStream fileOutputStream = new FileOutputStream(pleiadesFile)) { |
|
113 |
|
|
114 |
int bytes_read; |
|
115 |
|
|
116 |
while ((bytes_read = gZIPInputStream.read(buffer)) > 0) { |
|
117 |
fileOutputStream.write(buffer, 0, bytes_read); |
|
118 |
} |
|
119 |
|
|
120 |
log.info("The Pleiades file was decompressed successfully"); |
|
121 |
|
|
122 |
} catch (IOException ex) { |
|
123 |
ex.printStackTrace(); |
|
124 |
} |
|
125 |
|
|
126 |
try (InputStream csvData = new FileInputStream(pleiadesFile)) { |
|
127 |
CSVParser parser = CSVParser.parse(csvData, StandardCharsets.UTF_8, CSVFormat.Builder.create(CSVFormat.DEFAULT) |
|
128 |
.setHeader() |
|
129 |
.setSkipHeaderRecord(true) |
|
130 |
.build()); |
|
131 |
for (CSVRecord csvRecord : parser) { |
|
132 |
externalCatalogItems.add(new ExternalCatalogItem(csvRecord.toList(), ExternalSource.PLEIADES)); |
|
133 |
} |
|
134 |
|
|
135 |
} catch (IOException ex) { |
|
136 |
ex.printStackTrace(); |
|
137 |
} |
|
138 |
saveAllWithThreads(externalCatalogItems); |
|
139 |
} |
|
140 |
|
|
141 |
/** |
|
142 |
* Downloads, extracts and reads GeoNames sources and saves them to database |
|
143 |
*/ |
|
144 |
private void addGeonamesSources() { |
|
145 |
byte[] buffer = new byte[BUFFER_SIZE]; |
|
146 |
for (String countryCode : ExternalCatalogItem.COUNTRY_CODES.keySet()) { |
|
147 |
List<ExternalCatalogItem> externalCatalogItems = new ArrayList<>(); |
|
148 |
// Downloads file from URL and extracts it |
|
149 |
String url = String.format(GEONAMES_FILE_URL, countryCode); |
|
150 |
try (ZipInputStream zis = new ZipInputStream(new URL(url).openStream())) { |
|
151 |
ZipEntry zipEntry = zis.getNextEntry(); |
|
152 |
while (zipEntry != null) { |
|
153 |
FileOutputStream fileOutputStream = new FileOutputStream(new File(new File(DIRECTORY_FOR_EXTERNAL_FILES), zipEntry.getName())); |
|
154 |
int bytes_read; |
|
155 |
while ((bytes_read = zis.read(buffer)) > 0) { |
|
156 |
fileOutputStream.write(buffer, 0, bytes_read); |
|
157 |
} |
|
158 |
zipEntry = zis.getNextEntry(); |
|
159 |
fileOutputStream.close(); |
|
160 |
} |
|
161 |
} catch (IOException e) { |
|
162 |
e.printStackTrace(); |
|
163 |
} |
|
164 |
|
|
165 |
log.info("The Geonames file with country code " + countryCode + " was decompressed successfully"); |
|
166 |
|
|
167 |
// Reads file and adds catalog items to list |
|
168 |
File geoNamesFile = new File(new File(DIRECTORY_FOR_EXTERNAL_FILES), String.format(GEONAMES_FILE_NAME, countryCode)); |
|
169 |
try (BufferedReader reader = new BufferedReader(new FileReader(geoNamesFile))) { |
|
170 |
String line; |
|
171 |
while ((line = reader.readLine()) != null) { |
|
172 |
externalCatalogItems.add(new ExternalCatalogItem(List.of(line.split("\t")), ExternalSource.GEONAMES)); |
|
173 |
} |
|
174 |
} catch (IOException e) { |
|
175 |
e.printStackTrace(); |
|
176 |
} |
|
177 |
saveAllWithThreads(externalCatalogItems); |
|
178 |
} |
|
179 |
} |
|
180 |
|
|
181 |
/** |
|
182 |
* Downloads and reads CIGS sources and saves them to database |
|
183 |
*/ |
|
184 |
private void addCigsSources() { |
|
185 |
List<ExternalCatalogItem> externalCatalogItems = new ArrayList<>(); |
|
186 |
byte[] buffer = new byte[BUFFER_SIZE]; |
|
187 |
File cigsFile = new File(new File(DIRECTORY_FOR_EXTERNAL_FILES), CIGS_FILE_NAME); |
|
188 |
|
|
189 |
try (InputStream inputStream = new URL(CIGS_FILE_URL).openStream(); |
|
190 |
FileOutputStream fileOutputStream = new FileOutputStream(cigsFile)) { |
|
191 |
int bytes_read; |
|
192 |
|
|
193 |
while ((bytes_read = inputStream.read(buffer)) > 0) { |
|
194 |
|
|
195 |
fileOutputStream.write(buffer, 0, bytes_read); |
|
196 |
} |
|
197 |
} catch (IOException e) { |
|
198 |
e.printStackTrace(); |
|
199 |
} |
|
200 |
|
|
201 |
log.info("The CIGS file was downloaded successfully"); |
|
202 |
|
|
203 |
try (InputStream csvData = new FileInputStream(cigsFile)) { |
|
204 |
CSVParser parser = CSVParser.parse(csvData, StandardCharsets.UTF_8, CSVFormat.Builder.create(CSVFormat.DEFAULT) |
|
205 |
.setHeader() |
|
206 |
.setSkipHeaderRecord(true) |
|
207 |
.build()); |
|
208 |
for (CSVRecord csvRecord : parser) { |
|
209 |
externalCatalogItems.add(new ExternalCatalogItem(csvRecord.toList(), ExternalSource.CIGS)); |
|
210 |
} |
|
211 |
} catch (IOException e) { |
|
212 |
e.printStackTrace(); |
|
213 |
} |
|
214 |
saveAllWithThreads(externalCatalogItems); |
|
215 |
} |
|
216 |
|
|
217 |
/** |
|
218 |
* Creates list of lists of external catalog items divided by batch size |
|
219 |
* |
|
220 |
* @param externalCatalogItems list of external catalog items |
|
221 |
* @return divided list of lists of external catalog items |
|
222 |
*/ |
|
223 |
private List<List<ExternalCatalogItem>> createSublist(List<ExternalCatalogItem> externalCatalogItems) { |
|
224 |
List<List<ExternalCatalogItem>> listOfSubList = new ArrayList<>(); |
|
225 |
for (int i = 0; i < externalCatalogItems.size(); i += BATCH_SIZE) { |
|
226 |
if (i + BATCH_SIZE <= externalCatalogItems.size()) { |
|
227 |
listOfSubList.add(externalCatalogItems.subList(i, i + BATCH_SIZE)); |
|
228 |
} else { |
|
229 |
listOfSubList.add(externalCatalogItems.subList(i, externalCatalogItems.size())); |
|
230 |
} |
|
231 |
} |
|
232 |
return listOfSubList; |
|
233 |
} |
|
234 |
|
|
235 |
/** |
|
236 |
* Divides list of external catalog items to sublist, creates threads (for saving sublists in batch) and executes them |
|
237 |
* |
|
238 |
* @param externalCatalogItems list of external catalog items |
|
239 |
*/ |
|
240 |
private void saveAllWithThreads(List<ExternalCatalogItem> externalCatalogItems) { |
|
241 |
ExecutorService executorService = Executors.newFixedThreadPool(hikariDataSource.getMaximumPoolSize()); |
|
242 |
List<List<ExternalCatalogItem>> subList = createSublist(externalCatalogItems); |
|
243 |
List<Callable<Void>> callables = subList.stream().map(sublist -> |
|
244 |
(Callable<Void>) () -> { |
|
245 |
saveAllInBatch(sublist); |
|
246 |
return null; |
|
247 |
}).collect(Collectors.toList()); |
|
248 |
try { |
|
249 |
executorService.invokeAll(callables); |
|
250 |
} catch (InterruptedException e) { |
|
251 |
e.printStackTrace(); |
|
252 |
} |
|
253 |
} |
|
254 |
|
|
255 |
/** |
|
256 |
* Saves external catalog items in batch |
|
257 |
* |
|
258 |
* @param externalCatalogItems list of external catalog items |
|
259 |
*/ |
|
260 |
private void saveAllInBatch(List<ExternalCatalogItem> externalCatalogItems) { |
|
261 |
String sql = String.format("INSERT INTO %s (id, external_source, latitude, longitude, location_precision, max_date, " + |
|
262 |
"min_date, time_period_keys, pid, names, feature_code, country, accuracy, geoname_id, pleiades_id, osm_id) " + |
|
263 |
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ExternalCatalogItem.class.getAnnotation(Table.class).name()); |
|
264 |
try (Connection connection = hikariDataSource.getConnection(); |
|
265 |
PreparedStatement statement = connection.prepareStatement(sql)) { |
|
266 |
int counter = 0; |
|
267 |
for (ExternalCatalogItem item : externalCatalogItems) { |
|
268 |
statement.clearParameters(); |
|
269 |
statement.setObject(1, item.getId()); |
|
270 |
statement.setString(2, item.getExternalSource().name()); |
|
271 |
statement.setObject(3, item.getLatitude()); |
|
272 |
statement.setObject(4, item.getLongitude()); |
|
273 |
statement.setString(5, item.getLocationPrecision()); |
|
274 |
statement.setObject(6, item.getMaxDate()); |
|
275 |
statement.setObject(7, item.getMinDate()); |
|
276 |
statement.setString(8, item.getTimePeriodKeys()); |
|
277 |
statement.setString(9, item.getPid()); |
|
278 |
statement.setString(10, String.join(",", item.getNames())); |
|
279 |
statement.setString(11, item.getFeatureCode()); |
|
280 |
statement.setString(12, item.getCountry()); |
|
281 |
statement.setObject(13, item.getAccuracy()); |
|
282 |
statement.setObject(14, item.getGeonameId()); |
|
283 |
statement.setObject(15, item.getPleiadesId()); |
|
284 |
statement.setObject(16, item.getOsmId()); |
|
285 |
statement.addBatch(); |
|
286 |
counter++; |
|
287 |
if (counter % BATCH_SIZE == 0 || counter == externalCatalogItems.size()) { |
|
288 |
statement.executeBatch(); |
|
289 |
statement.clearBatch(); |
|
290 |
} |
|
291 |
} |
|
292 |
} catch (Exception e) { |
|
293 |
e.printStackTrace(); |
|
294 |
} |
|
295 |
} |
|
296 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/ExternalSource.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
/** |
|
4 |
* Enum representing different sources of external catalog items |
|
5 |
*/ |
|
6 |
public enum ExternalSource { |
|
7 |
PLEIADES, |
|
8 |
GEONAMES, |
|
9 |
CIGS |
|
10 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/IExternalCatalogService.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
/** |
|
4 |
* External catalog service interface |
|
5 |
*/ |
|
6 |
public interface IExternalCatalogService { |
|
7 |
/** |
|
8 |
* Updates external catalog |
|
9 |
*/ |
|
10 |
void updateCatalog(); |
|
11 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/external/SetToStringConverter.java | ||
---|---|---|
1 |
package cz.zcu.kiv.backendapi.external; |
|
2 |
|
|
3 |
import javax.persistence.AttributeConverter; |
|
4 |
import java.util.Arrays; |
|
5 |
import java.util.HashSet; |
|
6 |
import java.util.Set; |
|
7 |
import java.util.stream.Collectors; |
|
8 |
|
|
9 |
/** |
|
10 |
* Class that converts set of strings to string and string to set of strings |
|
11 |
*/ |
|
12 |
public class SetToStringConverter implements AttributeConverter<Set<String>, String> { |
|
13 |
/** |
|
14 |
* Delimiter for splitting/joining string |
|
15 |
*/ |
|
16 |
private static final String DELIMITER = ","; |
|
17 |
|
|
18 |
@Override |
|
19 |
public String convertToDatabaseColumn(Set<String> attribute) { |
|
20 |
return attribute == null ? null : String.join(DELIMITER, attribute); |
|
21 |
} |
|
22 |
|
|
23 |
@Override |
|
24 |
public Set<String> convertToEntityAttribute(String dbData) { |
|
25 |
return dbData == null ? new HashSet<>() : Arrays.stream(dbData.split(DELIMITER)).collect(Collectors.toSet()); |
|
26 |
} |
|
27 |
|
|
28 |
} |
backend/src/main/java/cz/zcu/kiv/backendapi/security/SecurityConfig.java | ||
---|---|---|
62 | 62 |
PERMITTED_ENDPOINTS.put("/v3/api-docs/swagger-config", HttpMethod.GET); |
63 | 63 |
PERMITTED_ENDPOINTS.put("/catalog-items", HttpMethod.GET); |
64 | 64 |
PERMITTED_ENDPOINTS.put("/catalog-items/**", HttpMethod.GET); |
65 |
PERMITTED_ENDPOINTS.put("/external-catalog-items", HttpMethod.POST); //TODO delete |
|
65 | 66 |
} |
66 | 67 |
|
67 | 68 |
/** |
... | ... | |
83 | 84 |
.authorizeRequests() |
84 | 85 |
.antMatchers(HttpMethod.GET, PERMITTED_ENDPOINTS.keySet().stream().filter(k -> PERMITTED_ENDPOINTS.get(k).equals(HttpMethod.GET)).toArray(String[]::new)).permitAll() |
85 | 86 |
.antMatchers(HttpMethod.POST, "/login").permitAll() |
87 |
.antMatchers(HttpMethod.POST, "/external-catalog-items").permitAll() //TODO delete |
|
86 | 88 |
.antMatchers(HttpMethod.PATCH, "/users/*/permissions", "/users/*/password").hasRole(Role.ADMIN.name()) |
87 | 89 |
.antMatchers(HttpMethod.DELETE, "/users/**").hasRole(Role.ADMIN.name()) |
88 | 90 |
.antMatchers(HttpMethod.GET, "/users").hasRole(Role.ADMIN.name()) |
Také k dispozici: Unified diff
Added loading of external catalog items
re #9624