Skip to content

Commit eabe6a6

Browse files
fix(MergeFeedsJob): Add state for headers written; preserve calendar dates.
1 parent 162fc02 commit eabe6a6

File tree

5 files changed

+78
-27
lines changed

5 files changed

+78
-27
lines changed

src/main/java/com/conveyal/datatools/manager/jobs/MergeFeedsJob.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,13 @@ private void determineMergeStrategy() {
432432
// in both active/future feeds and that have consistent signature.
433433
// These trips will be linked to the new service_ids.
434434
serviceIdsToCloneRenameAndExtend.addAll(
435-
getActiveServiceIds(this.sharedTripIdsWithConsistentSignature)
435+
feedMergeContext.active.getServiceIds(this.sharedTripIdsWithConsistentSignature)
436436
);
437437

438438
// Build the set of calendars to be shortened to the day before the future feed start date
439439
// from trips in the active feed but not in the future feed.
440440
serviceIdsFromActiveFeedToTerminateEarly.addAll(
441-
getActiveServiceIds(feedMergeContext.getActiveTripIdsNotInFutureFeed())
441+
feedMergeContext.active.getServiceIds(feedMergeContext.getActiveTripIdsNotInFutureFeed())
442442
);
443443

444444

@@ -450,24 +450,6 @@ private void determineMergeStrategy() {
450450
}
451451
}
452452

453-
/**
454-
* Obtains the service ids corresponding to the provided trip ids.
455-
*/
456-
private Set<String> getActiveServiceIds(Set<String> tripIds) {
457-
return tripIds.stream()
458-
.map(tripId -> feedMergeContext.active.feed.trips.get(tripId).service_id)
459-
.collect(Collectors.toSet());
460-
}
461-
462-
/**
463-
* Obtains the service ids corresponding to the provided trip ids.
464-
*/
465-
private Set<String> getFutureServiceIds(Set<String> tripIds) {
466-
return tripIds.stream()
467-
.map(tripId -> feedMergeContext.future.feed.trips.get(tripId).service_id)
468-
.collect(Collectors.toSet());
469-
}
470-
471453
/**
472454
* Compare stop times for the given tripId between the future and active feeds. The comparison will inform whether
473455
* trip and/or service IDs should be modified in the output merged feed.

src/main/java/com/conveyal/datatools/manager/jobs/feedmerge/CalendarDatesMergeLineContext.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ public boolean checkFieldsForMergeConflicts(Set<NewGTFSError> idErrors, FieldCon
2929
return checkCalendarDatesIds(fieldContext);
3030
}
3131

32+
@Override
33+
public void afterRowWrite() throws IOException {
34+
// If the current row is for a calendar service_id that is marked for cloning/renaming, clone the
35+
// values, change the ID, extend the start/end dates to the feed's full range, and write the
36+
// additional line to the file.
37+
addClonedServiceId();
38+
}
39+
3240
@Override
3341
public void startNewFeed(int feedIndex) throws IOException {
3442
super.startNewFeed(feedIndex);
@@ -37,6 +45,7 @@ public void startNewFeed(int feedIndex) throws IOException {
3745

3846
private boolean checkCalendarDatesIds(FieldContext fieldContext) throws IOException {
3947
boolean shouldSkipRecord = false;
48+
String key = getTableScopedValue(table, getIdScope(), keyValue);
4049
// Drop any calendar_dates.txt records from the existing feed for dates that are
4150
// not before the first date of the future feed.
4251
LocalDate date = getCsvDate("date");
@@ -45,11 +54,37 @@ private boolean checkCalendarDatesIds(FieldContext fieldContext) throws IOExcept
4554
"Skipping calendar_dates entry {} because it operates in the time span of future feed (i.e., after or on {}).",
4655
keyValue,
4756
futureFeedFirstDateForCalendarValidity);
48-
String key = getTableScopedValue(table, getIdScope(), keyValue);
4957
mergeFeedsResult.skippedIds.add(key);
5058
shouldSkipRecord = true;
5159
}
52-
// Track service ID because we want to avoid removing trips that may reference this
60+
61+
if (job.mergeType.equals(SERVICE_PERIOD)) {
62+
if (isHandlingActiveFeed()) {
63+
// Remove calendar entries that are no longer used.
64+
if (feedMergeContext.active.getServiceIdsToRemove().contains(keyValue)) {
65+
LOG.warn(
66+
"Skipping active calendar entry {} because it will become unused in the merged feed.",
67+
keyValue);
68+
mergeFeedsResult.skippedIds.add(key);
69+
shouldSkipRecord = true;
70+
}
71+
} else {
72+
// If handling the future feed, the MTC revised feed merge logic is as follows:
73+
// - Calendar entries from the future feed will be inserted as is in the merged feed.
74+
// so no additional processing needed here, unless the calendar entry is no longer used,
75+
// in that case we drop the calendar entry.
76+
if (feedMergeContext.future.getServiceIdsToRemove().contains(keyValue)) {
77+
LOG.warn(
78+
"Skipping future calendar entry {} because it will become unused in the merged feed.",
79+
keyValue);
80+
mergeFeedsResult.skippedIds.add(key);
81+
shouldSkipRecord = true;
82+
}
83+
}
84+
}
85+
86+
87+
// Track service ID because we want to avoid removing trips that may reference this
5388
// service_id when the service_id is used by calendar.txt records that operate in
5489
// the valid date range, i.e., before the future feed's first date.
5590
if (!shouldSkipRecord && fieldContext.nameEquals(SERVICE_ID)) mergeFeedsResult.serviceIds.add(fieldContext.getValueToWrite());
@@ -75,4 +110,29 @@ private LocalDate getFutureFeedFirstDateForCheckingCalendarValidity() {
75110
}
76111
return futureFeedFirstDate;
77112
}
78-
}
113+
114+
/**
115+
* Adds a cloned service id for trips with the same signature in both the active & future feeds.
116+
* The cloned service id spans from the start date in the active feed until the end date in the future feed.
117+
* @throws IOException
118+
*/
119+
public void addClonedServiceId() throws IOException {
120+
if (isHandlingFutureFeed() && job.mergeType.equals(SERVICE_PERIOD)) {
121+
String originalServiceId = keyValue;
122+
if (job.serviceIdsToCloneRenameAndExtend.contains(originalServiceId)) {
123+
String[] clonedValues = getOriginalRowValues().clone();
124+
String newServiceId = clonedValues[keyFieldIndex] = String.join(":", getIdScope(), originalServiceId);
125+
126+
referenceTracker.checkReferencesAndUniqueness(
127+
keyValue,
128+
getLineNumber(),
129+
table.fields[0],
130+
newServiceId,
131+
table,
132+
keyField,
133+
table.getOrderFieldName()
134+
);
135+
writeValuesToTable(clonedValues, true);
136+
}
137+
}
138+
}}

src/main/java/com/conveyal/datatools/manager/jobs/feedmerge/CalendarMergeLineContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Set;
1313
import java.util.zip.ZipOutputStream;
1414

15+
import static com.conveyal.datatools.manager.jobs.feedmerge.MergeFeedsType.SERVICE_PERIOD;
1516
import static com.conveyal.datatools.manager.utils.MergeFeedUtils.getTableScopedValue;
1617
import static com.conveyal.datatools.manager.utils.MergeFeedUtils.hasDuplicateError;
1718
import static com.conveyal.gtfs.loader.DateField.GTFS_DATE_FORMATTER;
@@ -141,10 +142,9 @@ private boolean checkCalendarIds(Set<NewGTFSError> idErrors, FieldContext fieldC
141142
* @throws IOException
142143
*/
143144
public void addClonedServiceId() throws IOException {
144-
if (isHandlingFutureFeed()) {
145+
if (isHandlingFutureFeed() && job.mergeType.equals(SERVICE_PERIOD)) {
145146
String originalServiceId = keyValue;
146147
if (job.serviceIdsToCloneRenameAndExtend.contains(originalServiceId)) {
147-
// FIXME: Do we need to worry about calendar_dates?
148148
String[] clonedValues = getOriginalRowValues().clone();
149149
String newServiceId = clonedValues[keyFieldIndex] = String.join(":", getIdScope(), originalServiceId);
150150
// Modify start date only (preserve the end date from the future calendar entry).

src/main/java/com/conveyal/datatools/manager/jobs/feedmerge/MergeLineContext.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class MergeLineContext {
7676
public FeedSource feedSource;
7777
public boolean skipFile;
7878
public int mergedLineNumber = 0;
79+
private boolean headersWritten = false;
7980

8081
public static MergeLineContext create(MergeFeedsJob job, Table table, ZipOutputStream out) throws IOException {
8182
switch (table.name) {
@@ -509,7 +510,7 @@ public void flushAndClose() throws IOException {
509510
out.closeEntry();
510511
}
511512

512-
public void writeHeaders() throws IOException {
513+
private void writeHeaders() throws IOException {
513514
// Create entry for zip file.
514515
ZipEntry tableEntry = new ZipEntry(table.name + ".txt");
515516
out.putNextEntry(tableEntry);
@@ -518,6 +519,8 @@ public void writeHeaders() throws IOException {
518519
.map(f -> f.name)
519520
.toArray(String[]::new);
520521
writeValuesToTable(headers, false);
522+
523+
headersWritten = true;
521524
}
522525

523526
/**
@@ -596,7 +599,7 @@ private void finishRowAndWriteToZip() throws IOException {
596599
}
597600

598601
// Finally, handle writing lines to zip entry.
599-
if (mergedLineNumber == 0) {
602+
if (mergedLineNumber == 0 && !headersWritten) {
600603
writeHeaders();
601604
}
602605

src/test/java/com/conveyal/datatools/manager/jobs/MergeFeedsJobTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ void mergeMTCShouldHandleMatchingTripIdsAndDropUnusedFutureCalendar() throws SQL
484484
3
485485
);
486486

487+
// The calendar_dates entry should be preserved, but remapped to a different id.
488+
assertThatSqlCountQueryYieldsExpectedCount(
489+
String.format("SELECT count(*) FROM %s.calendar_dates", mergedNamespace),
490+
1
491+
);
492+
487493
assertNoUnusedServiceIds(mergedNamespace);
488494
assertNoRefIntegrityErrors(mergedNamespace);
489495
}

0 commit comments

Comments
 (0)