Add numeric tachograph nation resolution
This commit is contained in:
parent
b35d428e80
commit
fc0d6db99a
|
|
@ -1,14 +1,24 @@
|
|||
package at.procon.eventhub.dto;
|
||||
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
|
||||
/**
|
||||
* Tachograph driver-card identifier. The card number is scoped by issuing nation.
|
||||
*/
|
||||
public record DriverCardRefDto(
|
||||
String nation,
|
||||
Integer nationNumericCode,
|
||||
String number
|
||||
) {
|
||||
public DriverCardRefDto(String nation, String number) {
|
||||
this(nation, null, number);
|
||||
}
|
||||
|
||||
public DriverCardRefDto {
|
||||
nation = normalize(nation);
|
||||
TachographNationRegistry.NationResolution nationResolution =
|
||||
TachographNationRegistry.resolve(nation, nationNumericCode);
|
||||
nation = normalize(nationResolution.legacyNation());
|
||||
nationNumericCode = nationResolution.numericCode();
|
||||
number = normalizeNullable(number);
|
||||
}
|
||||
|
||||
|
|
@ -17,11 +27,19 @@ public record DriverCardRefDto(
|
|||
}
|
||||
|
||||
public String stableKey() {
|
||||
return (nation == null ? "" : nation) + ":" + (number == null ? "" : number);
|
||||
String nationKey = nationNumericCode == null ? nation : String.valueOf(nationNumericCode);
|
||||
return (nationKey == null ? "" : nationKey) + ":" + (number == null ? "" : number);
|
||||
}
|
||||
|
||||
private static String normalize(String value) {
|
||||
return value == null || value.isBlank() ? null : value.trim().toUpperCase();
|
||||
if (value == null || value.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
if (trimmed.matches("(?i)^unknown\\s+[0-9]+$")) {
|
||||
return "Unknown " + trimmed.replaceAll("[^0-9]", "");
|
||||
}
|
||||
return trimmed.toUpperCase();
|
||||
}
|
||||
|
||||
private static String normalizeNullable(String value) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
package at.procon.eventhub.dto;
|
||||
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
|
||||
/**
|
||||
* Vehicle registration number reference. VRN/plate is scoped by nation and can
|
||||
* be resolved historically by occurredAt later.
|
||||
*/
|
||||
public record VehicleRegistrationRefDto(
|
||||
String nation,
|
||||
Integer nationNumericCode,
|
||||
String number
|
||||
) {
|
||||
public VehicleRegistrationRefDto(String nation, String number) {
|
||||
this(nation, null, number);
|
||||
}
|
||||
|
||||
public VehicleRegistrationRefDto {
|
||||
nation = normalize(nation);
|
||||
TachographNationRegistry.NationResolution nationResolution =
|
||||
TachographNationRegistry.resolve(nation, nationNumericCode);
|
||||
nation = normalize(nationResolution.legacyNation());
|
||||
nationNumericCode = nationResolution.numericCode();
|
||||
number = normalizeNullable(number);
|
||||
}
|
||||
|
||||
|
|
@ -18,11 +28,19 @@ public record VehicleRegistrationRefDto(
|
|||
}
|
||||
|
||||
public String stableKey() {
|
||||
return (nation == null ? "" : nation) + ":" + (number == null ? "" : number);
|
||||
String nationKey = nationNumericCode == null ? nation : String.valueOf(nationNumericCode);
|
||||
return (nationKey == null ? "" : nationKey) + ":" + (number == null ? "" : number);
|
||||
}
|
||||
|
||||
private static String normalize(String value) {
|
||||
return value == null || value.isBlank() ? null : value.trim().toUpperCase();
|
||||
if (value == null || value.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
if (trimmed.matches("(?i)^unknown\\s+[0-9]+$")) {
|
||||
return "Unknown " + trimmed.replaceAll("[^0-9]", "");
|
||||
}
|
||||
return trimmed.toUpperCase();
|
||||
}
|
||||
|
||||
private static String normalizeNullable(String value) {
|
||||
|
|
|
|||
|
|
@ -40,10 +40,11 @@ public class DriverIdentityRepository {
|
|||
String sourceDriverEntityId = normalizeNullable(driverRef.sourceEntityId());
|
||||
DriverCardRefDto driverCard = driverRef.driverCard();
|
||||
String cardNation = driverCard == null ? null : normalizeNullable(driverCard.nation());
|
||||
Integer cardNationNumericCode = driverCard == null ? null : driverCard.nationNumericCode();
|
||||
String cardNumber = driverCard == null ? null : normalizeDriverCardNumber(cardNation, driverCard.number());
|
||||
|
||||
UUID driverId = findBySourceDriverEntityId(normalizedTenantKey, eventSourceId, sourceDriverEntityId);
|
||||
UUID driverCardId = resolveOrCreateDriverCardId(cardNation, cardNumber, driverId);
|
||||
UUID driverCardId = resolveOrCreateDriverCardId(cardNation, cardNationNumericCode, cardNumber, driverId);
|
||||
|
||||
if (driverId == null && driverCardId != null) {
|
||||
driverId = findDriverIdByCardId(driverCardId);
|
||||
|
|
@ -621,21 +622,24 @@ public class DriverIdentityRepository {
|
|||
|
||||
private UUID resolveOrCreateDriverCardId(
|
||||
String cardNation,
|
||||
Integer cardNationNumericCode,
|
||||
String cardNumber,
|
||||
UUID preferredDriverId
|
||||
) {
|
||||
if (cardNation == null || cardNumber == null) {
|
||||
if ((cardNation == null && cardNationNumericCode == null) || cardNumber == null) {
|
||||
return null;
|
||||
}
|
||||
UUID driverCardId = findDriverCardByCard(cardNation, cardNumber);
|
||||
UUID driverCardId = findDriverCardByCard(cardNation, cardNationNumericCode, cardNumber);
|
||||
if (driverCardId == null) {
|
||||
Map<String, Object> payload = new LinkedHashMap<>();
|
||||
put(payload, "source", "event");
|
||||
put(payload, "card_nation", cardNation);
|
||||
put(payload, "card_nation_numeric_code", cardNationNumericCode);
|
||||
put(payload, "card_number", cardNumber);
|
||||
return createDriverCard(
|
||||
preferredDriverId,
|
||||
cardNation,
|
||||
cardNationNumericCode,
|
||||
cardNumber,
|
||||
null,
|
||||
payload
|
||||
|
|
@ -683,10 +687,25 @@ public class DriverIdentityRepository {
|
|||
);
|
||||
}
|
||||
|
||||
private UUID findDriverCardByCard(String cardNation, String cardNumber) {
|
||||
if (cardNation == null || cardNumber == null) {
|
||||
private UUID findDriverCardByCard(String cardNation, Integer cardNationNumericCode, String cardNumber) {
|
||||
if ((cardNation == null && cardNationNumericCode == null) || cardNumber == null) {
|
||||
return null;
|
||||
}
|
||||
if (cardNationNumericCode != null) {
|
||||
return jdbcTemplate.query(
|
||||
"""
|
||||
select card.id
|
||||
from eventhub.driver_card card
|
||||
where card.nation_numeric_code = ?
|
||||
and card.card_number = ?
|
||||
order by card.updated_at desc
|
||||
limit 1
|
||||
""",
|
||||
rs -> rs.next() ? (UUID) rs.getObject("id") : null,
|
||||
cardNationNumericCode,
|
||||
cardNumber
|
||||
);
|
||||
}
|
||||
return jdbcTemplate.query(
|
||||
"""
|
||||
select card.id
|
||||
|
|
@ -782,6 +801,7 @@ public class DriverIdentityRepository {
|
|||
private UUID createDriverCard(
|
||||
UUID driverId,
|
||||
String cardNation,
|
||||
Integer cardNationNumericCode,
|
||||
String cardNumber,
|
||||
OffsetDateTime sourceUpdatedAt,
|
||||
Map<String, Object> payload
|
||||
|
|
@ -790,12 +810,13 @@ public class DriverIdentityRepository {
|
|||
jdbcTemplate.update(
|
||||
"""
|
||||
insert into eventhub.driver_card(
|
||||
id, driver_id, nation, card_number, source_updated_at, payload, updated_at
|
||||
) values (?, ?, ?, ?, ?, ?::jsonb, now())
|
||||
id, driver_id, nation, nation_numeric_code, card_number, source_updated_at, payload, updated_at
|
||||
) values (?, ?, ?, ?, ?, ?, ?::jsonb, now())
|
||||
""",
|
||||
driverCardId,
|
||||
driverId,
|
||||
cardNation,
|
||||
cardNationNumericCode,
|
||||
cardNumber,
|
||||
sourceUpdatedAt,
|
||||
toJson(payload)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import at.procon.eventhub.dto.VehicleRefDto;
|
|||
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
|
||||
import at.procon.eventhub.processing.model.UnifiedDriverEventsRequest;
|
||||
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
|
@ -149,11 +150,13 @@ public class EventHubEventReadRepository {
|
|||
detail.attributes,
|
||||
driver.source_driver_entity_id,
|
||||
driver_card.nation as driver_card_nation,
|
||||
driver_card.nation_numeric_code as driver_card_nation_numeric_code,
|
||||
driver_card.card_number as driver_card_number,
|
||||
vehicle.source_vehicle_entity_id,
|
||||
vehicle.vin,
|
||||
registration.source_registration_entity_id,
|
||||
registration.nation as vehicle_registration_nation,
|
||||
registration.nation_numeric_code as vehicle_registration_nation_numeric_code,
|
||||
registration.registration_number as vehicle_registration_number,
|
||||
event.driver_id,
|
||||
event.vehicle_id,
|
||||
|
|
@ -209,8 +212,15 @@ public class EventHubEventReadRepository {
|
|||
sql.append(" and driver_card.card_number = ?");
|
||||
params.add(driverCardNumber);
|
||||
if (driverCardNation != null) {
|
||||
TachographNationRegistry.NationResolution driverCardNationResolution =
|
||||
TachographNationRegistry.resolve(driverCardNation, null);
|
||||
if (driverCardNationResolution.numericCode() != null) {
|
||||
sql.append(" and driver_card.nation_numeric_code = ?");
|
||||
params.add(driverCardNationResolution.numericCode());
|
||||
} else {
|
||||
sql.append(" and driver_card.nation = ?");
|
||||
params.add(driverCardNation);
|
||||
params.add(driverCardNationResolution.legacyNation());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vehicleSourceEntityId != null) {
|
||||
|
|
@ -225,8 +235,15 @@ public class EventHubEventReadRepository {
|
|||
sql.append(" and registration.registration_number = ?");
|
||||
params.add(registrationNumber);
|
||||
if (registrationNation != null) {
|
||||
TachographNationRegistry.NationResolution registrationNationResolution =
|
||||
TachographNationRegistry.resolve(registrationNation, null);
|
||||
if (registrationNationResolution.numericCode() != null) {
|
||||
sql.append(" and registration.nation_numeric_code = ?");
|
||||
params.add(registrationNationResolution.numericCode());
|
||||
} else {
|
||||
sql.append(" and registration.nation = ?");
|
||||
params.add(registrationNation);
|
||||
params.add(registrationNationResolution.legacyNation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +292,11 @@ public class EventHubEventReadRepository {
|
|||
String cardNumber = rs.getString("driver_card_number");
|
||||
DriverCardRefDto driverCard = cardNumber == null
|
||||
? null
|
||||
: new DriverCardRefDto(rs.getString("driver_card_nation"), cardNumber);
|
||||
: new DriverCardRefDto(
|
||||
rs.getString("driver_card_nation"),
|
||||
integerValue(rs, "driver_card_nation_numeric_code"),
|
||||
cardNumber
|
||||
);
|
||||
DriverRefDto driverRef = new DriverRefDto(sourceEntityId, driverCard);
|
||||
return driverRef.hasAnyReference() ? driverRef : null;
|
||||
}
|
||||
|
|
@ -284,7 +305,11 @@ public class EventHubEventReadRepository {
|
|||
VehicleRegistrationRefDto registration = null;
|
||||
String registrationNumber = rs.getString("vehicle_registration_number");
|
||||
if (registrationNumber != null) {
|
||||
registration = new VehicleRegistrationRefDto(rs.getString("vehicle_registration_nation"), registrationNumber);
|
||||
registration = new VehicleRegistrationRefDto(
|
||||
rs.getString("vehicle_registration_nation"),
|
||||
integerValue(rs, "vehicle_registration_nation_numeric_code"),
|
||||
registrationNumber
|
||||
);
|
||||
}
|
||||
VehicleRefDto vehicleRef = new VehicleRefDto(
|
||||
firstNonBlank(
|
||||
|
|
@ -406,6 +431,17 @@ public class EventHubEventReadRepository {
|
|||
return Long.parseLong(value.toString());
|
||||
}
|
||||
|
||||
private Integer integerValue(ResultSet rs, String column) throws SQLException {
|
||||
Object value = rs.getObject(column);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Number number) {
|
||||
return number.intValue();
|
||||
}
|
||||
return Integer.parseInt(value.toString());
|
||||
}
|
||||
|
||||
private JsonNode json(String value) {
|
||||
try {
|
||||
return value == null || value.isBlank()
|
||||
|
|
|
|||
|
|
@ -532,14 +532,18 @@ public class EventRepository {
|
|||
String vin = normalizeNullable(vehicleRef.vin());
|
||||
String sourceRegistrationEntityId = normalizeNullable(vehicleRef.sourceRegistrationEntityId());
|
||||
VehicleRegistrationRefDto registration = vehicleRef.vehicleRegistration();
|
||||
String registrationNation = registration == null ? null : normalizeNullable(registration.nation());
|
||||
String registrationNationKey = registration == null
|
||||
? null
|
||||
: registration.nationNumericCode() == null
|
||||
? normalizeNullable(registration.nation())
|
||||
: registration.nationNumericCode().toString();
|
||||
String registrationNumber = registration == null ? null : normalizeNullable(registration.number());
|
||||
|
||||
return String.join("|",
|
||||
sourceVehicleEntityId == null ? "" : sourceVehicleEntityId,
|
||||
vin == null ? "" : vin,
|
||||
sourceRegistrationEntityId == null ? "" : sourceRegistrationEntityId,
|
||||
registrationNation == null ? "" : registrationNation,
|
||||
registrationNationKey == null ? "" : registrationNationKey,
|
||||
registrationNumber == null ? "" : registrationNumber
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public class VehicleIdentityRepository {
|
|||
String sourceRegistrationEntityId = normalizeNullable(vehicleRef.sourceRegistrationEntityId());
|
||||
VehicleRegistrationRefDto registration = vehicleRef.vehicleRegistration();
|
||||
String registrationNation = registration == null ? null : normalizeNullable(registration.nation());
|
||||
Integer registrationNationNumericCode = registration == null ? null : registration.nationNumericCode();
|
||||
String registrationNumber = registration == null ? null : normalizeNullable(registration.number());
|
||||
String registrationNationForCreate = creationNation(registrationNation, registrationNumber);
|
||||
|
||||
|
|
@ -49,14 +50,21 @@ public class VehicleIdentityRepository {
|
|||
eventSourceId,
|
||||
sourceRegistrationEntityId,
|
||||
registrationNation,
|
||||
registrationNationNumericCode,
|
||||
registrationNumber
|
||||
);
|
||||
if (registrationId == null && (sourceRegistrationEntityId != null || registrationNumber != null)) {
|
||||
registrationId = createRegistration(
|
||||
registrationNationForCreate,
|
||||
registrationNationNumericCode,
|
||||
registrationNumber,
|
||||
null,
|
||||
Map.of("source", "event")
|
||||
Map.of(
|
||||
"source", "event",
|
||||
"registration_nation", registrationNationForCreate,
|
||||
"registration_nation_numeric_code", registrationNationNumericCode,
|
||||
"registration_number", registrationNumber
|
||||
)
|
||||
);
|
||||
}
|
||||
if (registrationId != null && sourceRegistrationEntityId != null) {
|
||||
|
|
@ -95,7 +103,7 @@ public class VehicleIdentityRepository {
|
|||
);
|
||||
}
|
||||
touchVehicle(vehicleId, vin);
|
||||
touchRegistration(registrationId, registrationNationForCreate, registrationNumber);
|
||||
touchRegistration(registrationId, registrationNationForCreate, registrationNationNumericCode, registrationNumber);
|
||||
|
||||
return new ResolvedVehicleReferenceResolution(
|
||||
new ResolvedVehicleReference(vehicleId, registrationId),
|
||||
|
|
@ -617,6 +625,7 @@ public class VehicleIdentityRepository {
|
|||
int eventSourceId,
|
||||
String sourceRegistrationEntityId,
|
||||
String nation,
|
||||
Integer nationNumericCode,
|
||||
String registrationNumber
|
||||
) {
|
||||
if (isYellowFoxSyntheticRegistrationNation(nation) && registrationNumber != null) {
|
||||
|
|
@ -628,7 +637,7 @@ public class VehicleIdentityRepository {
|
|||
}
|
||||
UUID registrationId = findRegistrationBySourceRegistrationEntityId(tenantKey, eventSourceId, sourceRegistrationEntityId);
|
||||
if (registrationId == null) {
|
||||
registrationId = findRegistrationByPlate(nation, registrationNumber);
|
||||
registrationId = findRegistrationByPlate(nation, nationNumericCode, registrationNumber);
|
||||
}
|
||||
return registrationId;
|
||||
}
|
||||
|
|
@ -696,10 +705,25 @@ public class VehicleIdentityRepository {
|
|||
);
|
||||
}
|
||||
|
||||
private UUID findRegistrationByPlate(String nation, String registrationNumber) {
|
||||
if (nation == null || registrationNumber == null) {
|
||||
private UUID findRegistrationByPlate(String nation, Integer nationNumericCode, String registrationNumber) {
|
||||
if ((nation == null && nationNumericCode == null) || registrationNumber == null) {
|
||||
return null;
|
||||
}
|
||||
if (nationNumericCode != null) {
|
||||
return jdbcTemplate.query(
|
||||
"""
|
||||
select r.id
|
||||
from eventhub.vehicle_registration r
|
||||
where r.nation_numeric_code = ?
|
||||
and r.registration_number = ?
|
||||
order by r.updated_at desc
|
||||
limit 1
|
||||
""",
|
||||
rs -> rs.next() ? (UUID) rs.getObject("id") : null,
|
||||
nationNumericCode,
|
||||
registrationNumber
|
||||
);
|
||||
}
|
||||
return jdbcTemplate.query(
|
||||
"""
|
||||
select r.id
|
||||
|
|
@ -787,6 +811,7 @@ public class VehicleIdentityRepository {
|
|||
|
||||
private UUID createRegistration(
|
||||
String nation,
|
||||
Integer nationNumericCode,
|
||||
String registrationNumber,
|
||||
OffsetDateTime sourceUpdatedAt,
|
||||
Map<String, Object> payload
|
||||
|
|
@ -798,11 +823,12 @@ public class VehicleIdentityRepository {
|
|||
jdbcTemplate.update(
|
||||
"""
|
||||
insert into eventhub.vehicle_registration(
|
||||
id, nation, registration_number, source_updated_at, payload, updated_at
|
||||
) values (?, ?, ?, ?, ?::jsonb, now())
|
||||
id, nation, nation_numeric_code, registration_number, source_updated_at, payload, updated_at
|
||||
) values (?, ?, ?, ?, ?, ?::jsonb, now())
|
||||
""",
|
||||
registrationId,
|
||||
nation,
|
||||
nationNumericCode,
|
||||
registrationNumber,
|
||||
sourceUpdatedAt,
|
||||
toJson(payload)
|
||||
|
|
@ -946,26 +972,30 @@ public class VehicleIdentityRepository {
|
|||
);
|
||||
}
|
||||
|
||||
private void touchRegistration(UUID registrationId, String nation, String registrationNumber) {
|
||||
if (registrationId == null || (nation == null && registrationNumber == null)) {
|
||||
private void touchRegistration(UUID registrationId, String nation, Integer nationNumericCode, String registrationNumber) {
|
||||
if (registrationId == null || (nation == null && nationNumericCode == null && registrationNumber == null)) {
|
||||
return;
|
||||
}
|
||||
jdbcTemplate.update(
|
||||
"""
|
||||
update eventhub.vehicle_registration
|
||||
set nation = coalesce(cast(? as text), nation),
|
||||
nation_numeric_code = coalesce(cast(? as integer), nation_numeric_code),
|
||||
registration_number = coalesce(cast(? as text), registration_number),
|
||||
updated_at = now()
|
||||
where id = ?
|
||||
and (
|
||||
(nation is null and cast(? as text) is not null)
|
||||
or (nation_numeric_code is null and cast(? as integer) is not null)
|
||||
or (registration_number is null and cast(? as text) is not null)
|
||||
)
|
||||
""",
|
||||
nation,
|
||||
nationNumericCode,
|
||||
registrationNumber,
|
||||
registrationId,
|
||||
nation,
|
||||
nationNumericCode,
|
||||
registrationNumber
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
|
|||
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
||||
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend;
|
||||
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
import at.procon.eventhub.tachograph.dto.TachographImportRequest;
|
||||
import at.procon.eventhub.tachograph.service.TachographExtractionDefinitionRegistry;
|
||||
import java.io.IOException;
|
||||
|
|
@ -199,11 +200,11 @@ public class TachographDbRuntimeEventLoader implements RuntimeDriverEventLoader,
|
|||
params.put("lastSourcePackageImportedAt", null);
|
||||
params.put("lastSourcePackageIdNumeric", null);
|
||||
params.put("driverSourceEntityId", request.driverSourceEntityId());
|
||||
params.put("driverCardNation", request.driverCardNation());
|
||||
params.put("driverCardNation", alphaNation(request.driverCardNation()));
|
||||
params.put("driverCardNumber", request.driverCardNumber());
|
||||
params.put("vehicleSourceEntityId", vehicleRef == null ? null : vehicleRef.sourceVehicleEntityId());
|
||||
params.put("vehicleVin", vehicleRef == null ? null : vehicleRef.vin());
|
||||
params.put("vehicleRegistrationNation", vehicleRef == null ? null : vehicleRef.registrationNation());
|
||||
params.put("vehicleRegistrationNation", vehicleRef == null ? null : alphaNation(vehicleRef.registrationNation()));
|
||||
params.put("vehicleRegistrationNumber", vehicleRef == null ? null : vehicleRef.registrationNumber());
|
||||
return params;
|
||||
}
|
||||
|
|
@ -251,4 +252,8 @@ public class TachographDbRuntimeEventLoader implements RuntimeDriverEventLoader,
|
|||
throw new IllegalStateException("Failed to load tachograph runtime SQL " + sqlResource, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String alphaNation(String rawNation) {
|
||||
return TachographNationRegistry.alphaCode(rawNation, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import at.procon.eventhub.dto.EventHubEventDto;
|
|||
import at.procon.eventhub.dto.VehicleRefDto;
|
||||
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
||||
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
|
||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
||||
|
|
@ -57,8 +58,7 @@ public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVe
|
|||
return request.registrationNumber() != null
|
||||
&& vehicleRef.vehicleRegistration() != null
|
||||
&& request.registrationNumber().equals(vehicleRef.vehicleRegistration().number())
|
||||
&& (request.registrationNation() == null
|
||||
|| request.registrationNation().equals(vehicleRef.vehicleRegistration().nation()));
|
||||
&& matchesNation(request.registrationNation(), vehicleRef.vehicleRegistration().nation(), vehicleRef.vehicleRegistration().nationNumericCode());
|
||||
}
|
||||
|
||||
private boolean withinWindow(
|
||||
|
|
@ -74,4 +74,18 @@ public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVe
|
|||
}
|
||||
return occurredTo == null || !occurredAt.isAfter(occurredTo);
|
||||
}
|
||||
|
||||
private boolean matchesNation(String requestedNation, String actualNation, Integer actualNationNumericCode) {
|
||||
if (requestedNation == null) {
|
||||
return true;
|
||||
}
|
||||
TachographNationRegistry.NationResolution requested = TachographNationRegistry.resolve(requestedNation, null);
|
||||
TachographNationRegistry.NationResolution actual = TachographNationRegistry.resolve(actualNation, actualNationNumericCode);
|
||||
if (requested.numericCode() != null || actual.numericCode() != null) {
|
||||
return requested.numericCode() != null
|
||||
&& actual.numericCode() != null
|
||||
&& requested.numericCode().equals(actual.numericCode());
|
||||
}
|
||||
return requested.legacyNation() != null && requested.legacyNation().equals(actual.legacyNation());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,11 @@ public class UnifiedRuntimeEventAssemblyService {
|
|||
UnifiedDiscoveredVehicleRef candidate = new UnifiedDiscoveredVehicleRef(
|
||||
vehicleRef.sourceVehicleEntityId(),
|
||||
vehicleRef.vin(),
|
||||
vehicleRef.vehicleRegistration() == null ? null : vehicleRef.vehicleRegistration().nation(),
|
||||
vehicleRef.vehicleRegistration() == null
|
||||
? null
|
||||
: vehicleRef.vehicleRegistration().nationNumericCode() == null
|
||||
? vehicleRef.vehicleRegistration().nation()
|
||||
: vehicleRef.vehicleRegistration().nationNumericCode().toString(),
|
||||
vehicleRef.vehicleRegistration() == null ? null : vehicleRef.vehicleRegistration().number()
|
||||
);
|
||||
if (!candidate.hasAnyReference()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
package at.procon.eventhub.reference;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class TachographNationRegistry {
|
||||
|
||||
private static final Pattern UNKNOWN_NUMERIC_PATTERN = Pattern.compile("(?i)^unknown\\s+([0-9]+)$");
|
||||
private static final String RESOURCE_PATH = "/reference/nation.csv";
|
||||
private static final Map<Integer, NationRecord> BY_NUMERIC_CODE;
|
||||
private static final Map<String, NationRecord> BY_ALPHA_CODE;
|
||||
|
||||
static {
|
||||
Map<Integer, NationRecord> byNumeric = new LinkedHashMap<>();
|
||||
Map<String, NationRecord> byAlpha = new LinkedHashMap<>();
|
||||
try {
|
||||
var inputStream = TachographNationRegistry.class.getResourceAsStream(RESOURCE_PATH);
|
||||
if (inputStream == null) {
|
||||
throw new IllegalStateException("Missing tachograph nation reference data " + RESOURCE_PATH);
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
reader.readLine();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
String[] parts = line.split(";", -1);
|
||||
if (parts.length < 5) {
|
||||
continue;
|
||||
}
|
||||
Integer numericCode = parseInteger(parts[3]);
|
||||
if (numericCode == null) {
|
||||
continue;
|
||||
}
|
||||
String name = normalizeNullable(parts[1]);
|
||||
String alphaCode = normalizeAlpha(parts[2]);
|
||||
String defaultLanguageCode = normalizeNullable(parts[4]);
|
||||
NationRecord record = new NationRecord(numericCode, alphaCode, name, defaultLanguageCode, true);
|
||||
byNumeric.put(numericCode, record);
|
||||
if (alphaCode != null) {
|
||||
byAlpha.put(alphaCode, record);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to load tachograph nation reference data.", ex);
|
||||
}
|
||||
BY_NUMERIC_CODE = Collections.unmodifiableMap(byNumeric);
|
||||
BY_ALPHA_CODE = Collections.unmodifiableMap(byAlpha);
|
||||
}
|
||||
|
||||
private TachographNationRegistry() {
|
||||
}
|
||||
|
||||
public static NationResolution resolve(String legacyNation, Integer nationNumericCode) {
|
||||
String normalizedLegacyNation = normalizeNullable(legacyNation);
|
||||
if (nationNumericCode != null) {
|
||||
NationRecord record = BY_NUMERIC_CODE.get(nationNumericCode);
|
||||
if (record != null) {
|
||||
return new NationResolution(
|
||||
record.alphaCode(),
|
||||
record.numericCode(),
|
||||
record.name(),
|
||||
true
|
||||
);
|
||||
}
|
||||
return new NationResolution(
|
||||
normalizedLegacyNation != null ? normalizedLegacyNation : unknownLabel(nationNumericCode),
|
||||
nationNumericCode,
|
||||
unknownLabel(nationNumericCode),
|
||||
false
|
||||
);
|
||||
}
|
||||
if (normalizedLegacyNation == null) {
|
||||
return NationResolution.empty();
|
||||
}
|
||||
Integer parsedNumericCode = parseInteger(normalizedLegacyNation);
|
||||
if (parsedNumericCode != null) {
|
||||
return resolve(null, parsedNumericCode);
|
||||
}
|
||||
Matcher unknownMatcher = UNKNOWN_NUMERIC_PATTERN.matcher(normalizedLegacyNation);
|
||||
if (unknownMatcher.matches()) {
|
||||
Integer unknownNumericCode = parseInteger(unknownMatcher.group(1));
|
||||
if (unknownNumericCode != null) {
|
||||
return resolve(normalizedLegacyNation, unknownNumericCode);
|
||||
}
|
||||
}
|
||||
NationRecord record = BY_ALPHA_CODE.get(normalizedLegacyNation.toUpperCase(Locale.ROOT));
|
||||
if (record != null) {
|
||||
return new NationResolution(
|
||||
record.alphaCode(),
|
||||
record.numericCode(),
|
||||
record.name(),
|
||||
true
|
||||
);
|
||||
}
|
||||
return new NationResolution(normalizedLegacyNation, null, null, false);
|
||||
}
|
||||
|
||||
public static Integer numericCode(String legacyNation, Integer nationNumericCode) {
|
||||
return resolve(legacyNation, nationNumericCode).numericCode();
|
||||
}
|
||||
|
||||
public static String alphaCode(String legacyNation, Integer nationNumericCode) {
|
||||
return resolve(legacyNation, nationNumericCode).legacyNation();
|
||||
}
|
||||
|
||||
public static NationRecord recordByNumericCode(Integer numericCode) {
|
||||
if (numericCode == null) {
|
||||
return null;
|
||||
}
|
||||
NationRecord record = BY_NUMERIC_CODE.get(numericCode);
|
||||
return record != null
|
||||
? record
|
||||
: new NationRecord(numericCode, null, unknownLabel(numericCode), null, false);
|
||||
}
|
||||
|
||||
private static String normalizeNullable(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
if (trimmed.equalsIgnoreCase("NULL")) {
|
||||
return null;
|
||||
}
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private static String normalizeAlpha(String value) {
|
||||
String normalized = normalizeNullable(value);
|
||||
return normalized == null ? null : normalized.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static Integer parseInteger(String value) {
|
||||
String normalized = normalizeNullable(value);
|
||||
if (normalized == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(normalized);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String unknownLabel(Integer numericCode) {
|
||||
return numericCode == null ? null : "Unknown " + numericCode;
|
||||
}
|
||||
|
||||
public record NationRecord(
|
||||
Integer numericCode,
|
||||
String alphaCode,
|
||||
String name,
|
||||
String defaultLanguageCode,
|
||||
boolean known
|
||||
) {
|
||||
}
|
||||
|
||||
public record NationResolution(
|
||||
String legacyNation,
|
||||
Integer numericCode,
|
||||
String displayName,
|
||||
boolean known
|
||||
) {
|
||||
public static NationResolution empty() {
|
||||
return new NationResolution(null, null, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import at.procon.eventhub.dto.CardStatus;
|
|||
import at.procon.eventhub.dto.DriverCardRefDto;
|
||||
import at.procon.eventhub.dto.DrivingStatus;
|
||||
import at.procon.eventhub.dto.EventDetailsDto;
|
||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -39,6 +40,7 @@ public class EventDetailsFactory {
|
|||
put(attributes, "cardStatus", cardStatus);
|
||||
if (driverCard != null && driverCard.hasValue()) {
|
||||
put(attributes, "cardNation", driverCard.nation());
|
||||
put(attributes, "cardNationNumericCode", driverCard.nationNumericCode());
|
||||
put(attributes, "cardNumber", driverCard.number());
|
||||
}
|
||||
return new EventDetailsDto("DRIVER_CARD", objectMapper.valueToTree(attributes));
|
||||
|
|
@ -52,15 +54,21 @@ public class EventDetailsFactory {
|
|||
|
||||
public EventDetailsDto place(String country, String region) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
put(attributes, "country", country);
|
||||
TachographNationRegistry.NationResolution nationResolution = TachographNationRegistry.resolve(country, null);
|
||||
put(attributes, "country", nationResolution.legacyNation());
|
||||
put(attributes, "countryNumericCode", nationResolution.numericCode());
|
||||
put(attributes, "region", region);
|
||||
return new EventDetailsDto("PLACE", objectMapper.valueToTree(attributes));
|
||||
}
|
||||
|
||||
public EventDetailsDto borderCrossing(String countryFrom, String countryTo) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
put(attributes, "countryFrom", countryFrom);
|
||||
put(attributes, "countryTo", countryTo);
|
||||
TachographNationRegistry.NationResolution fromResolution = TachographNationRegistry.resolve(countryFrom, null);
|
||||
TachographNationRegistry.NationResolution toResolution = TachographNationRegistry.resolve(countryTo, null);
|
||||
put(attributes, "countryFrom", fromResolution.legacyNation());
|
||||
put(attributes, "countryFromNumericCode", fromResolution.numericCode());
|
||||
put(attributes, "countryTo", toResolution.legacyNation());
|
||||
put(attributes, "countryToNumericCode", toResolution.numericCode());
|
||||
return new EventDetailsDto("BORDER_CROSSING", objectMapper.valueToTree(attributes));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,84 @@ create table if not exists eventhub.source_master_relation (
|
|||
constraint chk_source_master_relation_valid_time_order check (valid_from is null or valid_to is null or valid_from <= valid_to)
|
||||
);
|
||||
|
||||
create table if not exists eventhub.nation (
|
||||
numeric_code integer primary key,
|
||||
alpha_code text,
|
||||
name text not null,
|
||||
default_language_code text,
|
||||
is_known boolean not null default true,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
insert into eventhub.nation(numeric_code, alpha_code, name, default_language_code, is_known)
|
||||
values
|
||||
(0, '0', 'Unknown 0', null, true),
|
||||
(1, 'A', 'Austria', 'de-AT', true),
|
||||
(2, 'AL', 'Albania', 'sq-AL', true),
|
||||
(3, 'AND', 'Andorra', 'ca', true),
|
||||
(4, 'ARM', 'Armenia', 'hy-AM', true),
|
||||
(5, 'AZ', 'Azerbaijan', 'az', true),
|
||||
(6, 'B', 'Belgium', null, true),
|
||||
(7, 'BG', 'Bulgaria', 'bg-BG', true),
|
||||
(8, 'BIH', 'Bosnia Herzegovina', null, true),
|
||||
(9, 'BY', 'Belarus', 'be-BY', true),
|
||||
(10, 'CH', 'Switzerland', 'de-CH', true),
|
||||
(11, 'CY', 'Cyprus', null, true),
|
||||
(12, 'CZ', 'Czech Republic', 'cs-CZ', true),
|
||||
(13, 'D', 'Germany', 'de-DE', true),
|
||||
(14, 'DK', 'Denmark', 'da-DK', true),
|
||||
(15, 'E', 'Spain', 'es-ES', true),
|
||||
(16, 'EST', 'Estonia', 'et-EE', true),
|
||||
(17, 'F', 'France', 'fr-FR', true),
|
||||
(18, 'FIN', 'Finland', 'fi-FI', true),
|
||||
(19, 'FL', 'Liechtenstein', 'de-LI', true),
|
||||
(20, 'FR', 'Faroe Islands', 'fo-FO', true),
|
||||
(21, 'UK', 'United Kingdom', 'en-GB', true),
|
||||
(22, 'GE', 'Georgia', 'ka-GE', true),
|
||||
(23, 'GR', 'Greece', 'el-GR', true),
|
||||
(24, 'H', 'Hungary', 'hu-HU', true),
|
||||
(25, 'HR', 'Croatia', 'hr-HR', true),
|
||||
(26, 'I', 'Italy', 'it-IT', true),
|
||||
(27, 'IRL', 'Ireland', 'en-IE', true),
|
||||
(28, 'IS', 'Iceland', 'is-IS', true),
|
||||
(29, 'KZ', 'Kazakhstan', 'kk-KZ', true),
|
||||
(30, 'L', 'Luxembourg', 'de-LU', true),
|
||||
(31, 'LT', 'Lithuania', 'lt-LT', true),
|
||||
(32, 'LV', 'Latvia', 'lv-LV', true),
|
||||
(33, 'M', 'Malta', null, true),
|
||||
(34, 'MC', 'Monaco', 'fr-MC', true),
|
||||
(35, 'MD', 'Moldova', 'ro', true),
|
||||
(36, 'MK', 'North Macedonia', 'mk-MK', true),
|
||||
(37, 'N', 'Norway', 'no', true),
|
||||
(38, 'NL', 'Netherlands', 'nl-NL', true),
|
||||
(39, 'P', 'Portugal', 'pt-PT', true),
|
||||
(40, 'PL', 'Poland', 'pl-PL', true),
|
||||
(41, 'RO', 'Romania', 'ro-RO', true),
|
||||
(42, 'RSM', 'San Marino', 'it-IT', true),
|
||||
(43, 'RUS', 'Russia', 'ru-RU', true),
|
||||
(44, 'S', 'Sweden', 'sv-SE', true),
|
||||
(45, 'SK', 'Slovakia', 'sk-SK', true),
|
||||
(46, 'SLO', 'Slovenia', 'sl-SI', true),
|
||||
(47, 'TM', 'Turkmenistan', null, true),
|
||||
(48, 'TR', 'Turkey', 'tr-TR', true),
|
||||
(49, 'UA', 'Ukraine', 'uk-UA', true),
|
||||
(50, 'V', 'Vatican City', 'it-IT', true),
|
||||
(51, 'YU', 'Yugoslavia', null, true),
|
||||
(52, 'MNE', 'Montenegro', null, true),
|
||||
(53, 'SRB', 'Serbia', null, true),
|
||||
(54, 'UZ', 'Uzbekistan', null, true),
|
||||
(55, 'TJ', 'Tajikistan', null, true),
|
||||
(253, 'EC', 'European Community', null, true),
|
||||
(254, 'EUR', 'Rest of Europe', null, true),
|
||||
(255, 'WLD', 'Rest of the world', null, true)
|
||||
on conflict (numeric_code) do update set
|
||||
alpha_code = excluded.alpha_code,
|
||||
name = excluded.name,
|
||||
default_language_code = excluded.default_language_code,
|
||||
is_known = excluded.is_known,
|
||||
updated_at = now();
|
||||
|
||||
create table if not exists eventhub.driver (
|
||||
id uuid primary key,
|
||||
first_names text,
|
||||
|
|
@ -160,6 +238,7 @@ create table if not exists eventhub.driver_card (
|
|||
id uuid primary key,
|
||||
driver_id uuid references eventhub.driver(id),
|
||||
nation text not null,
|
||||
nation_numeric_code integer references eventhub.nation(numeric_code),
|
||||
card_number text not null,
|
||||
source_updated_at timestamptz,
|
||||
payload jsonb not null default '{}'::jsonb,
|
||||
|
|
@ -203,6 +282,7 @@ create table if not exists eventhub.vehicle (
|
|||
create table if not exists eventhub.vehicle_registration (
|
||||
id uuid primary key,
|
||||
nation text not null,
|
||||
nation_numeric_code integer references eventhub.nation(numeric_code),
|
||||
registration_number text not null,
|
||||
source_updated_at timestamptz,
|
||||
payload jsonb not null default '{}'::jsonb,
|
||||
|
|
@ -353,6 +433,10 @@ create index if not exists idx_vehicle_vin
|
|||
create index if not exists idx_vehicle_registration_plate
|
||||
on eventhub.vehicle_registration(nation, registration_number);
|
||||
|
||||
create index if not exists idx_vehicle_registration_numeric_key
|
||||
on eventhub.vehicle_registration(nation_numeric_code, registration_number)
|
||||
where nation_numeric_code is not null;
|
||||
|
||||
create index if not exists idx_vehicle_registration_assignment_registration_time
|
||||
on eventhub.vehicle_registration_assignment(vehicle_registration_id, valid_from desc, valid_to);
|
||||
|
||||
|
|
@ -382,6 +466,10 @@ create index if not exists idx_event_domain_type_time
|
|||
create index if not exists idx_driver_card_key
|
||||
on eventhub.driver_card(nation, card_number);
|
||||
|
||||
create index if not exists idx_driver_card_numeric_key
|
||||
on eventhub.driver_card(nation_numeric_code, card_number)
|
||||
where nation_numeric_code is not null;
|
||||
|
||||
create index if not exists idx_driver_card_driver
|
||||
on eventhub.driver_card(driver_id)
|
||||
where driver_id is not null;
|
||||
|
|
@ -396,6 +484,10 @@ create unique index if not exists ux_vehicle_vin
|
|||
create unique index if not exists ux_vehicle_registration_plate
|
||||
on eventhub.vehicle_registration(nation, registration_number);
|
||||
|
||||
create unique index if not exists ux_eventhub_nation_alpha_code
|
||||
on eventhub.nation(alpha_code)
|
||||
where alpha_code is not null;
|
||||
|
||||
create index if not exists idx_source_driver_identity_driver
|
||||
on eventhub.source_driver_identity(driver_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
create table if not exists eventhub.nation (
|
||||
numeric_code integer primary key,
|
||||
alpha_code text,
|
||||
name text not null,
|
||||
default_language_code text,
|
||||
is_known boolean not null default true,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create unique index if not exists ux_eventhub_nation_alpha_code
|
||||
on eventhub.nation(alpha_code)
|
||||
where alpha_code is not null;
|
||||
|
||||
insert into eventhub.nation(numeric_code, alpha_code, name, default_language_code, is_known)
|
||||
values
|
||||
(0, '0', 'Unknown 0', null, true),
|
||||
(1, 'A', 'Austria', 'de-AT', true),
|
||||
(2, 'AL', 'Albania', 'sq-AL', true),
|
||||
(3, 'AND', 'Andorra', 'ca', true),
|
||||
(4, 'ARM', 'Armenia', 'hy-AM', true),
|
||||
(5, 'AZ', 'Azerbaijan', 'az', true),
|
||||
(6, 'B', 'Belgium', null, true),
|
||||
(7, 'BG', 'Bulgaria', 'bg-BG', true),
|
||||
(8, 'BIH', 'Bosnia Herzegovina', null, true),
|
||||
(9, 'BY', 'Belarus', 'be-BY', true),
|
||||
(10, 'CH', 'Switzerland', 'de-CH', true),
|
||||
(11, 'CY', 'Cyprus', null, true),
|
||||
(12, 'CZ', 'Czech Republic', 'cs-CZ', true),
|
||||
(13, 'D', 'Germany', 'de-DE', true),
|
||||
(14, 'DK', 'Denmark', 'da-DK', true),
|
||||
(15, 'E', 'Spain', 'es-ES', true),
|
||||
(16, 'EST', 'Estonia', 'et-EE', true),
|
||||
(17, 'F', 'France', 'fr-FR', true),
|
||||
(18, 'FIN', 'Finland', 'fi-FI', true),
|
||||
(19, 'FL', 'Liechtenstein', 'de-LI', true),
|
||||
(20, 'FR', 'Faroe Islands', 'fo-FO', true),
|
||||
(21, 'UK', 'United Kingdom', 'en-GB', true),
|
||||
(22, 'GE', 'Georgia', 'ka-GE', true),
|
||||
(23, 'GR', 'Greece', 'el-GR', true),
|
||||
(24, 'H', 'Hungary', 'hu-HU', true),
|
||||
(25, 'HR', 'Croatia', 'hr-HR', true),
|
||||
(26, 'I', 'Italy', 'it-IT', true),
|
||||
(27, 'IRL', 'Ireland', 'en-IE', true),
|
||||
(28, 'IS', 'Iceland', 'is-IS', true),
|
||||
(29, 'KZ', 'Kazakhstan', 'kk-KZ', true),
|
||||
(30, 'L', 'Luxembourg', 'de-LU', true),
|
||||
(31, 'LT', 'Lithuania', 'lt-LT', true),
|
||||
(32, 'LV', 'Latvia', 'lv-LV', true),
|
||||
(33, 'M', 'Malta', null, true),
|
||||
(34, 'MC', 'Monaco', 'fr-MC', true),
|
||||
(35, 'MD', 'Moldova', 'ro', true),
|
||||
(36, 'MK', 'North Macedonia', 'mk-MK', true),
|
||||
(37, 'N', 'Norway', 'no', true),
|
||||
(38, 'NL', 'Netherlands', 'nl-NL', true),
|
||||
(39, 'P', 'Portugal', 'pt-PT', true),
|
||||
(40, 'PL', 'Poland', 'pl-PL', true),
|
||||
(41, 'RO', 'Romania', 'ro-RO', true),
|
||||
(42, 'RSM', 'San Marino', 'it-IT', true),
|
||||
(43, 'RUS', 'Russia', 'ru-RU', true),
|
||||
(44, 'S', 'Sweden', 'sv-SE', true),
|
||||
(45, 'SK', 'Slovakia', 'sk-SK', true),
|
||||
(46, 'SLO', 'Slovenia', 'sl-SI', true),
|
||||
(47, 'TM', 'Turkmenistan', null, true),
|
||||
(48, 'TR', 'Turkey', 'tr-TR', true),
|
||||
(49, 'UA', 'Ukraine', 'uk-UA', true),
|
||||
(50, 'V', 'Vatican City', 'it-IT', true),
|
||||
(51, 'YU', 'Yugoslavia', null, true),
|
||||
(52, 'MNE', 'Montenegro', null, true),
|
||||
(53, 'SRB', 'Serbia', null, true),
|
||||
(54, 'UZ', 'Uzbekistan', null, true),
|
||||
(55, 'TJ', 'Tajikistan', null, true),
|
||||
(253, 'EC', 'European Community', null, true),
|
||||
(254, 'EUR', 'Rest of Europe', null, true),
|
||||
(255, 'WLD', 'Rest of the world', null, true)
|
||||
on conflict (numeric_code) do update set
|
||||
alpha_code = excluded.alpha_code,
|
||||
name = excluded.name,
|
||||
default_language_code = excluded.default_language_code,
|
||||
is_known = excluded.is_known,
|
||||
updated_at = now();
|
||||
|
||||
alter table eventhub.driver_card
|
||||
add column if not exists nation_numeric_code integer references eventhub.nation(numeric_code);
|
||||
|
||||
alter table eventhub.vehicle_registration
|
||||
add column if not exists nation_numeric_code integer references eventhub.nation(numeric_code);
|
||||
|
||||
create index if not exists idx_driver_card_numeric_key
|
||||
on eventhub.driver_card(nation_numeric_code, card_number)
|
||||
where nation_numeric_code is not null;
|
||||
|
||||
create index if not exists idx_vehicle_registration_numeric_key
|
||||
on eventhub.vehicle_registration(nation_numeric_code, registration_number)
|
||||
where nation_numeric_code is not null;
|
||||
|
||||
update eventhub.driver_card card
|
||||
set nation_numeric_code = nation_ref.numeric_code
|
||||
from eventhub.nation nation_ref
|
||||
where card.nation_numeric_code is null
|
||||
and upper(card.nation) = upper(nation_ref.alpha_code);
|
||||
|
||||
update eventhub.driver_card card
|
||||
set nation_numeric_code = cast(card.nation as integer)
|
||||
where card.nation_numeric_code is null
|
||||
and trim(card.nation) ~ '^[0-9]+$';
|
||||
|
||||
update eventhub.driver_card card
|
||||
set nation_numeric_code = cast(substring(card.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') as integer)
|
||||
where card.nation_numeric_code is null
|
||||
and substring(card.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') is not null;
|
||||
|
||||
update eventhub.driver_card card
|
||||
set nation = coalesce(nation_ref.alpha_code, 'Unknown ' || card.nation_numeric_code::text)
|
||||
from eventhub.nation nation_ref
|
||||
where card.nation_numeric_code is not null
|
||||
and nation_ref.numeric_code = card.nation_numeric_code
|
||||
and (
|
||||
trim(card.nation) ~ '^[0-9]+$'
|
||||
or substring(card.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') is not null
|
||||
);
|
||||
|
||||
update eventhub.vehicle_registration registration
|
||||
set nation_numeric_code = nation_ref.numeric_code
|
||||
from eventhub.nation nation_ref
|
||||
where registration.nation_numeric_code is null
|
||||
and upper(registration.nation) = upper(nation_ref.alpha_code);
|
||||
|
||||
update eventhub.vehicle_registration registration
|
||||
set nation_numeric_code = cast(registration.nation as integer)
|
||||
where registration.nation_numeric_code is null
|
||||
and trim(registration.nation) ~ '^[0-9]+$';
|
||||
|
||||
update eventhub.vehicle_registration registration
|
||||
set nation_numeric_code = cast(substring(registration.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') as integer)
|
||||
where registration.nation_numeric_code is null
|
||||
and substring(registration.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') is not null;
|
||||
|
||||
update eventhub.vehicle_registration registration
|
||||
set nation = coalesce(nation_ref.alpha_code, 'Unknown ' || registration.nation_numeric_code::text)
|
||||
from eventhub.nation nation_ref
|
||||
where registration.nation_numeric_code is not null
|
||||
and nation_ref.numeric_code = registration.nation_numeric_code
|
||||
and (
|
||||
trim(registration.nation) ~ '^[0-9]+$'
|
||||
or substring(registration.nation from '^[Uu]nknown[[:space:]]+([0-9]+)$') is not null
|
||||
);
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
ID;Name;AlphaCode;NumericCode;DefaultLanguageCode;ID_Certificate;ID_FileLog
|
||||
0;Unknown 0;0;0;NULL;NULL;NULL
|
||||
1;Austria;A;1;de-AT;NULL;NULL
|
||||
2;Albania;AL;2;sq-AL;NULL;NULL
|
||||
3;Andorra;AND;3;ca;NULL;NULL
|
||||
4;Armenia;ARM;4;hy-AM;NULL;NULL
|
||||
5;Azerbaijan;AZ;5;az;NULL;NULL
|
||||
6;Belgium;B;6;NULL;NULL;NULL
|
||||
7;Bulgaria;BG;7;bg-BG;NULL;NULL
|
||||
8;Bosnia Herzegovina;BIH;8;NULL;NULL;NULL
|
||||
9;Belarus;BY;9;be-BY;NULL;NULL
|
||||
10;Switzerland;CH;10;de-CH;NULL;NULL
|
||||
11;Cyprus;CY;11;NULL;NULL;NULL
|
||||
12;Czech Republic;CZ;12;cs-CZ;NULL;NULL
|
||||
13;Germany;D;13;de-DE;NULL;NULL
|
||||
14;Denmark;DK;14;da-DK;NULL;NULL
|
||||
15;Spain;E;15;es-ES;NULL;NULL
|
||||
16;Estonia;EST;16;et-EE;NULL;NULL
|
||||
17;France;F;17;fr-FR;NULL;NULL
|
||||
18;Finland;FIN;18;fi-FI;NULL;NULL
|
||||
19;Liechtenstein;FL;19;de-LI;NULL;NULL
|
||||
20;Faroe Islands;FR;20;fo-FO;NULL;NULL
|
||||
21;United Kingdom;UK;21;en-GB;NULL;NULL
|
||||
22;Georgia;GE;22;ka-GE;NULL;NULL
|
||||
23;Greece;GR;23;el-GR;NULL;NULL
|
||||
24;Hungary;H;24;hu-HU;NULL;NULL
|
||||
25;Croatia;HR;25;hr-HR;NULL;NULL
|
||||
26;Italy;I;26;it-IT;NULL;NULL
|
||||
27;Ireland;IRL;27;en-IE;NULL;NULL
|
||||
28;Iceland;IS;28;is-IS;NULL;NULL
|
||||
29;Kazakhstan;KZ;29;kk-KZ;NULL;NULL
|
||||
30;Luxembourg;L;30;de-LU;NULL;NULL
|
||||
31;Lithuania;LT;31;lt-LT;NULL;NULL
|
||||
32;Latvia;LV;32;lv-LV;NULL;NULL
|
||||
33;Malta;M;33;NULL;NULL;NULL
|
||||
34;Monaco;MC;34;fr-MC;NULL;NULL
|
||||
35;Moldova;MD;35;ro;NULL;NULL
|
||||
36;North Macedonia;MK;36;mk-MK;NULL;NULL
|
||||
37;Norway;N;37;no;NULL;NULL
|
||||
38;Netherlands;NL;38;nl-NL;NULL;NULL
|
||||
39;Portugal;P;39;pt-PT;NULL;NULL
|
||||
40;Poland;PL;40;pl-PL;NULL;NULL
|
||||
41;Romania;RO;41;ro-RO;NULL;NULL
|
||||
42;San Marino;RSM;42;it-IT;NULL;NULL
|
||||
43;Russia;RUS;43;ru-RU;NULL;NULL
|
||||
44;Sweden;S;44;sv-SE;NULL;NULL
|
||||
45;Slovakia;SK;45;sk-SK;NULL;NULL
|
||||
46;Slovenia;SLO;46;sl-SI;NULL;NULL
|
||||
47;Turkmenistan;TM;47;NULL;NULL;NULL
|
||||
48;Turkey;TR;48;tr-TR;NULL;NULL
|
||||
49;Ukraine;UA;49;uk-UA;NULL;NULL
|
||||
50;Vatican City;V;50;it-IT;NULL;NULL
|
||||
51;Yugoslavia;YU;51;NULL;NULL;NULL
|
||||
52;Montenegro;MNE;52;NULL;NULL;763087
|
||||
53;Serbia;SRB;53;NULL;NULL;272576
|
||||
54;Uzbekistan;UZ;54;NULL;NULL;308001
|
||||
55;Tajikistan;TJ;55;NULL;NULL;NULL
|
||||
253;European Community;EC;253;NULL;NULL;NULL
|
||||
254;Rest of Europe;EUR;254;NULL;NULL;NULL
|
||||
255;Rest of the world;WLD;255;NULL;NULL;NULL
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package at.procon.eventhub.reference;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import at.procon.eventhub.dto.DriverCardRefDto;
|
||||
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class TachographNationRegistryTest {
|
||||
|
||||
@Test
|
||||
void resolvesKnownNumericNationCodeToAlphaAndNumeric() {
|
||||
DriverCardRefDto driverCard = new DriverCardRefDto("13", "CARD-1");
|
||||
VehicleRegistrationRefDto registration = new VehicleRegistrationRefDto("13", "W-1");
|
||||
|
||||
assertThat(driverCard.nation()).isEqualTo("D");
|
||||
assertThat(driverCard.nationNumericCode()).isEqualTo(13);
|
||||
assertThat(driverCard.stableKey()).isEqualTo("13:CARD-1");
|
||||
|
||||
assertThat(registration.nation()).isEqualTo("D");
|
||||
assertThat(registration.nationNumericCode()).isEqualTo(13);
|
||||
assertThat(registration.stableKey()).isEqualTo("13:W-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesKnownAlphaNationCodeToNumeric() {
|
||||
DriverCardRefDto driverCard = new DriverCardRefDto("A", "CARD-1");
|
||||
|
||||
assertThat(driverCard.nation()).isEqualTo("A");
|
||||
assertThat(driverCard.nationNumericCode()).isEqualTo(1);
|
||||
assertThat(driverCard.stableKey()).isEqualTo("1:CARD-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesUnknownNumericNationCodeAsSyntheticLabel() {
|
||||
DriverCardRefDto driverCard = new DriverCardRefDto("209", "CARD-1");
|
||||
|
||||
assertThat(driverCard.nation()).isEqualTo("Unknown 209");
|
||||
assertThat(driverCard.nationNumericCode()).isEqualTo(209);
|
||||
assertThat(driverCard.stableKey()).isEqualTo("209:CARD-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsSyntheticTelematicsNationOutsideTachographMapping() {
|
||||
VehicleRegistrationRefDto registration = new VehicleRegistrationRefDto("YELLOWFOX", "W-1");
|
||||
|
||||
assertThat(registration.nation()).isEqualTo("YELLOWFOX");
|
||||
assertThat(registration.nationNumericCode()).isNull();
|
||||
assertThat(registration.stableKey()).isEqualTo("YELLOWFOX:W-1");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue