diff --git a/build.gradle b/build.gradle index 7a6668c..2086520 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ apply plugin: 'eclipse' apply plugin: 'nu.studer.jooq' group = 'jottyfan' -version = '1.0.8' +version = '1.0.9' description = """timetrack""" diff --git a/src/main/java/de/jottyfan/timetrack/help/LocalDateConverter.java b/src/main/java/de/jottyfan/timetrack/help/LocalDateConverter.java new file mode 100644 index 0000000..a66b4eb --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/help/LocalDateConverter.java @@ -0,0 +1,31 @@ +package de.jottyfan.timetrack.help; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.convert.Converter; +import javax.faces.convert.ConverterException; +import javax.faces.convert.FacesConverter; + +/** + * + * @author jotty + * + */ +@FacesConverter("de.jottyfan.timetrack.help.LocalDateConverter") +public class LocalDateConverter implements Converter { + + protected final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd.MM.yyyy"); + + @Override + public LocalDate getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException { + return LocalDate.from(dtf.parse(value)); + } + + @Override + public String getAsString(FacesContext context, UIComponent component, LocalDate value) throws ConverterException { + return value.format(dtf); + } +} diff --git a/src/main/java/de/jottyfan/timetrack/help/Pages.java b/src/main/java/de/jottyfan/timetrack/help/Pages.java index 199308f..81a07c9 100644 --- a/src/main/java/de/jottyfan/timetrack/help/Pages.java +++ b/src/main/java/de/jottyfan/timetrack/help/Pages.java @@ -51,6 +51,10 @@ public enum Pages * done clear */ DONE_CLEAR("/pages/done/clear.jsf"), + /** + * done read + */ + DONE_READ("/pages/done/read.jsf"), /** * organize init */ diff --git a/src/main/java/de/jottyfan/timetrack/modules/done/DoneControl.java b/src/main/java/de/jottyfan/timetrack/modules/done/DoneControl.java index c39a727..2f2b226 100644 --- a/src/main/java/de/jottyfan/timetrack/modules/done/DoneControl.java +++ b/src/main/java/de/jottyfan/timetrack/modules/done/DoneControl.java @@ -57,6 +57,11 @@ public class DoneControl extends Navigation implements ControlInterface, Seriali return navigateTo(Pages.DONE_DELETE); } + public String toRead() { + boolean ready = model.prepareRead((JooqFacesContext) FacesContext.getCurrentInstance()); + return ready ? navigateTo(Pages.DONE_READ) : toStart(); + } + public String doUpdate() { boolean ready = model.update((JooqFacesContext) FacesContext.getCurrentInstance()); return ready ? toList() : toEdit(model.getBean()); @@ -71,6 +76,11 @@ public class DoneControl extends Navigation implements ControlInterface, Seriali boolean ready = model.insert((JooqFacesContext) FacesContext.getCurrentInstance()); return ready ? toList() : toAdd(); } + + public String doDownload() { + boolean ready = model.download((JooqFacesContext) FacesContext.getCurrentInstance()); + return ready ? "" : ""; + } public String getCurrentTimeAsString() { return new SimpleDateFormat("HH:mm:ss").format(getCurrentDate()); diff --git a/src/main/java/de/jottyfan/timetrack/modules/done/DoneGateway.java b/src/main/java/de/jottyfan/timetrack/modules/done/DoneGateway.java index 91f2cd5..82354d7 100644 --- a/src/main/java/de/jottyfan/timetrack/modules/done/DoneGateway.java +++ b/src/main/java/de/jottyfan/timetrack/modules/done/DoneGateway.java @@ -4,15 +4,17 @@ import static de.jottyfan.timetrack.db.done.Tables.T_DONE; import static de.jottyfan.timetrack.db.done.Tables.T_JOB; import static de.jottyfan.timetrack.db.done.Tables.T_MODULE; import static de.jottyfan.timetrack.db.done.Tables.T_PROJECT; +import static de.jottyfan.timetrack.db.done.Tables.V_HAMSTERSUMMARY; import static de.jottyfan.timetrack.db.done.Tables.V_TASKLIST; import static de.jottyfan.timetrack.db.done.Tables.V_TOTALOFDAY; +import static de.jottyfan.timetrack.db.profile.Tables.T_LOGIN; +import java.sql.Date; import java.sql.SQLException; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,9 +25,11 @@ import org.jooq.DSLContext; import org.jooq.DeleteConditionStep; import org.jooq.InsertValuesStep6; import org.jooq.Record; +import org.jooq.Record3; import org.jooq.Record4; import org.jooq.Record5; import org.jooq.SelectConditionStep; +import org.jooq.SelectJoinStep; import org.jooq.SelectWhereStep; import org.jooq.UpdateConditionStep; import org.jooq.exception.DataAccessException; @@ -51,7 +55,7 @@ public class DoneGateway extends JooqGateway { public DoneGateway(JooqFacesContext facesContext) { super(facesContext); String dailyMinutes = facesContext.getExternalContext().getInitParameter("requestedDailyMinutes"); - this.requested = dailyMinutes == null ? 480 : Integer.valueOf(dailyMinutes); // defaults to 8 hours/day + this.requested = dailyMinutes == null ? 480 : Integer.valueOf(dailyMinutes); // defaults to 8 hours/day } /** @@ -259,11 +263,11 @@ public class DoneGateway extends JooqGateway { * @param day * the day * @return the day summary if found, an empty map otherwise - * @throws SQLException - * @throws ClassNotFoundException - * @throws DataAccessException + * @throws SQLException + * @throws ClassNotFoundException + * @throws DataAccessException */ - public WholeDaySummaryBean getDaySummary(Date day) throws DataAccessException, ClassNotFoundException, SQLException { + public WholeDaySummaryBean getDaySummary(java.util.Date day) throws DataAccessException, ClassNotFoundException, SQLException { try (DSLContext jooq = getJooq()) { SelectConditionStep> sql = jooq // @formatter:off @@ -298,7 +302,7 @@ public class DoneGateway extends JooqGateway { * @throws ClassNotFoundException * @throws DataAccessException */ - public List getAllJobs(Date day) throws DataAccessException, ClassNotFoundException, SQLException { + public List getAllJobs(java.util.Date day) throws DataAccessException, ClassNotFoundException, SQLException { try (DSLContext jooq = getJooq()) { SelectConditionStep> sql = jooq // @formatter:off @@ -327,9 +331,9 @@ public class DoneGateway extends JooqGateway { * get json representation of all calendar events of user * * @return - * @throws SQLException - * @throws ClassNotFoundException - * @throws DataAccessException + * @throws SQLException + * @throws ClassNotFoundException + * @throws DataAccessException */ public String getAllCalendarEvents() throws DataAccessException, ClassNotFoundException, SQLException { try (DSLContext jooq = getJooq()) { @@ -352,8 +356,8 @@ public class DoneGateway extends JooqGateway { String projectName = r.get(T_PROJECT.NAME); String moduleName = r.get(T_MODULE.NAME); String jobName = r.get(T_JOB.NAME); - Date timeFrom = r.get(T_DONE.TIME_FROM); - Date timeUntil = r.get(T_DONE.TIME_UNTIL); + java.util.Date timeFrom = r.get(T_DONE.TIME_FROM); + java.util.Date timeUntil = r.get(T_DONE.TIME_UNTIL); StringBuilder buf = new StringBuilder(); buf.append(projectName); @@ -376,4 +380,55 @@ public class DoneGateway extends JooqGateway { return list.toJson(); } } + + public List getAllUsers() throws DataAccessException, ClassNotFoundException, SQLException { + List list = new ArrayList<>(); + try (DSLContext jooq = getJooq()) { + SelectJoinStep> sql = jooq + // @formatter:off + .select(T_LOGIN.LOGIN, + T_LOGIN.FORENAME, + T_LOGIN.SURNAME) + .from(T_LOGIN); + // @formatter:on + LOGGER.debug(sql.toString()); + for (Record r : sql.fetch()) { + String username = r.get(T_LOGIN.LOGIN); + String forename = r.get(T_LOGIN.FORENAME); + String surname = r.get(T_LOGIN.SURNAME); + list.add(new UserBean(username, forename, surname)); + } + } + return list; + } + + public String download(DownloadBean bean) throws DataAccessException, ClassNotFoundException, SQLException { + StringBuilder buf = new StringBuilder(); + try (DSLContext jooq = getJooq()) { + SelectConditionStep> sql = jooq + // @formatter:off + .select(V_HAMSTERSUMMARY.WORKDAY, + V_HAMSTERSUMMARY.DURATION, + V_HAMSTERSUMMARY.PROJECT_NAME, + V_HAMSTERSUMMARY.MODULE_NAME, + V_HAMSTERSUMMARY.JOB_NAME) + .from(V_HAMSTERSUMMARY) + .where(V_HAMSTERSUMMARY.LOGIN.eq(bean.getUsername())) + .and(V_HAMSTERSUMMARY.WORKDAY.le(Date.valueOf(bean.getUntilDate())) + .and(V_HAMSTERSUMMARY.WORKDAY.ge(Date.valueOf(bean.getFromDate())))); + // @formatter:on + LOGGER.debug(sql.toString()); + String sep = ";"; + buf.append("day").append(sep).append("duration").append(sep).append("project").append(sep).append("module").append(sep).append("activity\n"); + for (Record r : sql.fetch()) { + String date = new SimpleDateFormat("dd.MM.yyyy").format(r.get(V_HAMSTERSUMMARY.WORKDAY)); + buf.append(date).append(sep); + buf.append(r.get(V_HAMSTERSUMMARY.DURATION)).append(sep); + buf.append(r.get(V_HAMSTERSUMMARY.PROJECT_NAME)).append(sep); + buf.append(r.get(V_HAMSTERSUMMARY.MODULE_NAME)).append(sep); + buf.append(r.get(V_HAMSTERSUMMARY.JOB_NAME)).append("\n"); + } + } + return buf.toString(); + } } diff --git a/src/main/java/de/jottyfan/timetrack/modules/done/DoneModel.java b/src/main/java/de/jottyfan/timetrack/modules/done/DoneModel.java index 01c400d..d72103b 100644 --- a/src/main/java/de/jottyfan/timetrack/modules/done/DoneModel.java +++ b/src/main/java/de/jottyfan/timetrack/modules/done/DoneModel.java @@ -1,8 +1,10 @@ package de.jottyfan.timetrack.modules.done; +import java.io.IOException; import java.io.Serializable; import java.sql.SQLException; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; @@ -42,12 +44,14 @@ public class DoneModel implements Model, Serializable { private List times; private List allJobs; private WholeDaySummaryBean daySummary; - private Date day; + private java.util.Date day; private String calendarEvents; + private List users; + private DownloadBean downloadBean; public boolean init(JooqFacesContext facesContext) { try { - day = day == null ? new Date() : day; + day = day == null ? new java.util.Date() : day; beans = getAllOfDay(facesContext, day); DoneGateway gw = new DoneGateway(facesContext); modules = gw.getAllModules(); @@ -74,6 +78,20 @@ public class DoneModel implements Model, Serializable { } } + public boolean prepareRead(JooqFacesContext facesContext) { + try { + users = new DoneGateway(facesContext).getAllUsers(); + downloadBean = new DownloadBean(); + downloadBean.setFromDate(LocalDate.of(LocalDate.now().getYear(), 1, 1)); + downloadBean.setUntilDate(LocalDate.now()); + return true; + } catch (DataAccessException | ClassNotFoundException | SQLException e) { + facesContext.addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_ERROR, "error loading defaults", e.getMessage())); + return false; + } + } + private void defineTimes() { times = new ArrayList<>(); Integer currentHour = getCurrentTime().get(GregorianCalendar.HOUR_OF_DAY); @@ -127,10 +145,11 @@ public class DoneModel implements Model, Serializable { * @param login * the user to look up for * @return all entries - * @throws SQLException - * @throws ClassNotFoundException + * @throws SQLException + * @throws ClassNotFoundException */ - private List getAllOfDay(JooqFacesContext facesContext, Date day) throws DataAccessException, ClassNotFoundException, SQLException { + private List getAllOfDay(JooqFacesContext facesContext, Date day) + throws DataAccessException, ClassNotFoundException, SQLException { LocalDateTime ldt = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); return new DoneGateway(facesContext).getAll(ldt); } @@ -166,6 +185,21 @@ public class DoneModel implements Model, Serializable { } } + public boolean download(JooqFacesContext facesContext) { + try { + String csvContent = new DoneGateway(facesContext).download(downloadBean); + facesContext.getExternalContext().responseReset(); + facesContext.getExternalContext().setResponseContentType("text/csv"); + facesContext.getExternalContext().setResponseHeader("Content-Disposition", "attachment; filename=\"worktime.csv\""); + facesContext.getExternalContext().getResponseOutputWriter().append(csvContent); + facesContext.responseComplete(); + return true; + } catch (DataAccessException | ClassNotFoundException | SQLException | IOException e) { + facesContext.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "error", e.getMessage())); + return false; + } + } + public String getAttach() { StringBuilder buf = new StringBuilder(); String thatday = new SimpleDateFormat("dd.MM.yyyy").format(day); @@ -176,14 +210,14 @@ public class DoneModel implements Model, Serializable { buf.append(sdb.getModuleName()).append("\t"); buf.append(sdb.getJobName()).append("\t"); buf.append("\n"); - } + } return buf.toString(); } - + public String getDayIso8601() { return day == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(day); } - + public void setBean(DoneBean bean) { this.bean = bean; } @@ -231,4 +265,18 @@ public class DoneModel implements Model, Serializable { public String getCalendarEvents() { return calendarEvents; } + + /** + * @return the users + */ + public List getUsers() { + return users; + } + + /** + * @return the downloadBean + */ + public DownloadBean getDownloadBean() { + return downloadBean; + } } diff --git a/src/main/java/de/jottyfan/timetrack/modules/done/DownloadBean.java b/src/main/java/de/jottyfan/timetrack/modules/done/DownloadBean.java new file mode 100644 index 0000000..b0732f1 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/done/DownloadBean.java @@ -0,0 +1,61 @@ +package de.jottyfan.timetrack.modules.done; + +import java.io.Serializable; +import java.time.LocalDate; + +/** + * + * @author jotty + * + */ +public class DownloadBean implements Serializable { + private static final long serialVersionUID = 1L; + + private String username; + private LocalDate fromDate; + private LocalDate untilDate; + + /** + * @return the fromDate + */ + public LocalDate getFromDate() { + return fromDate; + } + + /** + * @param fromDate + * the fromDate to set + */ + public void setFromDate(LocalDate fromDate) { + this.fromDate = fromDate; + } + + /** + * @return the untilDate + */ + public LocalDate getUntilDate() { + return untilDate; + } + + /** + * @param untilDate + * the untilDate to set + */ + public void setUntilDate(LocalDate untilDate) { + this.untilDate = untilDate; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } +} diff --git a/src/main/java/de/jottyfan/timetrack/modules/done/UserBean.java b/src/main/java/de/jottyfan/timetrack/modules/done/UserBean.java new file mode 100644 index 0000000..fa00222 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/modules/done/UserBean.java @@ -0,0 +1,44 @@ +package de.jottyfan.timetrack.modules.done; + +import java.io.Serializable; + +/** + * + * @author jotty + * + */ +public class UserBean implements Serializable { + private static final long serialVersionUID = 1L; + + private final String username; + private final String forename; + private final String surname; + + public UserBean(String username, String forename, String surname) { + super(); + this.username = username; + this.forename = forename; + this.surname = surname; + } + + /** + * @return the forename + */ + public String getForename() { + return forename; + } + + /** + * @return the surname + */ + public String getSurname() { + return surname; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } +} diff --git a/src/main/webapp/pages/done/read.xhtml b/src/main/webapp/pages/done/read.xhtml new file mode 100644 index 0000000..466cd01 --- /dev/null +++ b/src/main/webapp/pages/done/read.xhtml @@ -0,0 +1,46 @@ + + + + Arbeitszeit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/pages/start.xhtml b/src/main/webapp/pages/start.xhtml index 815d48d..6fd80ad 100644 --- a/src/main/webapp/pages/start.xhtml +++ b/src/main/webapp/pages/start.xhtml @@ -63,6 +63,7 @@ + diff --git a/src/test/java/de/jottyfan/timetrack/help/TestLocalDateConverter.java b/src/test/java/de/jottyfan/timetrack/help/TestLocalDateConverter.java new file mode 100644 index 0000000..0d2377c --- /dev/null +++ b/src/test/java/de/jottyfan/timetrack/help/TestLocalDateConverter.java @@ -0,0 +1,28 @@ +package de.jottyfan.timetrack.help; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.Date; + +import org.junit.jupiter.api.Test; + +/** + * + * @author jotty + * + */ +public class TestLocalDateConverter { + @Test + public void testGetAsObject() { + String now = new SimpleDateFormat("dd.MM.yyyy").format(new Date()); + assertEquals(LocalDate.now(), new LocalDateConverter().getAsObject(null, null, now)); + } + + @Test + public void testGetAsString() { + String now = new SimpleDateFormat("dd.MM.yyyy").format(new Date()); + assertEquals(now, new LocalDateConverter().getAsString(null, null, LocalDate.now())); + } +}