- intermediate commit
This commit is contained in:
parent
79dea999ad
commit
3141e62b08
33 changed files with 153 additions and 316 deletions
|
|
@ -116,7 +116,7 @@ const executeRequest = async (requestingStore, request) => {
|
|||
trace: null
|
||||
}
|
||||
|
||||
logger.error(error);
|
||||
logger.error(error, e);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(error, {store: requestingStore, request: request});
|
||||
|
||||
|
|
@ -172,4 +172,4 @@ const executeRequest = async (requestingStore, request) => {
|
|||
}
|
||||
|
||||
export default performRequest;
|
||||
export {performUpload, performDownload};
|
||||
export {performUpload, performDownload, getCsrfToken};
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ export default {
|
|||
}
|
||||
|
||||
:deep(.leaflet-control-attribution) {
|
||||
font-size: 10px;
|
||||
font-size: 0.8rem;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<div class="input-field">{{ coordinatesDMS }}</div>
|
||||
</div>
|
||||
<div class="supplier-map">
|
||||
<open-street-map-embed :coordinates="supplierCoordinates" zoom="15" width="600px" height="300px" custom-filter="grayscale(0.8) sepia(0.5) hue-rotate(180deg) saturate(0.5) brightness(1.0)"></open-street-map-embed>
|
||||
<open-street-map-embed :coordinates="supplierCoordinates" :zoom="15" width="100%" height="300px" custom-filter="grayscale(0.8) sepia(0.5) hue-rotate(180deg) saturate(0.5) brightness(1.0)"></open-street-map-embed>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<modal :state="selectSupplierModalState" @close="closeEditModal">
|
||||
|
|
@ -177,12 +177,9 @@ export default {
|
|||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
gap: 3.6rem;
|
||||
flex: 1 1 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.supplier-container {
|
||||
|
|
|
|||
|
|
@ -91,88 +91,48 @@ export const useAssistantStore = defineStore('assistant', {
|
|||
|
||||
console.log(`${url} with query ${query}`);
|
||||
|
||||
const headers = new Headers();
|
||||
headers.append('search', encodeURIComponent(query));
|
||||
try {
|
||||
const resp = await performRequest(this, 'GET', `${config.backendUrl}/calculation/search/?search=${encodeURIComponent(query)}`, null, true);
|
||||
const data = resp.data;
|
||||
|
||||
const request = {url: url, params: {method: 'GET'}};
|
||||
const newSuppliers = [];
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: headers
|
||||
}).catch(e => {
|
||||
this.error = {code: 'Network error.', message: "Please check your internet connection.", trace: null}
|
||||
this.loading = false;
|
||||
|
||||
console.error(this.error);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(this.error, {store: this, request: request});
|
||||
|
||||
throw e;
|
||||
});
|
||||
|
||||
const data = await response.json().catch(e => {
|
||||
this.error = {
|
||||
code: 'Malformed response',
|
||||
message: "Malformed server response. Please contact support.",
|
||||
trace: null
|
||||
for (const supplier of data.supplier) {
|
||||
const newSupplier = {
|
||||
id: `s${supplier.id}`,
|
||||
origId: supplier.id,
|
||||
isUserSupplier: false,
|
||||
name: supplier.name,
|
||||
address: supplier.address,
|
||||
iso: supplier.country['iso_code']
|
||||
};
|
||||
newSuppliers.push(newSupplier);
|
||||
}
|
||||
|
||||
|
||||
for (const supplier of data.user_supplier) {
|
||||
const newSupplier = {
|
||||
id: `u${supplier.id}`,
|
||||
origId: supplier.id,
|
||||
isUserSupplier: true,
|
||||
name: supplier.name,
|
||||
address: supplier.address,
|
||||
iso: supplier.country['iso_code']
|
||||
};
|
||||
newSuppliers.push(newSupplier);
|
||||
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
this.materials = data.materials;
|
||||
this.suppliers = newSuppliers;
|
||||
|
||||
console.error(this.error);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(this.error, {store: this, request: request});
|
||||
|
||||
throw e;
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
this.error = {
|
||||
code: data.error.code,
|
||||
title: data.error.title,
|
||||
message: data.error.message,
|
||||
trace: data.error.trace
|
||||
};
|
||||
this.loading = false;
|
||||
|
||||
console.error(this.error);
|
||||
const errorStore = useErrorStore();
|
||||
void errorStore.addError(this.error, {store: this, request: request});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const newSuppliers = [];
|
||||
|
||||
for (const supplier of data.supplier) {
|
||||
const newSupplier = {
|
||||
id: `s${supplier.id}`,
|
||||
origId: supplier.id,
|
||||
isUserSupplier: false,
|
||||
name: supplier.name,
|
||||
address: supplier.address,
|
||||
iso: supplier.country['iso_code']
|
||||
};
|
||||
newSuppliers.push(newSupplier);
|
||||
}
|
||||
|
||||
|
||||
for (const supplier of data.user_supplier) {
|
||||
const newSupplier = {
|
||||
id: `u${supplier.id}`,
|
||||
origId: supplier.id,
|
||||
isUserSupplier: true,
|
||||
name: supplier.name,
|
||||
address: supplier.address,
|
||||
iso: supplier.country['iso_code']
|
||||
};
|
||||
newSuppliers.push(newSupplier);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
this.materials = data.materials;
|
||||
this.suppliers = newSuppliers;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {defineStore, getActivePinia} from 'pinia'
|
||||
import {config} from '@/config'
|
||||
import {toRaw} from "vue";
|
||||
import {getCsrfToken} from "@/backend.js";
|
||||
|
||||
export const useErrorStore = defineStore('error', {
|
||||
state() {
|
||||
|
|
@ -46,8 +47,10 @@ export const useErrorStore = defineStore('error', {
|
|||
|
||||
const params = {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
'X-XSRF-TOKEN': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify(toRaw(this.sendCache))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ public class DevUserEmulationFilter extends OncePerRequestFilter {
|
|||
setEmulatedUser(user);
|
||||
System.out.println("DevUserEmulationFilter - Set user: " + user.getEmail());
|
||||
}
|
||||
} else {
|
||||
System.out.println("DevUserEmulationFilter - " + request.getRequestURI() + " - No emulated user set");
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import de.avatic.lcc.service.calculation.PremiseCreationService;
|
|||
import de.avatic.lcc.service.calculation.PremiseSearchStringAnalyzerService;
|
||||
import de.avatic.lcc.util.exception.badrequest.InvalidArgumentException;
|
||||
import de.avatic.lcc.util.exception.base.BadRequestException;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -35,7 +34,6 @@ import org.springframework.web.bind.annotation.*;
|
|||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -82,18 +80,14 @@ public class PremiseController {
|
|||
|
||||
@GetMapping({"/search", "/search/"})
|
||||
@PreAuthorize("hasAnyRole('SUPER', 'CALCULATION')")
|
||||
public ResponseEntity<PremiseSearchResultDTO> findMaterialsAndSuppliers(@RequestHeader String search) {
|
||||
public ResponseEntity<PremiseSearchResultDTO> findMaterialsAndSuppliers(@RequestParam String search) {
|
||||
|
||||
try {
|
||||
// Decode the header value
|
||||
String decodedValue = URLDecoder.decode(search, StandardCharsets.UTF_8);
|
||||
return ResponseEntity.ok(premiseSearchStringAnalyzerService.findMaterialAndSuppliers(decodedValue));
|
||||
} catch (Exception e) {
|
||||
throw new BadRequestException("Bad string encoding", "Unable to decode request",e);
|
||||
throw new BadRequestException("Bad string encoding", "Unable to decode request", e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@PostMapping({"/create", "/create/"})
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BatchGeocodingResult {
|
||||
|
||||
private final List<GeocodingResult> results;
|
||||
private final int successfulRequests;
|
||||
private final int totalRequests;
|
||||
|
||||
public BatchGeocodingResult(List<GeocodingResult> results, int successfulRequests, int totalRequests) {
|
||||
this.results = results;
|
||||
this.successfulRequests = successfulRequests;
|
||||
this.totalRequests = totalRequests;
|
||||
}
|
||||
|
||||
public List<GeocodingResult> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public int getSuccessfulRequests() {
|
||||
return successfulRequests;
|
||||
}
|
||||
|
||||
public int getTotalRequests() {
|
||||
return totalRequests;
|
||||
}
|
||||
|
||||
public int getFailedRequests() {
|
||||
return totalRequests - successfulRequests;
|
||||
}
|
||||
|
||||
public boolean isFullySuccessful() {
|
||||
return successfulRequests == totalRequests;
|
||||
}
|
||||
|
||||
public GeocodingResult getResult(int index) {
|
||||
if (index >= 0 && index < results.size()) {
|
||||
return results.get(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class BatchResponseItem {
|
||||
@JsonProperty("statusCode")
|
||||
private Integer statusCode;
|
||||
|
||||
@JsonProperty("response")
|
||||
private GeocodingResponse response;
|
||||
|
||||
public Integer getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public void setStatusCode(Integer statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public GeocodingResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(GeocodingResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import de.avatic.lcc.model.nodes.Location;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding.batch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding.batch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -12,7 +12,7 @@ public class BatchGeocodingResponse {
|
|||
private Summary summary;
|
||||
|
||||
@JsonProperty("batchItems")
|
||||
private List<BatchResponseItem> batchItems;
|
||||
private List<GeocodingBatchResponseItem> batchItems;
|
||||
|
||||
public Summary getSummary() {
|
||||
return summary;
|
||||
|
|
@ -22,11 +22,11 @@ public class BatchGeocodingResponse {
|
|||
this.summary = summary;
|
||||
}
|
||||
|
||||
public List<BatchResponseItem> getBatchItems() {
|
||||
public List<GeocodingBatchResponseItem> getBatchItems() {
|
||||
return batchItems;
|
||||
}
|
||||
|
||||
public void setBatchItems(List<BatchResponseItem> batchItems) {
|
||||
public void setBatchItems(List<GeocodingBatchResponseItem> batchItems) {
|
||||
this.batchItems = batchItems;
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding.batch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package de.avatic.lcc.model.azuremaps.geocoding.batch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.Feature;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeocodingBatchResponseItem {
|
||||
|
||||
@JsonProperty("type")
|
||||
private String type;
|
||||
|
||||
@JsonProperty("features")
|
||||
private List<Feature> features;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public List<Feature> getFeatures() {
|
||||
return features;
|
||||
}
|
||||
|
||||
public void setFeatures(List<Feature> features) {
|
||||
this.features = features;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.geocoding.batch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.avatic.lcc.model.azuremaps;
|
||||
package de.avatic.lcc.model.azuremaps.route;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package de.avatic.lcc.service.api;
|
||||
|
||||
import de.avatic.lcc.excelmodel.ExcelNode;
|
||||
import de.avatic.lcc.model.azuremaps.*;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.batch.BatchGeocodingRequest;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.batch.BatchGeocodingResponse;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.batch.BatchItem;
|
||||
import de.avatic.lcc.model.bulk.BulkInstruction;
|
||||
import de.avatic.lcc.util.exception.internalerror.ExcelValidationError;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
|
@ -11,10 +14,11 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class BatchGeoApiService {
|
||||
|
|
@ -32,60 +36,60 @@ public class BatchGeoApiService {
|
|||
this.subscriptionKey = subscriptionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Geocode multiple addresses in a single batch request
|
||||
* @param addresses List of address strings to geocode
|
||||
* @return BatchGeocodingResult containing all results
|
||||
*/
|
||||
public BatchGeocodingResult geocodeBatch(List<BulkInstruction<ExcelNode>> nodes) {
|
||||
|
||||
public void geocodeBatch(List<BulkInstruction<ExcelNode>> nodes) {
|
||||
if (nodes == null || nodes.isEmpty()) {
|
||||
logger.warn("Address list is null or empty");
|
||||
return new BatchGeocodingResult(new ArrayList<>(), 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<BulkInstruction<ExcelNode>> noGeo = new ArrayList<>();
|
||||
int totalSuccessful = 0;
|
||||
|
||||
for(var node : nodes) {
|
||||
if(node.getEntity().getGeoLat() == null || node.getEntity().getGeoLng() == null) {
|
||||
for (var node : nodes) {
|
||||
if (node.getEntity().getGeoLat() == null || node.getEntity().getGeoLng() == null) {
|
||||
noGeo.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Split into chunks if exceeds max batch size
|
||||
if (noGeo.size() > MAX_BATCH_SIZE) {
|
||||
return geocodeLargeBatch(noGeo);
|
||||
for (int currentBatch = 0; currentBatch < noGeo.size(); currentBatch += MAX_BATCH_SIZE) {
|
||||
int end = Math.min(currentBatch + MAX_BATCH_SIZE, noGeo.size());
|
||||
var chunk = noGeo.subList(currentBatch, end);
|
||||
|
||||
var chunkResult = executeBatchRequest(chunk.stream()
|
||||
.map(BulkInstruction::getEntity).map(this::getGeoCodeString).map(BatchItem::new)
|
||||
.toList());
|
||||
|
||||
if (chunkResult.isPresent()) {
|
||||
|
||||
totalSuccessful += chunkResult.get().getSummary().getSuccessfulRequests();
|
||||
|
||||
|
||||
for (int itemIdx = 0; itemIdx < chunk.size(); itemIdx++) {
|
||||
var result = chunkResult.get().getBatchItems().get(itemIdx);
|
||||
var node = chunk.get(itemIdx).getEntity();
|
||||
|
||||
if (!result.getFeatures().isEmpty() && result.getFeatures().getFirst().getProperties().getConfidence().equalsIgnoreCase("high")) {
|
||||
var geometry = result.getFeatures().getFirst().getGeometry();
|
||||
node.setGeoLng(BigDecimal.valueOf(geometry.getCoordinates().get(0)));
|
||||
node.setGeoLat(BigDecimal.valueOf(geometry.getCoordinates().get(1)));
|
||||
} else {
|
||||
logger.warn("Geocoding failed for address {}", node.getAddress());
|
||||
throw new ExcelValidationError("Unable to geocode " + node.getName() + ". Please check your address or enter geo position yourself.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<BatchItem> batchItems = noGeo.stream()
|
||||
.map(BulkInstruction::getEntity).map(ExcelNode::getAddress).map(BatchItem::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return executeBatchRequest(batchItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Geocode multiple addresses with detailed address components
|
||||
* @param batchItems List of BatchItem with detailed address information
|
||||
* @return BatchGeocodingResult containing all results
|
||||
*/
|
||||
public BatchGeocodingResult geocodeBatchDetailed(List<BatchItem> batchItems) {
|
||||
if (batchItems == null || batchItems.isEmpty()) {
|
||||
logger.warn("Batch items list is null or empty");
|
||||
return new BatchGeocodingResult(new ArrayList<>(), 0, 0);
|
||||
}
|
||||
|
||||
// Split into chunks if exceeds max batch size
|
||||
if (batchItems.size() > MAX_BATCH_SIZE) {
|
||||
return geocodeLargeBatchDetailed(batchItems);
|
||||
}
|
||||
|
||||
return executeBatchRequest(batchItems);
|
||||
private String getGeoCodeString(ExcelNode excelNode) {
|
||||
return excelNode.getAddress() + ", " + excelNode.getCountryId();
|
||||
}
|
||||
|
||||
private BatchGeocodingResult executeBatchRequest(List<BatchItem> batchItems) {
|
||||
private Optional<BatchGeocodingResponse> executeBatchRequest(List<BatchItem> batchItems) {
|
||||
try {
|
||||
URI uri = UriComponentsBuilder.fromUriString(AZURE_MAPS_BATCH_GEOCODING_URL)
|
||||
.queryParam("api-version", "2023-06-01")
|
||||
.queryParam("api-version", "2025-01-01")
|
||||
.queryParam("subscription-key", subscriptionKey)
|
||||
.build()
|
||||
.toUri();
|
||||
|
|
@ -106,21 +110,7 @@ public class BatchGeoApiService {
|
|||
BatchGeocodingResponse.class
|
||||
);
|
||||
|
||||
BatchGeocodingResponse response = responseEntity.getBody();
|
||||
|
||||
if (response != null) {
|
||||
List<GeocodingResult> results = processResponse(response);
|
||||
int successful = response.getSummary() != null ?
|
||||
response.getSummary().getSuccessfulRequests() : 0;
|
||||
int total = response.getSummary() != null ?
|
||||
response.getSummary().getTotalRequests() : batchItems.size();
|
||||
|
||||
logger.info("Batch geocoding completed: {}/{} successful", successful, total);
|
||||
return new BatchGeocodingResult(results, successful, total);
|
||||
}
|
||||
|
||||
logger.warn("Received null response from batch geocoding");
|
||||
return new BatchGeocodingResult(new ArrayList<>(), 0, batchItems.size());
|
||||
return Optional.ofNullable(responseEntity.getBody());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to execute batch geocoding request", e);
|
||||
|
|
@ -128,73 +118,4 @@ public class BatchGeoApiService {
|
|||
}
|
||||
}
|
||||
|
||||
private List<GeocodingResult> processResponse(BatchGeocodingResponse response) {
|
||||
List<GeocodingResult> results = new ArrayList<>();
|
||||
|
||||
if (response.getBatchItems() != null) {
|
||||
for (BatchResponseItem item : response.getBatchItems()) {
|
||||
if (item.getStatusCode() == 200 &&
|
||||
item.getResponse() != null &&
|
||||
item.getResponse().getFeatures() != null &&
|
||||
!item.getResponse().getFeatures().isEmpty()) {
|
||||
|
||||
Feature feature = item.getResponse().getFeatures().get(0);
|
||||
results.add(new GeocodingResult(feature));
|
||||
} else {
|
||||
// Add null for failed requests to maintain index alignment
|
||||
results.add(null);
|
||||
logger.debug("Failed to geocode item at index {}: status code {}",
|
||||
results.size() - 1, item.getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle batches larger than MAX_BATCH_SIZE by splitting into multiple requests
|
||||
*/
|
||||
private BatchGeocodingResult geocodeLargeBatch(List<BulkInstruction<ExcelNode>> addresses) {
|
||||
logger.info("Processing large batch of {} addresses in chunks of {}",
|
||||
addresses.size(), MAX_BATCH_SIZE);
|
||||
|
||||
List<GeocodingResult> allResults = new ArrayList<>();
|
||||
int totalSuccessful = 0;
|
||||
int totalRequests = addresses.size();
|
||||
//
|
||||
// for (int i = 0; i < addresses.size(); i += MAX_BATCH_SIZE) {
|
||||
// int end = Math.min(i + MAX_BATCH_SIZE, addresses.size());
|
||||
// List<String> chunk = addresses.subList(i, end);
|
||||
//
|
||||
// BatchGeocodingResult chunkResult = geocodeBatch(chunk);
|
||||
// allResults.addAll(chunkResult.getResults());
|
||||
// totalSuccessful += chunkResult.getSuccessfulRequests();
|
||||
// }
|
||||
|
||||
return new BatchGeocodingResult(allResults, totalSuccessful, totalRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle detailed batches larger than MAX_BATCH_SIZE
|
||||
*/
|
||||
private BatchGeocodingResult geocodeLargeBatchDetailed(List<BatchItem> batchItems) {
|
||||
logger.info("Processing large detailed batch of {} items in chunks of {}",
|
||||
batchItems.size(), MAX_BATCH_SIZE);
|
||||
|
||||
List<GeocodingResult> allResults = new ArrayList<>();
|
||||
int totalSuccessful = 0;
|
||||
int totalRequests = batchItems.size();
|
||||
|
||||
for (int i = 0; i < batchItems.size(); i += MAX_BATCH_SIZE) {
|
||||
int end = Math.min(i + MAX_BATCH_SIZE, batchItems.size());
|
||||
List<BatchItem> chunk = batchItems.subList(i, end);
|
||||
|
||||
BatchGeocodingResult chunkResult = geocodeBatchDetailed(chunk);
|
||||
allResults.addAll(chunkResult.getResults());
|
||||
totalSuccessful += chunkResult.getSuccessfulRequests();
|
||||
}
|
||||
|
||||
return new BatchGeocodingResult(allResults, totalSuccessful, totalRequests);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package de.avatic.lcc.service.api;
|
||||
|
||||
import de.avatic.lcc.model.azuremaps.RouteDirectionsResponse;
|
||||
import de.avatic.lcc.model.azuremaps.route.RouteDirectionsResponse;
|
||||
import de.avatic.lcc.model.nodes.Distance;
|
||||
import de.avatic.lcc.model.nodes.DistanceMatrixState;
|
||||
import de.avatic.lcc.model.nodes.Node;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package de.avatic.lcc.service.api;
|
||||
|
||||
import de.avatic.lcc.model.azuremaps.GeocodingResponse;
|
||||
import de.avatic.lcc.model.azuremaps.GeocodingResult;
|
||||
import de.avatic.lcc.model.azuremaps.Feature;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.GeocodingResponse;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.GeocodingResult;
|
||||
import de.avatic.lcc.model.azuremaps.geocoding.Feature;
|
||||
import de.avatic.lcc.model.nodes.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -36,7 +36,7 @@ public class GeoApiService {
|
|||
|
||||
try {
|
||||
URI uri = UriComponentsBuilder.fromUriString(AZURE_MAPS_GEOCODING_URL)
|
||||
.queryParam("api-version", "2023-06-01")
|
||||
.queryParam("api-version", "2025-01-01")
|
||||
.queryParam("query", address)
|
||||
.queryParam("subscription-key", subscriptionKey)
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ public class BulkImportService {
|
|||
break;
|
||||
case NODE:
|
||||
var nodeInstructions = nodeExcelMapper.extractSheet(sheet);
|
||||
// batchGeoApiService.geocodeBatch(nodeInstructions);
|
||||
batchGeoApiService.geocodeBatch(nodeInstructions);
|
||||
nodeInstructions.forEach(nodeBulkImportService::processNodeInstructions);
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue