download worktime summaries

This commit is contained in:
2019-09-25 13:05:17 +02:00
parent dbd3e42466
commit 7548ba6986
11 changed files with 349 additions and 21 deletions

View File

@ -22,7 +22,7 @@ apply plugin: 'eclipse'
apply plugin: 'nu.studer.jooq' apply plugin: 'nu.studer.jooq'
group = 'jottyfan' group = 'jottyfan'
version = '1.0.8' version = '1.0.9'
description = """timetrack""" description = """timetrack"""

View File

@ -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<LocalDate> {
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);
}
}

View File

@ -51,6 +51,10 @@ public enum Pages
* done clear * done clear
*/ */
DONE_CLEAR("/pages/done/clear.jsf"), DONE_CLEAR("/pages/done/clear.jsf"),
/**
* done read
*/
DONE_READ("/pages/done/read.jsf"),
/** /**
* organize init * organize init
*/ */

View File

@ -57,6 +57,11 @@ public class DoneControl extends Navigation implements ControlInterface, Seriali
return navigateTo(Pages.DONE_DELETE); 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() { public String doUpdate() {
boolean ready = model.update((JooqFacesContext) FacesContext.getCurrentInstance()); boolean ready = model.update((JooqFacesContext) FacesContext.getCurrentInstance());
return ready ? toList() : toEdit(model.getBean()); 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()); boolean ready = model.insert((JooqFacesContext) FacesContext.getCurrentInstance());
return ready ? toList() : toAdd(); return ready ? toList() : toAdd();
} }
public String doDownload() {
boolean ready = model.download((JooqFacesContext) FacesContext.getCurrentInstance());
return ready ? "" : "";
}
public String getCurrentTimeAsString() { public String getCurrentTimeAsString() {
return new SimpleDateFormat("HH:mm:ss").format(getCurrentDate()); return new SimpleDateFormat("HH:mm:ss").format(getCurrentDate());

View File

@ -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_JOB;
import static de.jottyfan.timetrack.db.done.Tables.T_MODULE; 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.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_TASKLIST;
import static de.jottyfan.timetrack.db.done.Tables.V_TOTALOFDAY; 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.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -23,9 +25,11 @@ import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep; import org.jooq.DeleteConditionStep;
import org.jooq.InsertValuesStep6; import org.jooq.InsertValuesStep6;
import org.jooq.Record; import org.jooq.Record;
import org.jooq.Record3;
import org.jooq.Record4; import org.jooq.Record4;
import org.jooq.Record5; import org.jooq.Record5;
import org.jooq.SelectConditionStep; import org.jooq.SelectConditionStep;
import org.jooq.SelectJoinStep;
import org.jooq.SelectWhereStep; import org.jooq.SelectWhereStep;
import org.jooq.UpdateConditionStep; import org.jooq.UpdateConditionStep;
import org.jooq.exception.DataAccessException; import org.jooq.exception.DataAccessException;
@ -51,7 +55,7 @@ public class DoneGateway extends JooqGateway {
public DoneGateway(JooqFacesContext facesContext) { public DoneGateway(JooqFacesContext facesContext) {
super(facesContext); super(facesContext);
String dailyMinutes = facesContext.getExternalContext().getInitParameter("requestedDailyMinutes"); 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 * @param day
* the day * the day
* @return the day summary if found, an empty map otherwise * @return the day summary if found, an empty map otherwise
* @throws SQLException * @throws SQLException
* @throws ClassNotFoundException * @throws ClassNotFoundException
* @throws DataAccessException * @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()) { try (DSLContext jooq = getJooq()) {
SelectConditionStep<Record4<String, String, String, String>> sql = jooq SelectConditionStep<Record4<String, String, String, String>> sql = jooq
// @formatter:off // @formatter:off
@ -298,7 +302,7 @@ public class DoneGateway extends JooqGateway {
* @throws ClassNotFoundException * @throws ClassNotFoundException
* @throws DataAccessException * @throws DataAccessException
*/ */
public List<DailySummaryBean> getAllJobs(Date day) throws DataAccessException, ClassNotFoundException, SQLException { public List<DailySummaryBean> getAllJobs(java.util.Date day) throws DataAccessException, ClassNotFoundException, SQLException {
try (DSLContext jooq = getJooq()) { try (DSLContext jooq = getJooq()) {
SelectConditionStep<Record4<String, String, String, String>> sql = jooq SelectConditionStep<Record4<String, String, String, String>> sql = jooq
// @formatter:off // @formatter:off
@ -327,9 +331,9 @@ public class DoneGateway extends JooqGateway {
* get json representation of all calendar events of user * get json representation of all calendar events of user
* *
* @return * @return
* @throws SQLException * @throws SQLException
* @throws ClassNotFoundException * @throws ClassNotFoundException
* @throws DataAccessException * @throws DataAccessException
*/ */
public String getAllCalendarEvents() throws DataAccessException, ClassNotFoundException, SQLException { public String getAllCalendarEvents() throws DataAccessException, ClassNotFoundException, SQLException {
try (DSLContext jooq = getJooq()) { try (DSLContext jooq = getJooq()) {
@ -352,8 +356,8 @@ public class DoneGateway extends JooqGateway {
String projectName = r.get(T_PROJECT.NAME); String projectName = r.get(T_PROJECT.NAME);
String moduleName = r.get(T_MODULE.NAME); String moduleName = r.get(T_MODULE.NAME);
String jobName = r.get(T_JOB.NAME); String jobName = r.get(T_JOB.NAME);
Date timeFrom = r.get(T_DONE.TIME_FROM); java.util.Date timeFrom = r.get(T_DONE.TIME_FROM);
Date timeUntil = r.get(T_DONE.TIME_UNTIL); java.util.Date timeUntil = r.get(T_DONE.TIME_UNTIL);
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append(projectName); buf.append(projectName);
@ -376,4 +380,55 @@ public class DoneGateway extends JooqGateway {
return list.toJson(); return list.toJson();
} }
} }
public List<UserBean> getAllUsers() throws DataAccessException, ClassNotFoundException, SQLException {
List<UserBean> list = new ArrayList<>();
try (DSLContext jooq = getJooq()) {
SelectJoinStep<Record3<String, String, String>> 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<Record5<Date, String, String, String, String>> 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();
}
} }

View File

@ -1,8 +1,10 @@
package de.jottyfan.timetrack.modules.done; package de.jottyfan.timetrack.modules.done;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
@ -42,12 +44,14 @@ public class DoneModel implements Model, Serializable {
private List<TimeBean> times; private List<TimeBean> times;
private List<DailySummaryBean> allJobs; private List<DailySummaryBean> allJobs;
private WholeDaySummaryBean daySummary; private WholeDaySummaryBean daySummary;
private Date day; private java.util.Date day;
private String calendarEvents; private String calendarEvents;
private List<UserBean> users;
private DownloadBean downloadBean;
public boolean init(JooqFacesContext facesContext) { public boolean init(JooqFacesContext facesContext) {
try { try {
day = day == null ? new Date() : day; day = day == null ? new java.util.Date() : day;
beans = getAllOfDay(facesContext, day); beans = getAllOfDay(facesContext, day);
DoneGateway gw = new DoneGateway(facesContext); DoneGateway gw = new DoneGateway(facesContext);
modules = gw.getAllModules(); 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() { private void defineTimes() {
times = new ArrayList<>(); times = new ArrayList<>();
Integer currentHour = getCurrentTime().get(GregorianCalendar.HOUR_OF_DAY); Integer currentHour = getCurrentTime().get(GregorianCalendar.HOUR_OF_DAY);
@ -127,10 +145,11 @@ public class DoneModel implements Model, Serializable {
* @param login * @param login
* the user to look up for * the user to look up for
* @return all entries * @return all entries
* @throws SQLException * @throws SQLException
* @throws ClassNotFoundException * @throws ClassNotFoundException
*/ */
private List<DoneBean> getAllOfDay(JooqFacesContext facesContext, Date day) throws DataAccessException, ClassNotFoundException, SQLException { private List<DoneBean> getAllOfDay(JooqFacesContext facesContext, Date day)
throws DataAccessException, ClassNotFoundException, SQLException {
LocalDateTime ldt = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); LocalDateTime ldt = day.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
return new DoneGateway(facesContext).getAll(ldt); 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() { public String getAttach() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
String thatday = new SimpleDateFormat("dd.MM.yyyy").format(day); 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.getModuleName()).append("\t");
buf.append(sdb.getJobName()).append("\t"); buf.append(sdb.getJobName()).append("\t");
buf.append("\n"); buf.append("\n");
} }
return buf.toString(); return buf.toString();
} }
public String getDayIso8601() { public String getDayIso8601() {
return day == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(day); return day == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(day);
} }
public void setBean(DoneBean bean) { public void setBean(DoneBean bean) {
this.bean = bean; this.bean = bean;
} }
@ -231,4 +265,18 @@ public class DoneModel implements Model, Serializable {
public String getCalendarEvents() { public String getCalendarEvents() {
return calendarEvents; return calendarEvents;
} }
/**
* @return the users
*/
public List<UserBean> getUsers() {
return users;
}
/**
* @return the downloadBean
*/
public DownloadBean getDownloadBean() {
return downloadBean;
}
} }

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,46 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:my="http://xmlns.jcp.org/jsf/composite/my" xmlns:b="http://bootsfaces.net/ui">
<h:head>
<title>Arbeitszeit</title>
</h:head>
<h:body>
<ui:composition template="/pages/template.xhtml">
<ui:define name="top"></ui:define>
<ui:define name="main">
<b:panel title="Arbeitszeiten" look="primary">
<b:form>
<b:panelGrid colSpans="4,3,3,2">
<b:selectOneMenu value="#{doneModel.downloadBean.username}">
<f:selectItems value="#{doneModel.users}" var="u" itemValue="#{u.username}" itemLabel="#{u.forename} #{u.surname}" />
</b:selectOneMenu>
<b:dateTimePicker value="#{doneModel.downloadBean.fromDate}" format="DD.MM.YYYY" locale="de" disableTimeInterval="true" required="true"
requiredMessage="von muss ausgefüllt werden">
<f:facet name="prepend">
<h:outputText value="von" />
</f:facet>
<f:converter converterId="de.jottyfan.timetrack.help.LocalDateConverter" />
</b:dateTimePicker>
<b:dateTimePicker value="#{doneModel.downloadBean.untilDate}" format="DD.MM.YYYY" locale="de" disableTimeInterval="true" required="true"
requiredMessage="bis muss ausgefüllt werden">
<f:facet name="prepend">
<h:outputText value="bis" />
</f:facet>
<f:converter converterId="de.jottyfan.timetrack.help.LocalDateConverter" />
</b:dateTimePicker>
<b:commandButton action="#{doneControl.doDownload}" value="herunterladen" look="primary" iconAwesome="download" />
</b:panelGrid>
</b:form>
</b:panel>
</ui:define>
<ui:define name="navigation">
<b:form>
<b:buttonGroup>
<b:commandButton action="#{doneControl.toStart}" value="zurück" look="primary" iconAwesome="arrow-left" />
</b:buttonGroup>
</b:form>
</ui:define>
</ui:composition>
</h:body>
</html>

View File

@ -63,6 +63,7 @@
<b:commandButton action="#{contactControl.toList}" value="#{contactControl.amount} Kontakte verwalten" look="primary" iconAwesome="group" <b:commandButton action="#{contactControl.toList}" value="#{contactControl.amount} Kontakte verwalten" look="primary" iconAwesome="group"
rendered="#{sessionBean.hasPrivilege('write_contact')}" /> rendered="#{sessionBean.hasPrivilege('write_contact')}" />
<b:commandButton action="#{doneControl.toList}" value="Arbeitszeit verwalten" look="primary" iconAwesome="clock-o" rendered="#{sessionBean.hasPrivilege('write_done')}" /> <b:commandButton action="#{doneControl.toList}" value="Arbeitszeit verwalten" look="primary" iconAwesome="clock-o" rendered="#{sessionBean.hasPrivilege('write_done')}" />
<b:commandButton action="#{doneControl.toRead}" value="Arbeitszeiten anzeigen" look="primary" iconAwesome="clock-o" rendered="#{sessionBean.hasPrivilege('read_done')}" />
<b:commandButton action="#{sessionControl.doLogout}" value="abmelden" look="danger" iconAwesome="sign-out" /> <b:commandButton action="#{sessionControl.doLogout}" value="abmelden" look="danger" iconAwesome="sign-out" />
</b:buttonGroup> </b:buttonGroup>
</b:form> </b:form>

View File

@ -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()));
}
}