Skip to content

Commit 7319ecd

Browse files
committed
#178: handle edge case where project was mapped but no work exist for that day but is present in heimat
1 parent 27235ed commit 7319ecd

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

src/main/java/de/doubleslash/keeptime/controller/HeimatController.java

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public List<Mapping> getTableRows(final LocalDate currentReportDate, final List<
6161
.filter(Project::isWork)
6262
.collect(Collectors.toCollection(() -> new TreeSet<>(
6363
Comparator.comparing(Project::getIndex))));
64+
final Map<Long, List<HeimatTime>> taskIdToHeimatTimesMap = heimatTimes.stream()
65+
.collect(Collectors.groupingBy(HeimatTime::taskId));
6466

6567
for (final Project project : workedProjectsSet) {
6668
String heimatNotes = "";
@@ -78,19 +80,15 @@ public List<Mapping> getTableRows(final LocalDate currentReportDate, final List<
7880
.filter(mapping -> mapping.heimatTaskId == optHeimatMapping.get()
7981
.getExternalTaskId())
8082
.findAny();
81-
optionalAlreadyBookedTimes = heimatTimes.stream()
82-
.filter(heimatTime -> optHeimatMapping.stream()
83-
.anyMatch(
84-
hm -> heimatTime.taskId()
85-
== hm.getExternalTaskId()))
86-
.toList();
83+
84+
final List<HeimatTime> heimatTimesForTaskId = taskIdToHeimatTimesMap.get(
85+
optHeimatMapping.get().getExternalTaskId());
86+
if(heimatTimesForTaskId != null) {
87+
optionalAlreadyBookedTimes = heimatTimesForTaskId;
88+
}
8789
if (!optionalAlreadyBookedTimes.isEmpty()) {
88-
heimatNotes = optionalAlreadyBookedTimes.stream()
89-
.map(HeimatTime::note)
90-
.collect(Collectors.joining(". "));
91-
heimatTimeSeconds = optionalAlreadyBookedTimes.stream()
92-
.reduce(0L, (subtotal, element) -> subtotal
93-
+ element.durationInMinutes() * 60L, Long::sum);
90+
heimatNotes = addHeimatNotes(optionalAlreadyBookedTimes);
91+
heimatTimeSeconds = addHeimatTimes(optionalAlreadyBookedTimes);
9492
}
9593
}
9694
final List<Work> onlyCurrentProjectWork = currentWorkItems.stream()
@@ -135,6 +133,7 @@ public List<Mapping> getTableRows(final LocalDate currentReportDate, final List<
135133
list.add(mapping);
136134
}
137135
}
136+
138137
final List<Long> mappedIds = mappedProjects.stream().map(ExternalProjectMapping::getExternalTaskId).toList();
139138
final Map<Long, List<HeimatTime>> notMappedExistingTimes = heimatTimes.stream()
140139
.filter(ht -> !mappedIds.contains(
@@ -155,9 +154,53 @@ public List<Mapping> getTableRows(final LocalDate currentReportDate, final List<
155154
new ArrayList<>(0), heimatNotes, "", heimatTimeSeconds, 0);
156155
list.add(mapping);
157156
});
157+
158+
taskIdToHeimatTimesMap.forEach((id,times) -> {
159+
final Optional<ExternalProjectMapping> mapping = mappedProjects.stream()
160+
.filter(mp -> mp.getExternalTaskId()
161+
== id)
162+
.findAny();
163+
if (mapping.isEmpty())
164+
return;
165+
final ExternalProjectMapping externalProjectMapping = mapping.get();
166+
final Optional<Project> optionalProject = workedProjectsSet.stream()
167+
.filter(wp -> wp.getId()
168+
== externalProjectMapping.getProject()
169+
.getId())
170+
.findAny();
171+
if (optionalProject.isPresent()) {
172+
return;
173+
}
174+
String heimatNotes = addHeimatNotes(times);
175+
long heimatTimeSeconds = addHeimatTimes(times);
176+
177+
final Mapping mapping2 = new Mapping(id, true,
178+
"Present in HEIMAT but not KeepTime\n\n" + externalProjectMapping.getExternalTaskName() + "\n"
179+
+ externalProjectMapping.getExternalProjectName(), times,
180+
mappedProjects.stream().filter(mp->mp.getExternalTaskId() == id).map(
181+
ExternalProjectMapping::getProject).toList(), heimatNotes, "", heimatTimeSeconds, 0);
182+
list.add(mapping2);
183+
});
184+
158185
return list;
159186
}
160187

188+
private static long addHeimatTimes(final List<HeimatTime> optionalAlreadyBookedTimes) {
189+
long heimatTimeSeconds;
190+
heimatTimeSeconds = optionalAlreadyBookedTimes.stream()
191+
.reduce(0L, (subtotal, element) -> subtotal
192+
+ element.durationInMinutes() * 60L, Long::sum);
193+
return heimatTimeSeconds;
194+
}
195+
196+
private static String addHeimatNotes(final List<HeimatTime> optionalAlreadyBookedTimes) {
197+
String heimatNotes;
198+
heimatNotes = optionalAlreadyBookedTimes.stream()
199+
.map(HeimatTime::note)
200+
.collect(Collectors.joining(". "));
201+
return heimatNotes;
202+
}
203+
161204
public List<HeimatErrors> saveDay(final List<UserMapping> items, LocalDate date) {
162205
List<HeimatErrors> errors = new ArrayList<>();
163206

@@ -206,7 +249,7 @@ public List<HeimatTask> getTasks(final LocalDate forDate) {
206249
return new ArrayList<>(uniqueMap.values());
207250
}
208251

209-
public record UserMapping(Mapping mapping, boolean shouldSync, String userNotes, int userMinutes){ }
252+
public record UserMapping(Mapping mapping, boolean shouldSync, String userNotes, int userMinutes) {}
210253

211254
public record Mapping(long heimatTaskId, boolean canBeSynced, String syncMessage, List<HeimatTime> existingTimes,
212255
List<Project> projects, String heimatNotes, String keeptimeNotes, long heimatSeconds,

src/test/java/de/doubleslash/keeptime/controller/HeimatControllerTest.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.hamcrest.Matchers;
1414
import org.junit.jupiter.api.BeforeEach;
1515
import org.junit.jupiter.api.Test;
16-
import org.mockito.Mock;
1716
import org.mockito.Mockito;
1817

1918
import java.time.LocalDate;
@@ -52,14 +51,14 @@ public void beforeEach() {
5251
mockedHeimatSettings = Mockito.mock(HeimatSettings.class);
5352
mockedHeimatAPI = Mockito.mock(HeimatAPI.class);
5453
mockedExternalMappingsRepository = Mockito.mock(ExternalProjectsMappingsRepository.class);
55-
heimatController = new HeimatController(mockedHeimatSettings,mockedHeimatAPI, mockedExternalMappingsRepository,
54+
heimatController = new HeimatController(mockedHeimatSettings, mockedHeimatAPI, mockedExternalMappingsRepository,
5655
new Controller(null, null, null, null));
5756

5857
when(mockedExternalMappingsRepository.findByExternalSystemId(ExternalSystem.Heimat)).thenReturn(externalMappings);
5958

6059
availableTasks.add(
6160
new HeimatTask(project1To1Mapping.getExternalTaskId(), project1To1Mapping.getExternalTaskName(),
62-
project1To1Mapping.getExternalProjectName(),"PROJECT", false, "", false, false));
61+
project1To1Mapping.getExternalProjectName(), "PROJECT", false, "", false, false));
6362
when(mockedHeimatAPI.getMyTasks(now.toLocalDate())).thenReturn(availableTasks);
6463
}
6564

@@ -190,7 +189,39 @@ void shouldShowHeimatTimeWhenProjectIsNotMappedInKeeptime() {
190189
}
191190

192191
@Test
193-
void shouldGenerateLinkForDay(){
192+
void shouldShowHeimatTimeWhenProjectIsMappedInKeeptimeButNoWorkAtThatDay() {
193+
// ARRANGE
194+
final HeimatTime existingTime1 = new HeimatTime(project1To1Mapping.getExternalTaskId(), now.toLocalDate(), null,
195+
null, 60, "Existing note 1", 12);
196+
// there could be more than 1 time for a task in heimat (e.g. when manually saved with start,end feature)
197+
final HeimatTime existingTime2 = new HeimatTime(project1To1Mapping.getExternalTaskId(), now.toLocalDate(), null,
198+
null, 30, "Existing note 2", 13);
199+
when(mockedHeimatAPI.getMyTimes(now.toLocalDate())).thenReturn(Arrays.asList(existingTime1, existingTime2));
200+
externalMappings.add(project1To1Mapping);
201+
externalMappings.add(project2To1Mapping);
202+
203+
// ACT
204+
final List<HeimatController.Mapping> tableRows = heimatController.getTableRows(now.toLocalDate(), workItems);
205+
final HeimatController.Mapping mapping = tableRows.get(0);
206+
207+
// ASSERT
208+
assertAll(
209+
() -> assertThat(tableRows.size(), Matchers.is(1)),
210+
() -> assertTrue(mapping.canBeSynced()),
211+
() -> assertThat(mapping.syncMessage(), Matchers.containsString("Present in HEIMAT but not KeepTime")),
212+
() -> assertThat(mapping.syncMessage(), Matchers.containsString(project1To1Mapping.getExternalTaskName())),
213+
() -> assertThat(mapping.keeptimeSeconds(), Matchers.is(0L)),
214+
() -> assertThat(mapping.keeptimeNotes(), Matchers.is("")),
215+
() -> assertThat(mapping.projects(), Matchers.containsInAnyOrder(workProject1, workProject2)),
216+
() -> assertThat(mapping.heimatNotes(), Matchers.is("Existing note 1. Existing note 2")),
217+
() -> assertThat(mapping.heimatSeconds(), Matchers.is((60 + 30) * 60L)),
218+
() -> assertThat(mapping.existingTimes(), Matchers.containsInAnyOrder(existingTime1, existingTime2))
219+
//
220+
);
221+
}
222+
223+
@Test
224+
void shouldGenerateLinkForDay() {
194225
when(mockedHeimatSettings.getHeimatUrl()).thenReturn("https://doubleslash.de");
195226
final String urlForDay = heimatController.getUrlForDay(LocalDate.of(1999, 4, 2));
196227
assertThat(urlForDay, Matchers.is("https://doubleslash.de/core/heimat/time/main/day/1999/4/2"));

0 commit comments

Comments
 (0)