diff --git a/.classpath b/.classpath index 6d924d7..bfdccfd 100644 --- a/.classpath +++ b/.classpath @@ -6,6 +6,13 @@ + + + + + + + diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component index 0d9c0d3..9542a85 100644 --- a/.settings/org.eclipse.wst.common.component +++ b/.settings/org.eclipse.wst.common.component @@ -1,37 +1,48 @@ - + + - + + - + + - + + - + + - + + - + + - + + - + + + - + + diff --git a/build.gradle b/build.gradle index c7399a4..6aeb5ce 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'de.jottyfan' -version = '1.2.3' +version = '1.2.4' sourceCompatibility = '11' ext['spring-framework.version'] = '5.3.18' @@ -51,6 +51,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' implementation 'de.jottyfan:timetrackjooq:0.1.1' diff --git a/src/main/java/de/jottyfan/timetrack/help/LocalDateHelper.java b/src/main/java/de/jottyfan/timetrack/help/LocalDateHelper.java new file mode 100644 index 0000000..028e428 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/help/LocalDateHelper.java @@ -0,0 +1,28 @@ +package de.jottyfan.timetrack.help; + +import java.time.DayOfWeek; +import java.time.LocalDate; + +/** + * + * @author henkej + * + */ +public class LocalDateHelper { + + /** + * find the day of the week in relation to day + * + * @param day the day to start the search from + * @param dayOfWeek the day of the week that is wanted + * @param backwards if true, search before day, if false, search after day + * @return the found date + */ + public static final LocalDate seekDay(LocalDate day, DayOfWeek dayOfWeek, boolean backwards) { + LocalDate found = day; + while (!found.getDayOfWeek().equals(dayOfWeek)) { + found = backwards ? found.minusDays(1) : found.plusDays(1); + } + return found; + } +} diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/DoneBean.java b/src/main/java/de/jottyfan/timetrack/spring/done/DoneBean.java index 51d40cd..09cbae4 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/DoneBean.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/DoneBean.java @@ -76,7 +76,8 @@ public class DoneBean implements Serializable, Comparable { public final String toJson() { StringBuilder buf = new StringBuilder("{"); - buf.append("\"from\": ").append(nullable(timeFrom, "HH:mm")); + buf.append("\"daySlot\": ").append(day == null ? "null" : day.getDayOfWeek().getValue()); + buf.append(", \"from\": ").append(nullable(timeFrom, "HH:mm")); buf.append(", \"until\": ").append(nullable(timeUntil, "HH:mm")); buf.append(", \"billing\": ").append(nullable(billing, null)); buf.append("}"); diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/DoneController.java b/src/main/java/de/jottyfan/timetrack/spring/done/DoneController.java index 61879ac..75e7584 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/DoneController.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/DoneController.java @@ -45,11 +45,13 @@ public class DoneController { Duration maxWorkTime = Duration.ofHours(8); // TODO: to the configuration file LocalDate day = doneModel.getDay(); List list = doneService.getList(day, username); + List week = doneService.getWeek(day, username); SummaryBean bean = new SummaryBean(list, day, maxWorkTime); + SummaryBean weekBean = new SummaryBean(week, day, maxWorkTime); model.addAttribute("doneList", list); model.addAttribute("doneModel", doneModel); model.addAttribute("sum", bean); - model.addAttribute("schedule", bean.toJson()); // TODO: add the other days of that week + model.addAttribute("schedule", weekBean.toJson()); model.addAttribute("projectList", doneService.getProjects(false)); model.addAttribute("moduleList", doneService.getModules(false)); model.addAttribute("jobList", doneService.getJobs(false)); diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/IDoneService.java b/src/main/java/de/jottyfan/timetrack/spring/done/IDoneService.java index ea62db0..86c39f6 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/IDoneService.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/IDoneService.java @@ -5,7 +5,6 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import de.jottyfan.timetrack.db.done.tables.records.TProjectRecord; import de.jottyfan.timetrack.db.done.tables.records.VBillingRecord; import de.jottyfan.timetrack.db.done.tables.records.VJobRecord; import de.jottyfan.timetrack.db.done.tables.records.VModuleRecord; @@ -18,20 +17,13 @@ import de.jottyfan.timetrack.db.done.tables.records.VProjectRecord; */ public interface IDoneService { public List getList(LocalDate day, String username); - + public List getWeek(LocalDate day, String username); public DoneBean getBean(Integer id); - public String getCurrentUser(HttpServletRequest request); - public List getProjects(boolean includeNull); - public List getModules(boolean includeNull); - public List getJobs(boolean includeNull); - public List getBillings(boolean includeNull); - public Integer doUpsert(DoneBean bean, String username); - public Integer doDelete(Integer id); } diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/SummaryBean.java b/src/main/java/de/jottyfan/timetrack/spring/done/SummaryBean.java index f7e8fda..0ab1011 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/SummaryBean.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/SummaryBean.java @@ -4,7 +4,6 @@ import java.io.Serializable; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.List; /** @@ -19,17 +18,23 @@ public class SummaryBean implements Serializable { private final LocalDate day; private final List list; + /** + * @param list the list of beans + * @param day the day that this selection refers to; might be the day + * of the week for weekly sets + * @param maxDayWorktime the maximum work time of a day + */ public SummaryBean(List list, LocalDate day, Duration maxDayWorktime) { this.list = list; this.day = day; this.maxDayWorktime = maxDayWorktime; } - + public String toJson() { StringBuilder buf = new StringBuilder("{"); - buf.append("\"maxDayWorktime\": \"").append(String.format("%02d:%02d", maxDayWorktime.toHoursPart(), maxDayWorktime.toMinutesPart())); - buf.append("\", \"daySlot\": ").append(day == null ? "null" : day.getDayOfWeek().getValue()); - buf.append(", \"schedule\": ["); + buf.append("\"maxDayWorktime\": \"") + .append(String.format("%02d:%02d", maxDayWorktime.toHoursPart(), maxDayWorktime.toMinutesPart())); + buf.append("\", \"schedule\": ["); boolean first = true; for (DoneBean bean : list) { if (!first) { @@ -67,7 +72,7 @@ public class SummaryBean implements Serializable { int minutesPercent = Float.valueOf((5f / 3f) * minutes).intValue(); return String.format("%d,%d h", duration.toHoursPart(), minutesPercent); } - + /** * @return the start */ diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneGateway.java b/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneGateway.java index f491989..ae3f1d8 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneGateway.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneGateway.java @@ -8,6 +8,7 @@ import static de.jottyfan.timetrack.db.done.Tables.V_PROJECT; import static de.jottyfan.timetrack.db.profile.Tables.T_LOGIN; import java.sql.SQLException; +import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; @@ -34,6 +35,7 @@ import de.jottyfan.timetrack.db.done.tables.records.VJobRecord; import de.jottyfan.timetrack.db.done.tables.records.VModuleRecord; import de.jottyfan.timetrack.db.done.tables.records.VProjectRecord; import de.jottyfan.timetrack.db.profile.tables.records.TLoginRecord; +import de.jottyfan.timetrack.help.LocalDateHelper; import de.jottyfan.timetrack.spring.done.DoneBean; /** @@ -216,19 +218,19 @@ public class DoneGateway { } /** - * get list of entries of day + * get list of entries of interval * - * @param day the day + * @param start the lower limit + * @param end the upper limit + * @param userId the id of the user * * @return a list (an empty one at least) * @throws SQLException * @throws ClassNotFoundException * @throws DataAccessException */ - public List getAllOfDay(LocalDate day, Integer userId) + private List getAllOfInterval(LocalDateTime start, LocalDateTime end, Integer userId) throws DataAccessException, ClassNotFoundException, SQLException { - LocalDateTime dayStart = day.atStartOfDay(); - LocalDateTime dayEnd = day.atTime(23, 59, 59); SelectConditionStep> sql = getJooq() // @formatter:off .select(T_DONE.PK, @@ -239,8 +241,8 @@ public class DoneGateway { T_DONE.FK_JOB, T_DONE.FK_BILLING) .from(T_DONE) - .where(T_DONE.TIME_FROM.between(dayStart, dayEnd).or(T_DONE.TIME_FROM.isNull())) - .and(T_DONE.TIME_UNTIL.between(dayStart, dayEnd).or(T_DONE.TIME_UNTIL.isNull())) + .where(T_DONE.TIME_FROM.between(start, end).or(T_DONE.TIME_FROM.isNull())) + .and(T_DONE.TIME_UNTIL.between(start, end).or(T_DONE.TIME_UNTIL.isNull())) .and(T_DONE.FK_LOGIN.eq(userId == null ? -999999 : userId)); // @formatter:on LOGGER.debug("{}", sql.toString()); @@ -265,6 +267,40 @@ public class DoneGateway { return list; } + /** + * get list of entries of day + * + * @param day the day + * + * @return a list (an empty one at least) + * @throws SQLException + * @throws ClassNotFoundException + * @throws DataAccessException + */ + public List getAllOfDay(LocalDate day, Integer userId) + throws DataAccessException, ClassNotFoundException, SQLException { + LocalDateTime dayStart = day.atStartOfDay(); + LocalDateTime dayEnd = day.atTime(23, 59, 59); + return getAllOfInterval(dayStart, dayEnd, userId); + } + + /** + * get all entries of the week where day belongs to + * + * @param day the day that the week refers to + * @param userId the id of the user + * @return a list of done beans; an empty one at least + * @throws SQLException + * @throws ClassNotFoundException + * @throws DataAccessException + */ + public List getAllOfWeek(LocalDate day, Integer userId) + throws DataAccessException, ClassNotFoundException, SQLException { + LocalDate sunday = LocalDateHelper.seekDay(day, DayOfWeek.SUNDAY, true); + LocalDate saturday = LocalDateHelper.seekDay(day, DayOfWeek.SATURDAY, false); + return getAllOfInterval(sunday.atStartOfDay(), saturday.atTime(23, 59, 59), userId); + } + /** * delete an entry from the database * diff --git a/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneService.java b/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneService.java index 07ae0b8..f4c5cb9 100644 --- a/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneService.java +++ b/src/main/java/de/jottyfan/timetrack/spring/done/impl/DoneService.java @@ -57,6 +57,22 @@ public class DoneService implements IDoneService { } } + @Override + public List getWeek(LocalDate day, String username) { + try { + DoneGateway gw = new DoneGateway(dsl); + Integer userId = gw.getUserId(username); + if (userId == null) { + LOGGER.warn("userId of user {} is null", username); + } + return gw.getAllOfWeek(day, userId); + } catch (Exception e) { + LOGGER.error(e); + return new ArrayList<>(); + } + } + + @Override public DoneBean getBean(Integer id) { try { diff --git a/src/main/resources/static/js/schedule.js b/src/main/resources/static/js/schedule.js index 4aad292..6c1ebdf 100644 --- a/src/main/resources/static/js/schedule.js +++ b/src/main/resources/static/js/schedule.js @@ -17,16 +17,32 @@ class Schedule { drawBackgroundDay = function(ctx, width, height, offsetx) { var hourHeight = parseInt(height / 24); + var fs = ["#eeeeee", "#bbbbbb", "#eeeeee", "#bbbbbb", "#eeeeee", "#bbbbbb", + "#faf0fa", "#d9cbd9", "#faf0fa", "#9acbd9", "#cdf0fa", "#9acbd9", + "#cdf0fa", "#9acbd9", "#cdf0fa", "#d9cbd9", "#faf0fa", "#d9cbd9", + "#faf0fa", "#d9cbd9", "#eeeeee", "#bbbbbb", "#eeeeee", "#bbbbbb"]; for (var i = 0; i < 24; i++) { var x = parseInt(offsetx); var y = parseInt(i * hourHeight); var w = parseInt(width); var h = parseInt(hourHeight); - ctx.fillStyle = (i > 8 & i <= 17) ? (i % 2 ? "#cdf0fa" : "#9acbd9") : (i % 2 ? "#faf0fa" : "#d9cbd9"); + ctx.fillStyle = fs[i]; ctx.fillRect(x, y, w, h); } + ctx.lineWidth = 1; ctx.strokeStyle = "white"; ctx.strokeRect(offsetx, 0, width, height); + ctx.strokeStyle = "black"; + ctx.lineWidth = 2; + ctx.moveTo(parseInt(offsetx), parseInt(hourHeight * 6)); + ctx.lineTo(parseInt(offsetx + width), parseInt(hourHeight * 6)); + ctx.stroke(); + ctx.moveTo(parseInt(offsetx), parseInt(hourHeight * 12)); + ctx.lineTo(parseInt(offsetx + width), parseInt(hourHeight * 12)); + ctx.stroke(); + ctx.moveTo(parseInt(offsetx), parseInt(hourHeight * 18)); + ctx.lineTo(parseInt(offsetx + width), parseInt(hourHeight * 18)); + ctx.stroke(); } drawBackgroundWeek = function(ctx, width, height) { diff --git a/src/main/resources/templates/done/list.html b/src/main/resources/templates/done/list.html index 02a16c2..1e9284f 100644 --- a/src/main/resources/templates/done/list.html +++ b/src/main/resources/templates/done/list.html @@ -191,7 +191,6 @@ var ctx = $("#scheduleCanvas")[0].getContext("2d"); var currentDayRecords = JSON.parse('[(${schedule})]'); var scheduleRecords = currentDayRecords.schedule; - var daySlot = currentDayRecords.daySlot; for (var i = 0; i < scheduleRecords.length; i++) { var r = scheduleRecords[i]; var cssClass = r.billing; @@ -206,7 +205,7 @@ color = "#00aa00"; } /* daySlot 7 = sunday, but this should be slot 0 */ - schedule.drawSlot(ctx, daySlot > 6 ? 0 : daySlot, r.from, r.until, "black", color); + schedule.drawSlot(ctx, r.daySlot > 6 ? 0 : r.daySlot, r.from, r.until, "black", color); } var localeUrl = '[[@{/js/dataTables/de.json}]]'; $("#project_table").DataTable({ diff --git a/src/test/java/de/jottyfan/timetrack/help/TestLocalDateHelper.java b/src/test/java/de/jottyfan/timetrack/help/TestLocalDateHelper.java new file mode 100644 index 0000000..2e25e27 --- /dev/null +++ b/src/test/java/de/jottyfan/timetrack/help/TestLocalDateHelper.java @@ -0,0 +1,26 @@ +package de.jottyfan.timetrack.help; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.Test; + +/** + * + * @author henkej + * + */ +public class TestLocalDateHelper { + @Test + public void testFindWeekDays() { + assertEquals(20, LocalDateHelper.seekDay(LocalDate.of(2022, 2, 23), DayOfWeek.SUNDAY, true).getDayOfMonth()); + assertEquals(26, LocalDateHelper.seekDay(LocalDate.of(2022, 2, 23), DayOfWeek.SATURDAY, false).getDayOfMonth()); + assertEquals(13, LocalDateHelper.seekDay(LocalDate.of(2022, 2, 19), DayOfWeek.SUNDAY, true).getDayOfMonth()); + assertEquals(19, LocalDateHelper.seekDay(LocalDate.of(2022, 2, 19), DayOfWeek.SATURDAY, false).getDayOfMonth()); + assertEquals("26.12.2021", LocalDateHelper.seekDay(LocalDate.of(2022, 1, 1), DayOfWeek.SUNDAY, true).format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))); + assertEquals("02.01.2022", LocalDateHelper.seekDay(LocalDate.of(2021, 12, 31), DayOfWeek.SUNDAY, false).format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))); + } +}