Adjust runtime interval loading and result projections
This commit is contained in:
parent
7457872d51
commit
6bef8becf9
|
|
@ -55,6 +55,7 @@ public class EventHubEventReadRepository {
|
||||||
request.tenantKey(),
|
request.tenantKey(),
|
||||||
request.occurredFrom(),
|
request.occurredFrom(),
|
||||||
request.occurredTo(),
|
request.occurredTo(),
|
||||||
|
request.includeIntersectingIntervals() && "TACHOGRAPH".equalsIgnoreCase(providerKey),
|
||||||
request.driverSourceEntityId(),
|
request.driverSourceEntityId(),
|
||||||
request.driverCardNation(),
|
request.driverCardNation(),
|
||||||
request.driverCardNumber(),
|
request.driverCardNumber(),
|
||||||
|
|
@ -76,6 +77,7 @@ public class EventHubEventReadRepository {
|
||||||
request.tenantKey(),
|
request.tenantKey(),
|
||||||
request.occurredFrom(),
|
request.occurredFrom(),
|
||||||
request.occurredTo(),
|
request.occurredTo(),
|
||||||
|
request.includeIntersectingIntervals() && "TACHOGRAPH".equalsIgnoreCase(providerKey),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
@ -92,6 +94,7 @@ public class EventHubEventReadRepository {
|
||||||
String tenantKey,
|
String tenantKey,
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo,
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals,
|
||||||
String driverSourceEntityId,
|
String driverSourceEntityId,
|
||||||
String driverCardNation,
|
String driverCardNation,
|
||||||
String driverCardNumber,
|
String driverCardNumber,
|
||||||
|
|
@ -104,6 +107,7 @@ public class EventHubEventReadRepository {
|
||||||
) {
|
) {
|
||||||
StringBuilder sql = new StringBuilder(
|
StringBuilder sql = new StringBuilder(
|
||||||
"""
|
"""
|
||||||
|
with candidate_events as (
|
||||||
select
|
select
|
||||||
event.id,
|
event.id,
|
||||||
event.external_source_event_id,
|
event.external_source_event_id,
|
||||||
|
|
@ -223,14 +227,6 @@ public class EventHubEventReadRepository {
|
||||||
}
|
}
|
||||||
sql.append(")");
|
sql.append(")");
|
||||||
}
|
}
|
||||||
if (occurredFrom != null) {
|
|
||||||
sql.append(" and event.occurred_at >= ?");
|
|
||||||
params.add(occurredFrom);
|
|
||||||
}
|
|
||||||
if (occurredTo != null) {
|
|
||||||
sql.append(" and event.occurred_at <= ?");
|
|
||||||
params.add(occurredTo);
|
|
||||||
}
|
|
||||||
if (driverSourceEntityId != null) {
|
if (driverSourceEntityId != null) {
|
||||||
sql.append(
|
sql.append(
|
||||||
"""
|
"""
|
||||||
|
|
@ -307,7 +303,9 @@ public class EventHubEventReadRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sql.append(" order by event.occurred_at, event.event_domain, event.event_type, event.lifecycle, event.id");
|
sql.append("\n)");
|
||||||
|
appendTemporalFilter(sql, params, occurredFrom, occurredTo, includeIntersectingIntervals);
|
||||||
|
sql.append(" order by occurred_at, event_domain, event_type, lifecycle, id");
|
||||||
|
|
||||||
return jdbcTemplate.query(
|
return jdbcTemplate.query(
|
||||||
sql.toString(),
|
sql.toString(),
|
||||||
|
|
@ -316,6 +314,109 @@ public class EventHubEventReadRepository {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendTemporalFilter(
|
||||||
|
StringBuilder sql,
|
||||||
|
List<Object> params,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
|
) {
|
||||||
|
if (!includeIntersectingIntervals) {
|
||||||
|
sql.append("\nselect * from candidate_events");
|
||||||
|
appendPointWindowFilter(sql, params, occurredFrom, occurredTo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (occurredFrom == null && occurredTo == null) {
|
||||||
|
sql.append("\nselect * from candidate_events");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.append(
|
||||||
|
"""
|
||||||
|
|
||||||
|
, interval_events as (
|
||||||
|
select
|
||||||
|
'DRIVER_ACTIVITY' as interval_scope,
|
||||||
|
coalesce(payload #>> '{raw,intervalId}', payload #>> '{raw,sourceRowId}', external_source_event_id) as interval_key,
|
||||||
|
min(case when lifecycle = 'START' then occurred_at end) as started_at,
|
||||||
|
max(case when lifecycle = 'END' then occurred_at end) as ended_at
|
||||||
|
from candidate_events
|
||||||
|
where event_domain = 'DRIVER_ACTIVITY'
|
||||||
|
group by 1, 2
|
||||||
|
union all
|
||||||
|
select
|
||||||
|
'DRIVER_CARD' as interval_scope,
|
||||||
|
coalesce(payload #>> '{raw,intervalId}', payload #>> '{raw,sourceRowId}', external_source_event_id) as interval_key,
|
||||||
|
min(case when event_type = 'CARD_INSERTED' then occurred_at end) as started_at,
|
||||||
|
max(case when event_type = 'CARD_WITHDRAWN' then occurred_at end) as ended_at
|
||||||
|
from candidate_events
|
||||||
|
where event_domain = 'DRIVER_CARD'
|
||||||
|
and event_type in ('CARD_INSERTED', 'CARD_WITHDRAWN')
|
||||||
|
group by 1, 2
|
||||||
|
)
|
||||||
|
select ce.*
|
||||||
|
from candidate_events ce
|
||||||
|
left join interval_events ie
|
||||||
|
on ie.interval_key = coalesce(ce.payload #>> '{raw,intervalId}', ce.payload #>> '{raw,sourceRowId}', ce.external_source_event_id)
|
||||||
|
and (
|
||||||
|
(ie.interval_scope = 'DRIVER_ACTIVITY' and ce.event_domain = 'DRIVER_ACTIVITY')
|
||||||
|
or (ie.interval_scope = 'DRIVER_CARD'
|
||||||
|
and ce.event_domain = 'DRIVER_CARD'
|
||||||
|
and ce.event_type in ('CARD_INSERTED', 'CARD_WITHDRAWN'))
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
|
||||||
|
StringBuilder where = new StringBuilder();
|
||||||
|
appendPointWindowPredicate(where, params, "ce.occurred_at", occurredFrom, occurredTo);
|
||||||
|
if (where.length() == 0) {
|
||||||
|
where.append("\nwhere 1 = 0");
|
||||||
|
}
|
||||||
|
where.append("\n or (ie.interval_key is not null");
|
||||||
|
if (occurredTo != null) {
|
||||||
|
where.append("\n and ie.started_at <= ?");
|
||||||
|
params.add(occurredTo);
|
||||||
|
}
|
||||||
|
if (occurredFrom != null) {
|
||||||
|
where.append("\n and coalesce(ie.ended_at, 'infinity'::timestamptz) >= ?");
|
||||||
|
params.add(occurredFrom);
|
||||||
|
}
|
||||||
|
where.append("\n )");
|
||||||
|
sql.append(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendPointWindowFilter(
|
||||||
|
StringBuilder sql,
|
||||||
|
List<Object> params,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
StringBuilder where = new StringBuilder();
|
||||||
|
appendPointWindowPredicate(where, params, "occurred_at", occurredFrom, occurredTo);
|
||||||
|
sql.append(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendPointWindowPredicate(
|
||||||
|
StringBuilder sql,
|
||||||
|
List<Object> params,
|
||||||
|
String occurredAtColumn,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
boolean hasCondition = false;
|
||||||
|
if (occurredFrom != null) {
|
||||||
|
sql.append(hasCondition ? " and " : "\nwhere ");
|
||||||
|
sql.append(occurredAtColumn).append(" >= ?");
|
||||||
|
params.add(occurredFrom);
|
||||||
|
hasCondition = true;
|
||||||
|
}
|
||||||
|
if (occurredTo != null) {
|
||||||
|
sql.append(hasCondition ? " and " : "\nwhere ");
|
||||||
|
sql.append(occurredAtColumn).append(" <= ?");
|
||||||
|
params.add(occurredTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private EventHubEventDto mapEvent(ResultSet rs) throws SQLException {
|
private EventHubEventDto mapEvent(ResultSet rs) throws SQLException {
|
||||||
DriverRefDto driverRef = driverRef(rs);
|
DriverRefDto driverRef = driverRef(rs);
|
||||||
VehicleRefDto vehicleRef = vehicleRef(rs);
|
VehicleRefDto vehicleRef = vehicleRef(rs);
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,48 @@ public record DriverWorkingTimeProcessingResultDto(
|
||||||
List<DriverWorkingTimeSupportGeoEvent> supportGeoEvents,
|
List<DriverWorkingTimeSupportGeoEvent> supportGeoEvents,
|
||||||
List<String> notes
|
List<String> notes
|
||||||
) {
|
) {
|
||||||
|
public DriverWorkingTimeProcessingResultDto withIncludedIntervals(
|
||||||
|
boolean includeActivityIntervals,
|
||||||
|
boolean includeDrivingIntervals
|
||||||
|
) {
|
||||||
|
if (includeActivityIntervals && includeDrivingIntervals) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new DriverWorkingTimeProcessingResultDto(
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
|
sourceKind,
|
||||||
|
loadedFrom,
|
||||||
|
loadedTo,
|
||||||
|
requestedFrom,
|
||||||
|
requestedTo,
|
||||||
|
activityIntervalCount,
|
||||||
|
drivingIntervalCount,
|
||||||
|
drivingInterruptionIntervalCount,
|
||||||
|
drivingInterruptionVehicleChangeIntervalCount,
|
||||||
|
dailyWeeklyRestCandidateIntervalCount,
|
||||||
|
dailyWeeklyRestCandidateCoverageIntervalCount,
|
||||||
|
unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount,
|
||||||
|
potentialHomeOvernightStayIntervalCount,
|
||||||
|
potentialInVehicleOvernightStayIntervalCount,
|
||||||
|
potentialInVehicleTripIntervalCount,
|
||||||
|
vehicleUsageIntervalCount,
|
||||||
|
vuCardAbsentIntervalCount,
|
||||||
|
supportGeoEventCount,
|
||||||
|
includeActivityIntervals ? activityIntervals : List.of(),
|
||||||
|
includeDrivingIntervals ? drivingIntervals : List.of(),
|
||||||
|
drivingInterruptionIntervals,
|
||||||
|
drivingInterruptionVehicleChangeIntervals,
|
||||||
|
dailyWeeklyRestCandidateIntervals,
|
||||||
|
dailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
unclassifiedDailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
potentialHomeOvernightStayIntervals,
|
||||||
|
potentialInVehicleOvernightStayIntervals,
|
||||||
|
potentialInVehicleTripIntervals,
|
||||||
|
vehicleUsageIntervals,
|
||||||
|
vuCardAbsentIntervals,
|
||||||
|
supportGeoEvents,
|
||||||
|
notes
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,11 @@ public record UnifiedRuntimeProcessingApiRequest(
|
||||||
OffsetDateTime occurredTo,
|
OffsetDateTime occurredTo,
|
||||||
Boolean expandVehicleEvents,
|
Boolean expandVehicleEvents,
|
||||||
Integer vehicleExpansionPaddingMinutes,
|
Integer vehicleExpansionPaddingMinutes,
|
||||||
|
Boolean includeIntersectingIntervals,
|
||||||
Integer significantDrivingMinutes,
|
Integer significantDrivingMinutes,
|
||||||
Integer minimumRestPeriodMinutes
|
Integer minimumRestPeriodMinutes,
|
||||||
|
Boolean includeActivityIntervals,
|
||||||
|
Boolean includeDrivingIntervals
|
||||||
) {
|
) {
|
||||||
public UnifiedRuntimeProcessingRequest toRuntimeRequest() {
|
public UnifiedRuntimeProcessingRequest toRuntimeRequest() {
|
||||||
return new UnifiedRuntimeProcessingRequest(
|
return new UnifiedRuntimeProcessingRequest(
|
||||||
|
|
@ -52,7 +55,16 @@ public record UnifiedRuntimeProcessingApiRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents == null || expandVehicleEvents,
|
expandVehicleEvents == null || expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes == null ? 0 : Math.max(0, vehicleExpansionPaddingMinutes)
|
vehicleExpansionPaddingMinutes == null ? 0 : Math.max(0, vehicleExpansionPaddingMinutes),
|
||||||
|
includeIntersectingIntervals == null || includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean includeActivityIntervalsOrDefault() {
|
||||||
|
return includeActivityIntervals != null && includeActivityIntervals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean includeDrivingIntervalsOrDefault() {
|
||||||
|
return includeDrivingIntervals != null && includeDrivingIntervals;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,11 @@ public class DriverWorkingTimeDerivedProjectionsModule implements RuntimeProcess
|
||||||
for (Map.Entry<String, DriverWorkingTimePreparedInput> entry : preparedInputs.entrySet()) {
|
for (Map.Entry<String, DriverWorkingTimePreparedInput> entry : preparedInputs.entrySet()) {
|
||||||
DriverWorkingTimePreparedInput preparedInput = entry.getValue();
|
DriverWorkingTimePreparedInput preparedInput = entry.getValue();
|
||||||
DriverWorkingTimeProcessingResultDto projection =
|
DriverWorkingTimeProcessingResultDto projection =
|
||||||
workingTimeProcessingCore.process(preparedInput.processingInput());
|
workingTimeProcessingCore.process(preparedInput.processingInput())
|
||||||
|
.withIncludedIntervals(
|
||||||
|
scopeRequest.includeActivityIntervalsOrDefault(),
|
||||||
|
scopeRequest.includeDrivingIntervalsOrDefault()
|
||||||
|
);
|
||||||
warnings.addAll(preparedInput.partition().warnings());
|
warnings.addAll(preparedInput.partition().warnings());
|
||||||
UnifiedRuntimeProcessingRequest driverRequest = broadBundle.request().withDriverKey(preparedInput.driverKey());
|
UnifiedRuntimeProcessingRequest driverRequest = broadBundle.request().withDriverKey(preparedInput.driverKey());
|
||||||
driverResults.put(preparedInput.driverKey(), new UnifiedRuntimeDerivedProjectionResultDto(
|
driverResults.put(preparedInput.driverKey(), new UnifiedRuntimeDerivedProjectionResultDto(
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,8 @@ public class DriverWorkingTimeRuntimeProcessingPlan implements RuntimeProcessing
|
||||||
"minimumRestPeriodMinutes",
|
"minimumRestPeriodMinutes",
|
||||||
"attachVehicleOnlyEvents",
|
"attachVehicleOnlyEvents",
|
||||||
"vehicleEvidencePaddingMinutes",
|
"vehicleEvidencePaddingMinutes",
|
||||||
|
"includeActivityIntervals",
|
||||||
|
"includeDrivingIntervals",
|
||||||
"includePartitionDebug"
|
"includePartitionDebug"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -368,6 +370,10 @@ public class DriverWorkingTimeRuntimeProcessingPlan implements RuntimeProcessing
|
||||||
|
|
||||||
Integer significantDrivingMinutes = integerParameter(parameters, "significantDrivingMinutes", sourceSelection.significantDrivingMinutes());
|
Integer significantDrivingMinutes = integerParameter(parameters, "significantDrivingMinutes", sourceSelection.significantDrivingMinutes());
|
||||||
Integer minimumRestPeriodMinutes = integerParameter(parameters, "minimumRestPeriodMinutes", sourceSelection.minimumRestPeriodMinutes());
|
Integer minimumRestPeriodMinutes = integerParameter(parameters, "minimumRestPeriodMinutes", sourceSelection.minimumRestPeriodMinutes());
|
||||||
|
boolean includeActivityIntervals = booleanParameter(parameters, "includeActivityIntervals",
|
||||||
|
sourceSelection.includeActivityIntervalsOrDefault());
|
||||||
|
boolean includeDrivingIntervals = booleanParameter(parameters, "includeDrivingIntervals",
|
||||||
|
sourceSelection.includeDrivingIntervalsOrDefault());
|
||||||
boolean attachVehicleOnlyEvents = booleanParameter(parameters, "attachVehicleOnlyEvents",
|
boolean attachVehicleOnlyEvents = booleanParameter(parameters, "attachVehicleOnlyEvents",
|
||||||
partitioning == null ? sourceSelection.expandVehicleEvents() == null || sourceSelection.expandVehicleEvents() : partitioning.attachVehicleEvidenceOrDefault());
|
partitioning == null ? sourceSelection.expandVehicleEvents() == null || sourceSelection.expandVehicleEvents() : partitioning.attachVehicleEvidenceOrDefault());
|
||||||
Integer vehicleEvidencePaddingMinutes = nonNegativeIntegerParameter(parameters, "vehicleEvidencePaddingMinutes",
|
Integer vehicleEvidencePaddingMinutes = nonNegativeIntegerParameter(parameters, "vehicleEvidencePaddingMinutes",
|
||||||
|
|
@ -395,8 +401,11 @@ public class DriverWorkingTimeRuntimeProcessingPlan implements RuntimeProcessing
|
||||||
sourceSelection.occurredTo(),
|
sourceSelection.occurredTo(),
|
||||||
attachVehicleOnlyEvents,
|
attachVehicleOnlyEvents,
|
||||||
vehicleEvidencePaddingMinutes,
|
vehicleEvidencePaddingMinutes,
|
||||||
|
sourceSelection.includeIntersectingIntervals(),
|
||||||
significantDrivingMinutes,
|
significantDrivingMinutes,
|
||||||
minimumRestPeriodMinutes
|
minimumRestPeriodMinutes,
|
||||||
|
includeActivityIntervals,
|
||||||
|
includeDrivingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,11 @@ public class RuntimeTachographParityValidationService {
|
||||||
request.occurredTo(),
|
request.occurredTo(),
|
||||||
request.expandVehicleEventsOrDefault(),
|
request.expandVehicleEventsOrDefault(),
|
||||||
request.vehicleExpansionPaddingMinutesOrDefault(),
|
request.vehicleExpansionPaddingMinutesOrDefault(),
|
||||||
|
null,
|
||||||
request.significantDrivingMinutes(),
|
request.significantDrivingMinutes(),
|
||||||
request.minimumRestPeriodMinutes()
|
request.minimumRestPeriodMinutes(),
|
||||||
|
false,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
RuntimeEventPartitioningApiRequest partitioning = new RuntimeEventPartitioningApiRequest(
|
RuntimeEventPartitioningApiRequest partitioning = new RuntimeEventPartitioningApiRequest(
|
||||||
RuntimeEventPartitioningStrategy.DRIVER,
|
RuntimeEventPartitioningStrategy.DRIVER,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ public record UnifiedDriverEventsRequest(
|
||||||
String registrationNation,
|
String registrationNation,
|
||||||
String registrationNumber,
|
String registrationNumber,
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
public UnifiedDriverEventsRequest {
|
public UnifiedDriverEventsRequest {
|
||||||
Objects.requireNonNull(sourceFamily, "sourceFamily must not be null");
|
Objects.requireNonNull(sourceFamily, "sourceFamily must not be null");
|
||||||
|
|
@ -59,6 +60,16 @@ public record UnifiedDriverEventsRequest(
|
||||||
String driverKey,
|
String driverKey,
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
return forTachographFileSession(sessionId, driverKey, occurredFrom, occurredTo, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedDriverEventsRequest forTachographFileSession(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
return new UnifiedDriverEventsRequest(
|
return new UnifiedDriverEventsRequest(
|
||||||
UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION,
|
UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION,
|
||||||
|
|
@ -74,7 +85,8 @@ public record UnifiedDriverEventsRequest(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +105,29 @@ public record UnifiedDriverEventsRequest(
|
||||||
driverCardNumber,
|
driverCardNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
List.of()
|
List.of(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedDriverEventsRequest forTachographDbDriver(
|
||||||
|
String tenantKey,
|
||||||
|
String driverSourceEntityId,
|
||||||
|
String driverCardNation,
|
||||||
|
String driverCardNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
|
) {
|
||||||
|
return forTachographDbDriver(
|
||||||
|
tenantKey,
|
||||||
|
driverSourceEntityId,
|
||||||
|
driverCardNation,
|
||||||
|
driverCardNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
List.of(),
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +139,28 @@ public record UnifiedDriverEventsRequest(
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo,
|
OffsetDateTime occurredTo,
|
||||||
List<String> sourceKinds
|
List<String> sourceKinds
|
||||||
|
) {
|
||||||
|
return forTachographDbDriver(
|
||||||
|
tenantKey,
|
||||||
|
driverSourceEntityId,
|
||||||
|
driverCardNation,
|
||||||
|
driverCardNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
sourceKinds,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedDriverEventsRequest forTachographDbDriver(
|
||||||
|
String tenantKey,
|
||||||
|
String driverSourceEntityId,
|
||||||
|
String driverCardNation,
|
||||||
|
String driverCardNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
List<String> sourceKinds,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
return new UnifiedDriverEventsRequest(
|
return new UnifiedDriverEventsRequest(
|
||||||
UnifiedEventSourceFamily.TACHOGRAPH_DB,
|
UnifiedEventSourceFamily.TACHOGRAPH_DB,
|
||||||
|
|
@ -120,7 +176,8 @@ public record UnifiedDriverEventsRequest(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +204,8 @@ public record UnifiedDriverEventsRequest(
|
||||||
registrationNation,
|
registrationNation,
|
||||||
registrationNumber,
|
registrationNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,7 +231,8 @@ public record UnifiedDriverEventsRequest(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo,
|
OffsetDateTime occurredTo,
|
||||||
boolean expandVehicleEvents,
|
boolean expandVehicleEvents,
|
||||||
int vehicleExpansionPaddingMinutes
|
int vehicleExpansionPaddingMinutes,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
public UnifiedRuntimeProcessingRequest {
|
public UnifiedRuntimeProcessingRequest {
|
||||||
if (sourceFamilies == null || sourceFamilies.isEmpty()) {
|
if (sourceFamilies == null || sourceFamilies.isEmpty()) {
|
||||||
|
|
@ -118,7 +119,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredTo,
|
occurredTo,
|
||||||
UnifiedRuntimeEventBackend.SOURCE_DB,
|
UnifiedRuntimeEventBackend.SOURCE_DB,
|
||||||
true,
|
true,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +145,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredTo,
|
occurredTo,
|
||||||
UnifiedRuntimeEventBackend.SOURCE_DB,
|
UnifiedRuntimeEventBackend.SOURCE_DB,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +161,34 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
UnifiedRuntimeEventBackend eventBackend,
|
UnifiedRuntimeEventBackend eventBackend,
|
||||||
boolean expandVehicleEvents,
|
boolean expandVehicleEvents,
|
||||||
int vehicleExpansionPaddingMinutes
|
int vehicleExpansionPaddingMinutes
|
||||||
|
) {
|
||||||
|
return forDriver(
|
||||||
|
tenantKey,
|
||||||
|
sourceFamilies,
|
||||||
|
driverSourceEntityId,
|
||||||
|
driverCardNation,
|
||||||
|
driverCardNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
eventBackend,
|
||||||
|
expandVehicleEvents,
|
||||||
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedRuntimeProcessingRequest forDriver(
|
||||||
|
String tenantKey,
|
||||||
|
Set<UnifiedEventSourceFamily> sourceFamilies,
|
||||||
|
String driverSourceEntityId,
|
||||||
|
String driverCardNation,
|
||||||
|
String driverCardNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
UnifiedRuntimeEventBackend eventBackend,
|
||||||
|
boolean expandVehicleEvents,
|
||||||
|
int vehicleExpansionPaddingMinutes,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
return new UnifiedRuntimeProcessingRequest(
|
return new UnifiedRuntimeProcessingRequest(
|
||||||
null,
|
null,
|
||||||
|
|
@ -178,7 +209,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +244,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +276,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +308,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +340,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,7 +384,8 @@ public record UnifiedRuntimeProcessingRequest(
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
expandVehicleEvents,
|
expandVehicleEvents,
|
||||||
vehicleExpansionPaddingMinutes
|
vehicleExpansionPaddingMinutes,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ public record UnifiedVehicleEventsRequest(
|
||||||
String registrationNation,
|
String registrationNation,
|
||||||
String registrationNumber,
|
String registrationNumber,
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
public UnifiedVehicleEventsRequest {
|
public UnifiedVehicleEventsRequest {
|
||||||
Objects.requireNonNull(sourceFamily, "sourceFamily must not be null");
|
Objects.requireNonNull(sourceFamily, "sourceFamily must not be null");
|
||||||
|
|
@ -46,6 +47,28 @@ public record UnifiedVehicleEventsRequest(
|
||||||
String registrationNumber,
|
String registrationNumber,
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
return forTachographFileSession(
|
||||||
|
sessionId,
|
||||||
|
vehicleSourceEntityId,
|
||||||
|
vin,
|
||||||
|
registrationNation,
|
||||||
|
registrationNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedVehicleEventsRequest forTachographFileSession(
|
||||||
|
UUID sessionId,
|
||||||
|
String vehicleSourceEntityId,
|
||||||
|
String vin,
|
||||||
|
String registrationNation,
|
||||||
|
String registrationNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
return new UnifiedVehicleEventsRequest(
|
return new UnifiedVehicleEventsRequest(
|
||||||
UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION,
|
UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION,
|
||||||
|
|
@ -57,7 +80,8 @@ public record UnifiedVehicleEventsRequest(
|
||||||
registrationNation,
|
registrationNation,
|
||||||
registrationNumber,
|
registrationNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +102,31 @@ public record UnifiedVehicleEventsRequest(
|
||||||
registrationNumber,
|
registrationNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo,
|
occurredTo,
|
||||||
List.of()
|
List.of(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedVehicleEventsRequest forTachographDb(
|
||||||
|
String tenantKey,
|
||||||
|
String vehicleSourceEntityId,
|
||||||
|
String vin,
|
||||||
|
String registrationNation,
|
||||||
|
String registrationNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
|
) {
|
||||||
|
return forTachographDb(
|
||||||
|
tenantKey,
|
||||||
|
vehicleSourceEntityId,
|
||||||
|
vin,
|
||||||
|
registrationNation,
|
||||||
|
registrationNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
List.of(),
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +139,30 @@ public record UnifiedVehicleEventsRequest(
|
||||||
OffsetDateTime occurredFrom,
|
OffsetDateTime occurredFrom,
|
||||||
OffsetDateTime occurredTo,
|
OffsetDateTime occurredTo,
|
||||||
List<String> sourceKinds
|
List<String> sourceKinds
|
||||||
|
) {
|
||||||
|
return forTachographDb(
|
||||||
|
tenantKey,
|
||||||
|
vehicleSourceEntityId,
|
||||||
|
vin,
|
||||||
|
registrationNation,
|
||||||
|
registrationNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
sourceKinds,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UnifiedVehicleEventsRequest forTachographDb(
|
||||||
|
String tenantKey,
|
||||||
|
String vehicleSourceEntityId,
|
||||||
|
String vin,
|
||||||
|
String registrationNation,
|
||||||
|
String registrationNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
List<String> sourceKinds,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
) {
|
) {
|
||||||
return new UnifiedVehicleEventsRequest(
|
return new UnifiedVehicleEventsRequest(
|
||||||
UnifiedEventSourceFamily.TACHOGRAPH_DB,
|
UnifiedEventSourceFamily.TACHOGRAPH_DB,
|
||||||
|
|
@ -102,7 +174,8 @@ public record UnifiedVehicleEventsRequest(
|
||||||
registrationNation,
|
registrationNation,
|
||||||
registrationNumber,
|
registrationNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
includeIntersectingIntervals
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +198,8 @@ public record UnifiedVehicleEventsRequest(
|
||||||
registrationNation,
|
registrationNation,
|
||||||
registrationNumber,
|
registrationNumber,
|
||||||
occurredFrom,
|
occurredFrom,
|
||||||
occurredTo
|
occurredTo,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,8 @@ public class EventHubRuntimeEventLoader implements RuntimeDriverEventLoader, Run
|
||||||
request.driverCardNumber(),
|
request.driverCardNumber(),
|
||||||
request.occurredFrom(),
|
request.occurredFrom(),
|
||||||
request.occurredTo(),
|
request.occurredTo(),
|
||||||
request.tachographSourceKindNames()
|
request.tachographSourceKindNames(),
|
||||||
|
request.includeIntersectingIntervals()
|
||||||
);
|
);
|
||||||
case YELLOWFOX_DB -> UnifiedDriverEventsRequest.forYellowFoxDbDriver(
|
case YELLOWFOX_DB -> UnifiedDriverEventsRequest.forYellowFoxDbDriver(
|
||||||
request.tenantKey(),
|
request.tenantKey(),
|
||||||
|
|
@ -99,7 +100,8 @@ public class EventHubRuntimeEventLoader implements RuntimeDriverEventLoader, Run
|
||||||
vehicleRef.registrationNumber(),
|
vehicleRef.registrationNumber(),
|
||||||
request.vehicleOccurredFrom(),
|
request.vehicleOccurredFrom(),
|
||||||
request.vehicleOccurredTo(),
|
request.vehicleOccurredTo(),
|
||||||
request.tachographSourceKindNames()
|
request.tachographSourceKindNames(),
|
||||||
|
request.includeIntersectingIntervals()
|
||||||
);
|
);
|
||||||
case YELLOWFOX_DB -> UnifiedVehicleEventsRequest.forYellowFoxDb(
|
case YELLOWFOX_DB -> UnifiedVehicleEventsRequest.forYellowFoxDb(
|
||||||
request.tenantKey(),
|
request.tenantKey(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
package at.procon.eventhub.processing.service;
|
||||||
|
|
||||||
|
import at.procon.eventhub.dto.EventHubEventDto;
|
||||||
|
import at.procon.eventhub.dto.EventLifecycle;
|
||||||
|
import at.procon.eventhub.dto.EventType;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class RuntimeIntervalEventWindowSelector {
|
||||||
|
|
||||||
|
private RuntimeIntervalEventWindowSelector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static TachographTimelineEventBundle filterBundle(
|
||||||
|
TachographTimelineEventBundle bundle,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
|
) {
|
||||||
|
if (bundle == null) {
|
||||||
|
return new TachographTimelineEventBundle(List.of(), List.of(), List.of());
|
||||||
|
}
|
||||||
|
return new TachographTimelineEventBundle(
|
||||||
|
filterIntervalEvents(bundle.activityEvents(), occurredFrom, occurredTo, includeIntersectingIntervals),
|
||||||
|
filterIntervalEvents(bundle.vehicleUsageEvents(), occurredFrom, occurredTo, includeIntersectingIntervals),
|
||||||
|
filterPointEvents(bundle.supportEvents(), occurredFrom, occurredTo)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<EventHubEventDto> filterIntervalEvents(
|
||||||
|
List<EventHubEventDto> events,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
boolean includeIntersectingIntervals
|
||||||
|
) {
|
||||||
|
if (events == null || events.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
if (!includeIntersectingIntervals) {
|
||||||
|
return filterPointEvents(events, occurredFrom, occurredTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedHashMap<String, IntervalGroup> byInterval = new LinkedHashMap<>();
|
||||||
|
for (EventHubEventDto event : events) {
|
||||||
|
byInterval.computeIfAbsent(intervalKey(event), ignored -> new IntervalGroup()).add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<EventHubEventDto> result = new ArrayList<>();
|
||||||
|
for (IntervalGroup group : byInterval.values()) {
|
||||||
|
if (group.overlaps(occurredFrom, occurredTo)) {
|
||||||
|
result.addAll(group.events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return List.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<EventHubEventDto> filterPointEvents(
|
||||||
|
List<EventHubEventDto> events,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
return events.stream()
|
||||||
|
.filter(event -> withinWindow(event.occurredAt(), occurredFrom, occurredTo))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean withinWindow(
|
||||||
|
OffsetDateTime occurredAt,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo
|
||||||
|
) {
|
||||||
|
if (occurredAt == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (occurredFrom != null && occurredAt.isBefore(occurredFrom)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return occurredTo == null || !occurredAt.isAfter(occurredTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String intervalKey(EventHubEventDto event) {
|
||||||
|
JsonNode raw = raw(event);
|
||||||
|
String intervalId = text(raw, "intervalId");
|
||||||
|
if (intervalId != null) {
|
||||||
|
return intervalId;
|
||||||
|
}
|
||||||
|
String sourceRowId = text(raw, "sourceRowId");
|
||||||
|
return sourceRowId != null ? sourceRowId : event.externalSourceEventId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonNode raw(EventHubEventDto event) {
|
||||||
|
JsonNode payload = event == null ? null : event.payload();
|
||||||
|
if (payload == null || payload.isNull() || payload.isMissingNode()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JsonNode raw = payload.get("raw");
|
||||||
|
return raw == null || raw.isNull() ? payload : raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String text(JsonNode node, String field) {
|
||||||
|
if (node == null || field == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JsonNode value = node.get(field);
|
||||||
|
if (value == null || value.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String text = value.asText(null);
|
||||||
|
return text == null || text.isBlank() ? null : text.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class IntervalGroup {
|
||||||
|
private final List<EventHubEventDto> events = new ArrayList<>();
|
||||||
|
private OffsetDateTime startedAt;
|
||||||
|
private OffsetDateTime endedAt;
|
||||||
|
|
||||||
|
private void add(EventHubEventDto event) {
|
||||||
|
events.add(event);
|
||||||
|
if (event == null || event.occurredAt() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isStartEvent(event)) {
|
||||||
|
startedAt = min(startedAt, event.occurredAt());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isEndEvent(event)) {
|
||||||
|
endedAt = max(endedAt, event.occurredAt());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startedAt = min(startedAt, event.occurredAt());
|
||||||
|
endedAt = max(endedAt, event.occurredAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean overlaps(OffsetDateTime occurredFrom, OffsetDateTime occurredTo) {
|
||||||
|
if (startedAt == null && endedAt == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OffsetDateTime effectiveStart = startedAt == null ? endedAt : startedAt;
|
||||||
|
OffsetDateTime effectiveEnd = endedAt;
|
||||||
|
if (occurredTo != null && effectiveStart != null && effectiveStart.isAfter(occurredTo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return occurredFrom == null || effectiveEnd == null || !effectiveEnd.isBefore(occurredFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStartEvent(EventHubEventDto event) {
|
||||||
|
return event.lifecycle() == EventLifecycle.START
|
||||||
|
|| event.eventType() == EventType.CARD_INSERTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEndEvent(EventHubEventDto event) {
|
||||||
|
return event.lifecycle() == EventLifecycle.END
|
||||||
|
|| event.eventType() == EventType.CARD_WITHDRAWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime min(OffsetDateTime left, OffsetDateTime right) {
|
||||||
|
if (left == null) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == null) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.isBefore(right) ? left : right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime max(OffsetDateTime left, OffsetDateTime right) {
|
||||||
|
if (left == null) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == null) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.isAfter(right) ? left : right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -55,7 +55,8 @@ public class TachographFileSessionRuntimeEventLoader implements RuntimeDriverEve
|
||||||
sessionId,
|
sessionId,
|
||||||
request.driverKey(),
|
request.driverKey(),
|
||||||
request.occurredFrom(),
|
request.occurredFrom(),
|
||||||
request.occurredTo()
|
request.occurredTo(),
|
||||||
|
request.includeIntersectingIntervals()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +78,8 @@ public class TachographFileSessionRuntimeEventLoader implements RuntimeDriverEve
|
||||||
vehicleRef.registrationNation(),
|
vehicleRef.registrationNation(),
|
||||||
vehicleRef.registrationNumber(),
|
vehicleRef.registrationNumber(),
|
||||||
request.vehicleOccurredFrom(),
|
request.vehicleOccurredFrom(),
|
||||||
request.vehicleOccurredTo()
|
request.vehicleOccurredTo(),
|
||||||
|
request.includeIntersectingIntervals()
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ import at.procon.eventhub.processing.model.UnifiedDriverEventsRequest;
|
||||||
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
||||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
|
||||||
import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException;
|
import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException;
|
||||||
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
|
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
@ -38,30 +38,27 @@ public class TachographFileSessionUnifiedDriverEventSource implements UnifiedDri
|
||||||
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
|
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
|
||||||
if (request.driverKey() == null) {
|
if (request.driverKey() == null) {
|
||||||
return session.driversByKey().values().stream()
|
return session.driversByKey().values().stream()
|
||||||
.flatMap(driver -> eventBuilder.buildEvents(session, driver).stream())
|
.map(driver -> filterBundle(session, driver, request).allEvents())
|
||||||
.filter(event -> withinWindow(event.occurredAt(), request.occurredFrom(), request.occurredTo()))
|
.flatMap(List::stream)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
DriverExtractionSession driver = session.driversByKey().get(request.driverKey());
|
DriverExtractionSession driver = session.driversByKey().get(request.driverKey());
|
||||||
if (driver == null) {
|
if (driver == null) {
|
||||||
throw new DriverNotFoundInSessionException(request.sessionId(), request.driverKey());
|
throw new DriverNotFoundInSessionException(request.sessionId(), request.driverKey());
|
||||||
}
|
}
|
||||||
return eventBuilder.buildEvents(session, driver).stream()
|
return filterBundle(session, driver, request).allEvents();
|
||||||
.filter(event -> withinWindow(event.occurredAt(), request.occurredFrom(), request.occurredTo()))
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean withinWindow(
|
private TachographTimelineEventBundle filterBundle(
|
||||||
OffsetDateTime occurredAt,
|
TachographFileSession session,
|
||||||
OffsetDateTime occurredFrom,
|
DriverExtractionSession driver,
|
||||||
OffsetDateTime occurredTo
|
UnifiedDriverEventsRequest request
|
||||||
) {
|
) {
|
||||||
if (occurredAt == null) {
|
return RuntimeIntervalEventWindowSelector.filterBundle(
|
||||||
return false;
|
eventBuilder.buildEventBundle(session, driver),
|
||||||
}
|
request.occurredFrom(),
|
||||||
if (occurredFrom != null && occurredAt.isBefore(occurredFrom)) {
|
request.occurredTo(),
|
||||||
return false;
|
request.includeIntersectingIntervals()
|
||||||
}
|
);
|
||||||
return occurredTo == null || !occurredAt.isAfter(occurredTo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
||||||
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
|
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
|
||||||
import at.procon.eventhub.reference.TachographNationRegistry;
|
import at.procon.eventhub.reference.TachographNationRegistry;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
|
||||||
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
|
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
@ -37,9 +37,14 @@ public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVe
|
||||||
TachographFileSession session = repository.find(request.sessionId())
|
TachographFileSession session = repository.find(request.sessionId())
|
||||||
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
|
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
|
||||||
return session.driversByKey().values().stream()
|
return session.driversByKey().values().stream()
|
||||||
.flatMap(driver -> eventBuilder.buildEvents(session, driver).stream())
|
.map(driver -> RuntimeIntervalEventWindowSelector.filterBundle(
|
||||||
|
eventBuilder.buildEventBundle(session, driver),
|
||||||
|
request.occurredFrom(),
|
||||||
|
request.occurredTo(),
|
||||||
|
request.includeIntersectingIntervals()
|
||||||
|
).allEvents())
|
||||||
|
.flatMap(List::stream)
|
||||||
.filter(event -> matchesVehicle(event.vehicleRef(), request))
|
.filter(event -> matchesVehicle(event.vehicleRef(), request))
|
||||||
.filter(event -> withinWindow(event.occurredAt(), request.occurredFrom(), request.occurredTo()))
|
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
@ -61,20 +66,6 @@ public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVe
|
||||||
&& matchesNation(request.registrationNation(), vehicleRef.vehicleRegistration().nation(), vehicleRef.vehicleRegistration().nationNumericCode());
|
&& matchesNation(request.registrationNation(), vehicleRef.vehicleRegistration().nation(), vehicleRef.vehicleRegistration().nationNumericCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean withinWindow(
|
|
||||||
OffsetDateTime occurredAt,
|
|
||||||
OffsetDateTime occurredFrom,
|
|
||||||
OffsetDateTime occurredTo
|
|
||||||
) {
|
|
||||||
if (occurredAt == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (occurredFrom != null && occurredAt.isBefore(occurredFrom)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return occurredTo == null || !occurredAt.isAfter(occurredTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchesNation(String requestedNation, String actualNation, Integer actualNationNumericCode) {
|
private boolean matchesNation(String requestedNation, String actualNation, Integer actualNationNumericCode) {
|
||||||
if (requestedNation == null) {
|
if (requestedNation == null) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,10 @@ public class UnifiedEventTimelineReconstructor {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
JsonNode raw = raw(event);
|
JsonNode raw = raw(event);
|
||||||
String intervalId = firstNonBlank(
|
String intervalId = firstNonBlank(text(raw, "intervalId"), text(raw, "sourceRowId"), event.externalSourceEventId());
|
||||||
text(raw, "intervalId"),
|
if (intervalId == null) {
|
||||||
text(raw, "sourceRowId"),
|
continue;
|
||||||
event.externalSourceEventId()
|
}
|
||||||
);
|
|
||||||
ActivityAccumulator accumulator = byIntervalId.computeIfAbsent(
|
ActivityAccumulator accumulator = byIntervalId.computeIfAbsent(
|
||||||
intervalId,
|
intervalId,
|
||||||
ignored -> new ActivityAccumulator(intervalId)
|
ignored -> new ActivityAccumulator(intervalId)
|
||||||
|
|
@ -102,11 +101,10 @@ public class UnifiedEventTimelineReconstructor {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
JsonNode raw = raw(event);
|
JsonNode raw = raw(event);
|
||||||
String intervalId = firstNonBlank(
|
String intervalId = firstNonBlank(text(raw, "intervalId"), text(raw, "sourceRowId"), event.externalSourceEventId());
|
||||||
text(raw, "intervalId"),
|
if (intervalId == null) {
|
||||||
text(raw, "sourceRowId"),
|
continue;
|
||||||
event.externalSourceEventId()
|
}
|
||||||
);
|
|
||||||
VehicleUsageAccumulator accumulator = byIntervalId.computeIfAbsent(
|
VehicleUsageAccumulator accumulator = byIntervalId.computeIfAbsent(
|
||||||
intervalId,
|
intervalId,
|
||||||
ignored -> new VehicleUsageAccumulator(sessionId, driverKey, intervalId)
|
ignored -> new VehicleUsageAccumulator(sessionId, driverKey, intervalId)
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,11 @@ public class UnifiedRuntimeDerivedProjectionService {
|
||||||
.toList(),
|
.toList(),
|
||||||
notes
|
notes
|
||||||
);
|
);
|
||||||
DriverWorkingTimeProcessingResultDto projection = workingTimeProcessingCore.process(processingInput);
|
DriverWorkingTimeProcessingResultDto projection = workingTimeProcessingCore.process(processingInput)
|
||||||
|
.withIncludedIntervals(
|
||||||
|
apiRequest.includeActivityIntervalsOrDefault(),
|
||||||
|
apiRequest.includeDrivingIntervalsOrDefault()
|
||||||
|
);
|
||||||
notes = projection.notes();
|
notes = projection.notes();
|
||||||
|
|
||||||
RuntimeSupportEvidenceNormalizationDebugDto normalizationDebug = new RuntimeSupportEvidenceNormalizationDebugDto(
|
RuntimeSupportEvidenceNormalizationDebugDto normalizationDebug = new RuntimeSupportEvidenceNormalizationDebugDto(
|
||||||
|
|
|
||||||
|
|
@ -145,4 +145,5 @@ final class TachographRawPayloadSupport {
|
||||||
|| message.toLowerCase(java.util.Locale.ROOT).contains("not valid")
|
|| message.toLowerCase(java.util.Locale.ROOT).contains("not valid")
|
||||||
|| message.toLowerCase(java.util.Locale.ROOT).contains("invalid")));
|
|| message.toLowerCase(java.util.Locale.ROOT).contains("invalid")));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE
|
||||||
Map<String, Object> raw = new LinkedHashMap<>();
|
Map<String, Object> raw = new LinkedHashMap<>();
|
||||||
raw.put("intervalId", interval.intervalId());
|
raw.put("intervalId", interval.intervalId());
|
||||||
raw.put("sourceRowId", interval.intervalId());
|
raw.put("sourceRowId", interval.intervalId());
|
||||||
|
raw.put("sessionId", session.sessionId().toString());
|
||||||
raw.put("driverKey", driverKey);
|
raw.put("driverKey", driverKey);
|
||||||
raw.put("activityType", interval.activityType());
|
raw.put("activityType", interval.activityType());
|
||||||
raw.put("sourceRowIds", interval.sourceIntervalIds());
|
raw.put("sourceRowIds", interval.sourceIntervalIds());
|
||||||
|
|
@ -232,6 +233,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE
|
||||||
Map<String, Object> raw = new LinkedHashMap<>();
|
Map<String, Object> raw = new LinkedHashMap<>();
|
||||||
raw.put("intervalId", interval.intervalId());
|
raw.put("intervalId", interval.intervalId());
|
||||||
raw.put("sourceRowId", interval.intervalId());
|
raw.put("sourceRowId", interval.intervalId());
|
||||||
|
raw.put("sessionId", session.sessionId().toString());
|
||||||
raw.put("driverKey", driverKey);
|
raw.put("driverKey", driverKey);
|
||||||
raw.put("sourceRowIds", interval.sourceIntervalIds());
|
raw.put("sourceRowIds", interval.sourceIntervalIds());
|
||||||
raw.put("startedAt", timeText(interval.from()));
|
raw.put("startedAt", timeText(interval.from()));
|
||||||
|
|
@ -312,6 +314,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE
|
||||||
EventDetailsDto details = supportDetails(eventDomain, supportEvent);
|
EventDetailsDto details = supportDetails(eventDomain, supportEvent);
|
||||||
Map<String, Object> raw = new LinkedHashMap<>();
|
Map<String, Object> raw = new LinkedHashMap<>();
|
||||||
raw.put("sourceRowId", supportEvent.eventId());
|
raw.put("sourceRowId", supportEvent.eventId());
|
||||||
|
raw.put("sessionId", session.sessionId().toString());
|
||||||
raw.put("supportEventId", supportEvent.eventId());
|
raw.put("supportEventId", supportEvent.eventId());
|
||||||
raw.put("driverKey", supportEvent.driverKey());
|
raw.put("driverKey", supportEvent.driverKey());
|
||||||
raw.put("supportEventType", supportEvent.eventType());
|
raw.put("supportEventType", supportEvent.eventType());
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ create schema DriverActivityIntervalEvent(
|
||||||
);
|
);
|
||||||
|
|
||||||
create window OpenDriverActivityPoint#unique(driverKey, intervalId) as DriverActivityPointEvent;
|
create window OpenDriverActivityPoint#unique(driverKey, intervalId) as DriverActivityPointEvent;
|
||||||
|
|
||||||
insert into OpenDriverActivityPoint
|
insert into OpenDriverActivityPoint
|
||||||
select *
|
select *
|
||||||
from DriverActivityPointEvent(lifecycle = 'START');
|
from DriverActivityPointEvent(lifecycle = 'START');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.dto;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class DriverWorkingTimeProcessingResultDtoTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canExcludeActivityAndDrivingIntervalsWhileKeepingCounts() {
|
||||||
|
DriverWorkingTimeActivityInterval activityInterval = new DriverWorkingTimeActivityInterval(
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
"ACT-1",
|
||||||
|
"WORK",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"DRIVER_CARD",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z").toEpochSecond(),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z").toEpochSecond(),
|
||||||
|
3600L,
|
||||||
|
List.of("ACT-1"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"RAW_INTERVAL"
|
||||||
|
);
|
||||||
|
DriverWorkingTimeActivityInterval drivingInterval = new DriverWorkingTimeActivityInterval(
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
"DRV-1",
|
||||||
|
"DRIVE",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"DRIVER_CARD",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z").toEpochSecond(),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z").toEpochSecond(),
|
||||||
|
3600L,
|
||||||
|
List.of("DRV-1"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"RAW_INTERVAL"
|
||||||
|
);
|
||||||
|
DriverWorkingTimeProcessingResultDto result = new DriverWorkingTimeProcessingResultDto(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"12:123",
|
||||||
|
"UNIFIED_EVENT_STREAM",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
List.of(activityInterval),
|
||||||
|
List.of(drivingInterval),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of("note")
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverWorkingTimeProcessingResultDto compact = result.withIncludedIntervals(false, false);
|
||||||
|
|
||||||
|
assertThat(compact.activityIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(compact.drivingIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(compact.activityIntervals()).isEmpty();
|
||||||
|
assertThat(compact.drivingIntervals()).isEmpty();
|
||||||
|
assertThat(compact.notes()).containsExactly("note");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,6 +49,9 @@ class RuntimeEventProcessingServiceTest {
|
||||||
true,
|
true,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
),
|
),
|
||||||
new RuntimeEventPartitioningApiRequest(RuntimeEventPartitioningStrategy.DRIVER, null, false, null, false, null, false, null, null, null),
|
new RuntimeEventPartitioningApiRequest(RuntimeEventPartitioningStrategy.DRIVER, null, false, null, false, null, false, null, null, null),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
package at.procon.eventhub.processing.eventprocessing.module;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeDriverPartition;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimePreparedInput;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeProcessingCore;
|
||||||
|
import at.procon.eventhub.processing.dto.UnifiedRuntimeDerivedProjectionResultDto;
|
||||||
|
import at.procon.eventhub.processing.dto.UnifiedRuntimeProcessingApiRequest;
|
||||||
|
import at.procon.eventhub.processing.dto.UnifiedRuntimeDriverWorkingTimeScopeResultDto;
|
||||||
|
import at.procon.eventhub.processing.eventprocessing.plan.RuntimeProcessingExecutionApiRequest;
|
||||||
|
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
|
||||||
|
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
|
||||||
|
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class DriverWorkingTimeDerivedProjectionsModuleTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appliesIncludeFlagsOnModulePath() {
|
||||||
|
DriverWorkingTimeProcessingCore core = org.mockito.Mockito.mock(DriverWorkingTimeProcessingCore.class);
|
||||||
|
DriverWorkingTimeDerivedProjectionsModule module = new DriverWorkingTimeDerivedProjectionsModule(core);
|
||||||
|
|
||||||
|
DriverWorkingTimeActivityInterval activityInterval = new DriverWorkingTimeActivityInterval(
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
"ACT-1",
|
||||||
|
"WORK",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"DRIVER_CARD",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z").toEpochSecond(),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z").toEpochSecond(),
|
||||||
|
3600L,
|
||||||
|
List.of("ACT-1"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"RAW_INTERVAL"
|
||||||
|
);
|
||||||
|
DriverWorkingTimeActivityInterval drivingInterval = new DriverWorkingTimeActivityInterval(
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
"DRV-1",
|
||||||
|
"DRIVE",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"DRIVER_CARD",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z").toEpochSecond(),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z").toEpochSecond(),
|
||||||
|
3600L,
|
||||||
|
List.of("DRV-1"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"RAW_INTERVAL"
|
||||||
|
);
|
||||||
|
DriverWorkingTimeProcessingResultDto rawProjection = new DriverWorkingTimeProcessingResultDto(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"12:123",
|
||||||
|
"UNIFIED_EVENT_STREAM",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
List.of(activityInterval),
|
||||||
|
List.of(drivingInterval),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of("note")
|
||||||
|
);
|
||||||
|
when(core.process(any())).thenReturn(rawProjection);
|
||||||
|
|
||||||
|
UnifiedRuntimeProcessingApiRequest scope = new UnifiedRuntimeProcessingApiRequest(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
List.of(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Set.of(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
Set.of(),
|
||||||
|
false,
|
||||||
|
Set.of(),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
UnifiedRuntimeProcessingRequest runtimeRequest = scope.toRuntimeRequest();
|
||||||
|
DriverWorkingTimeProcessingInput processingInput = new DriverWorkingTimeProcessingInput(
|
||||||
|
null,
|
||||||
|
"12:123",
|
||||||
|
"UNIFIED_EVENT_STREAM",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
3,
|
||||||
|
720,
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
DriverWorkingTimePreparedInput preparedInput = new DriverWorkingTimePreparedInput(
|
||||||
|
"12:123",
|
||||||
|
new DriverWorkingTimeDriverPartition(
|
||||||
|
"12:123",
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
null,
|
||||||
|
List.of(),
|
||||||
|
null,
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
),
|
||||||
|
processingInput
|
||||||
|
);
|
||||||
|
UnifiedRuntimeEventBundle bundle = new UnifiedRuntimeEventBundle(
|
||||||
|
runtimeRequest,
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
RuntimeProcessingModuleContext context = new RuntimeProcessingModuleContext(
|
||||||
|
new RuntimeProcessingExecutionApiRequest("driver-working-time-v1", scope, null, List.of(), Map.of()),
|
||||||
|
List.of(),
|
||||||
|
Map.of("runtimeScopeApiRequest", scope),
|
||||||
|
Map.of(
|
||||||
|
DriverWorkingTimeModuleKeys.RUNTIME_EVENT_ASSEMBLY,
|
||||||
|
new RuntimeProcessingModuleResult(
|
||||||
|
DriverWorkingTimeModuleKeys.RUNTIME_EVENT_ASSEMBLY,
|
||||||
|
RuntimeProcessingModuleStatus.SUCCESS,
|
||||||
|
bundle,
|
||||||
|
Map.of(),
|
||||||
|
List.of()
|
||||||
|
),
|
||||||
|
DriverWorkingTimeModuleKeys.SUPPORT_EVIDENCE_NORMALIZATION,
|
||||||
|
new RuntimeProcessingModuleResult(
|
||||||
|
DriverWorkingTimeModuleKeys.SUPPORT_EVIDENCE_NORMALIZATION,
|
||||||
|
RuntimeProcessingModuleStatus.SUCCESS,
|
||||||
|
Map.of("12:123", preparedInput),
|
||||||
|
Map.of(),
|
||||||
|
List.of()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
RuntimeProcessingModuleResult result = module.execute(context);
|
||||||
|
UnifiedRuntimeDriverWorkingTimeScopeResultDto scopeResult =
|
||||||
|
(UnifiedRuntimeDriverWorkingTimeScopeResultDto) result.output();
|
||||||
|
UnifiedRuntimeDerivedProjectionResultDto driverResult = scopeResult.driverResults().get("12:123");
|
||||||
|
|
||||||
|
assertThat(driverResult.projection().activityIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(driverResult.projection().drivingIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(driverResult.projection().activityIntervals()).isEmpty();
|
||||||
|
assertThat(driverResult.projection().drivingIntervals()).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,8 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
||||||
"minimumRestPeriodMinutes",
|
"minimumRestPeriodMinutes",
|
||||||
"attachVehicleOnlyEvents",
|
"attachVehicleOnlyEvents",
|
||||||
"vehicleEvidencePaddingMinutes",
|
"vehicleEvidencePaddingMinutes",
|
||||||
|
"includeActivityIntervals",
|
||||||
|
"includeDrivingIntervals",
|
||||||
"includePartitionDebug"
|
"includePartitionDebug"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +73,9 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
||||||
true,
|
true,
|
||||||
15,
|
15,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
RuntimeEventProcessingApiRequest request = new RuntimeEventProcessingApiRequest(
|
RuntimeEventProcessingApiRequest request = new RuntimeEventProcessingApiRequest(
|
||||||
|
|
@ -93,6 +98,8 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
||||||
"minimumRestPeriodMinutes", "600",
|
"minimumRestPeriodMinutes", "600",
|
||||||
"vehicleEvidencePaddingMinutes", 20,
|
"vehicleEvidencePaddingMinutes", 20,
|
||||||
"attachVehicleOnlyEvents", true,
|
"attachVehicleOnlyEvents", true,
|
||||||
|
"includeActivityIntervals", true,
|
||||||
|
"includeDrivingIntervals", true,
|
||||||
"includePartitionDebug", true
|
"includePartitionDebug", true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -116,7 +123,8 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-31T23:59:59Z"),
|
OffsetDateTime.parse("2026-05-31T23:59:59Z"),
|
||||||
true,
|
true,
|
||||||
15
|
15,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
UnifiedRuntimeDerivedProjectionResultDto driverResult = new UnifiedRuntimeDerivedProjectionResultDto(
|
UnifiedRuntimeDerivedProjectionResultDto driverResult = new UnifiedRuntimeDerivedProjectionResultDto(
|
||||||
processedRequest,
|
processedRequest,
|
||||||
|
|
@ -158,6 +166,8 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
||||||
assertThat(delegated.minimumRestPeriodMinutes()).isEqualTo(600);
|
assertThat(delegated.minimumRestPeriodMinutes()).isEqualTo(600);
|
||||||
assertThat(delegated.vehicleExpansionPaddingMinutes()).isEqualTo(20);
|
assertThat(delegated.vehicleExpansionPaddingMinutes()).isEqualTo(20);
|
||||||
assertThat(delegated.expandVehicleEvents()).isTrue();
|
assertThat(delegated.expandVehicleEvents()).isTrue();
|
||||||
|
assertThat(delegated.includeActivityIntervals()).isTrue();
|
||||||
|
assertThat(delegated.includeDrivingIntervals()).isTrue();
|
||||||
assertThat(debugCaptor.getValue()).isTrue();
|
assertThat(debugCaptor.getValue()).isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,11 @@ class RuntimeMixedSourceEvidenceValidationServiceTest {
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
true,
|
true,
|
||||||
15,
|
15,
|
||||||
|
null,
|
||||||
3,
|
3,
|
||||||
720
|
720,
|
||||||
|
null,
|
||||||
|
null
|
||||||
),
|
),
|
||||||
new RuntimeEventPartitioningApiRequest(
|
new RuntimeEventPartitioningApiRequest(
|
||||||
RuntimeEventPartitioningStrategy.DRIVER,
|
RuntimeEventPartitioningStrategy.DRIVER,
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,8 @@ class UnifiedDriverEventsRequestTest {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
|
false
|
||||||
)).isInstanceOf(IllegalArgumentException.class)
|
)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("At least one driver or vehicle selector");
|
.hasMessageContaining("At least one driver or vehicle selector");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
assertThat(request.eventBackend()).isEqualTo(UnifiedRuntimeEventBackend.SOURCE_DB);
|
assertThat(request.eventBackend()).isEqualTo(UnifiedRuntimeEventBackend.SOURCE_DB);
|
||||||
assertThat(request.expandVehicleEvents()).isTrue();
|
assertThat(request.expandVehicleEvents()).isTrue();
|
||||||
assertThat(request.vehicleOccurredFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
assertThat(request.vehicleOccurredFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
||||||
|
assertThat(request.includeIntersectingIntervals()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -92,7 +93,8 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
true,
|
true,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(driverCardOnlyRequest.tachographSourceKinds()).containsExactly(UnifiedTachographSourceKind.DRIVER_CARD);
|
assertThat(driverCardOnlyRequest.tachographSourceKinds()).containsExactly(UnifiedTachographSourceKind.DRIVER_CARD);
|
||||||
|
|
@ -195,7 +197,8 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
true,
|
true,
|
||||||
10
|
10,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(request.driverKey()).isNull();
|
assertThat(request.driverKey()).isNull();
|
||||||
|
|
@ -224,7 +227,8 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
true,
|
true,
|
||||||
10
|
10,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(request.includeAllDrivers()).isTrue();
|
assertThat(request.includeAllDrivers()).isTrue();
|
||||||
|
|
@ -254,7 +258,8 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
)).isInstanceOf(IllegalArgumentException.class)
|
)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("Use either compositeSessionId");
|
.hasMessageContaining("Use either compositeSessionId");
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +286,8 @@ class UnifiedRuntimeProcessingRequestTest {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
)).isInstanceOf(IllegalArgumentException.class)
|
)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("At least one driver selector");
|
.hasMessageContaining("At least one driver selector");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ class UnifiedVehicleEventsRequestTest {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
|
false
|
||||||
)).isInstanceOf(IllegalArgumentException.class)
|
)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("At least one vehicle selector");
|
.hasMessageContaining("At least one vehicle selector");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ class EventHubRuntimeEventLoaderTest {
|
||||||
assertThat(driverRequest.occurredFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
assertThat(driverRequest.occurredFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
||||||
assertThat(driverRequest.occurredTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
assertThat(driverRequest.occurredTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
||||||
});
|
});
|
||||||
|
assertThat(driverSource.requests).extracting(UnifiedDriverEventsRequest::includeIntersectingIntervals)
|
||||||
|
.containsExactly(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -121,6 +123,8 @@ class EventHubRuntimeEventLoaderTest {
|
||||||
assertThat(vehicleRequest.occurredFrom()).isEqualTo(OffsetDateTime.parse("2026-04-30T23:45:00Z"));
|
assertThat(vehicleRequest.occurredFrom()).isEqualTo(OffsetDateTime.parse("2026-04-30T23:45:00Z"));
|
||||||
assertThat(vehicleRequest.occurredTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:15:00Z"));
|
assertThat(vehicleRequest.occurredTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:15:00Z"));
|
||||||
});
|
});
|
||||||
|
assertThat(vehicleSource.requests).extracting(UnifiedVehicleEventsRequest::includeIntersectingIntervals)
|
||||||
|
.containsExactly(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class CapturingDriverSource implements UnifiedDriverEventSource {
|
private static final class CapturingDriverSource implements UnifiedDriverEventSource {
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,49 @@ class TachographFileSessionRuntimeEventLoaderTest {
|
||||||
assertThat(loader.loadVehicleEvents(request, new UnifiedDiscoveredVehicleRef("VIN-1", "VIN-1", "12", "REG-1"))).hasSize(5);
|
assertThat(loader.loadVehicleEvents(request, new UnifiedDiscoveredVehicleRef("VIN-1", "VIN-1", "12", "REG-1"))).hasSize(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void keepsCompleteIntersectingIntervalsWhenRequestStartsInsideInterval() {
|
||||||
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
InMemoryTachographCompositeSessionRepository compositeRepository = new InMemoryTachographCompositeSessionRepository();
|
||||||
|
IntervalBackedDriverTimelineEventBuilder eventBuilder = new IntervalBackedDriverTimelineEventBuilder(
|
||||||
|
new DriverTimelineBuilder(),
|
||||||
|
new DriverKeyFactory(),
|
||||||
|
new VehicleKeyFactory(),
|
||||||
|
new EventDetailsFactory(new ObjectMapper())
|
||||||
|
);
|
||||||
|
TachographFileSessionRuntimeEventLoader loader = new TachographFileSessionRuntimeEventLoader(
|
||||||
|
new UnifiedDriverEventSourceService(List.of(new TachographFileSessionUnifiedDriverEventSource(repository, eventBuilder))),
|
||||||
|
new UnifiedVehicleEventSourceService(List.of(new TachographFileSessionUnifiedVehicleEventSource(repository, eventBuilder))),
|
||||||
|
compositeRepository,
|
||||||
|
new EventAcquisitionRecordKeyService(),
|
||||||
|
new EventHubEventSorter()
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverExtractionSession driver = driver();
|
||||||
|
TachographFileSession session = session(driver);
|
||||||
|
repository.save(session);
|
||||||
|
|
||||||
|
UnifiedRuntimeProcessingRequest request = UnifiedRuntimeProcessingRequest.forTachographFileSession(
|
||||||
|
session.sessionId(),
|
||||||
|
driver.driverKey(),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:45:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:15:00Z"),
|
||||||
|
true,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(loader.loadDriverEvents(request))
|
||||||
|
.extracting(event -> event.occurredAt())
|
||||||
|
.containsExactly(
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:30:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:45:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ class UnifiedRuntimeDriverTimelineServiceTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
false,
|
false,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ class UnifiedRuntimeEventAssemblyServiceTest {
|
||||||
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
|
||||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
true,
|
true,
|
||||||
15
|
15,
|
||||||
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue