BrightSide Workbench Full Report + Source Code
TimeTrackerQueue.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2020 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 package org.turro.timetracker;
20 
21 import java.time.Duration;
22 import java.time.Instant;
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.EnumSet;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.TreeSet;
29 import java.util.stream.Stream;
30 import org.amic.util.date.CheckDate;
31 import org.turro.elephant.db.ElephantPU;
32 import org.turro.elephant.db.WhereClause;
33 import org.turro.elephant.entities.db.TimeTracker;
34 import org.turro.elephant.entities.db.TimeTrackerPK;
35 import org.turro.elephant.entities.db.TimeTrackerType;
36 import org.turro.jpa.Dao;
37 import org.turro.sql.SqlClause;
38 
43 public class TimeTrackerQueue extends TreeSet<TimeTracker> {
44 
45  private final String entityPath, trackedPath;
46 
47  public boolean hasIntegrity() {
48  TimeTrackerType[] previousType = new TimeTrackerType[1];
49  return stream().noneMatch(e -> {
50  boolean match = !e.getTrackType().isPreviousValid(previousType[0]);
51  previousType[0] = e.getTrackType();
52  return match;
53  });
54  }
55 
56  public boolean correctDate(CheckDate dateTrack) {
57  if(isEmpty()) return true;
58  TimeTracker tt = last();
59  if(tt != null) {
60  return tt.getTimeTrack().before(dateTrack.getDate());
61  }
62  return false;
63  }
64 
65  public TimeTracker addTrack(TimeTrackerType type, Date dateTrack) {
66  if(type != null && type.isPreviousValid(getLastType())) {
67  TimeTracker tt = new TimeTracker();
68  tt.setEntityPath(entityPath);
69  tt.setTrackedPath(trackedPath);
70  tt.setTrackType(type);
71  tt.setTimeTrack(dateTrack);
72  if(!tt.isEmpty()) {
73  tt = getDao().saveObject(tt);
74  if(!tt.getTrackType().isStarts()) {
75  setHours();
76  tt = getDao().find(TimeTracker.class, TimeTrackerPK.from(tt));
77  }
78  return tt;
79  }
80  }
81  return null;
82  }
83 
85  TimeTracker last = isEmpty() ? null : last();
86  return last == null ? null : last.getTrackType();
87  }
88 
89  public Set<TimeTrackerType> getNextValids() {
90  TimeTracker last = isEmpty() ? null : last();
91  return last == null ? EnumSet.of(TimeTrackerType.TRACK_START) : TimeTrackerType.getValidNexts(last.getTrackType());
92  }
93 
94  public List<TimeTracker> getActiveTracks() {
96  }
97 
98  public List<TimeTracker> getInactiveTracks() {
100  }
101 
102  public List<TimeTracker> getTracksByStatus(Set<TimeTrackerType> types) {
103  WhereClause wc = new WhereClause();
104  wc.addClause("select tt from TimeTracker tt");
105  wc.addClause("where tt.trackedPath = :trackedPath");
106  wc.addNamedValue("trackedPath", trackedPath);
107  wc.addClause("and tt.trackType in (:types)");
108  wc.addNamedValue("types", types);
109  wc.addClause("and tt.timeTrack = (");
110  wc.addClause("select max(t2.timeTrack) from TimeTracker t2");
111  wc.addClause("where tt.entityPath = t2.entityPath");
112  wc.addClause("and tt.trackedPath = t2.trackedPath");
113  wc.addClause(")");
114  return getDao().getResultList(TimeTracker.class, wc);
115  }
116 
117  public void deleteLastTrack() {
118  getDao().deleteObject(last());
119  }
120 
121  private void setHours() {
122  Date from = SqlClause.select("min(tt.timeTrack)").from("TimeTracker tt")
123  .where().in("tt.trackType", List.copyOf(TimeTrackerType.getEndTypes()))
124  .and().equal("tt.hours", 0.0)
125  .and().equal("entityPath", entityPath)
126  .and().equal("trackedPath", trackedPath)
127  .dao(getDao())
128  .singleResult(Date.class);
129  List<TimeTracker> toSave = new ArrayList<>();
130  final AssignHoursModel model = new AssignHoursModel();
131  SqlClause.select("tt").from("TimeTracker tt")
132  .where().greater("tt.timeTrack", new CheckDate(from).addDays(-1).getDate())
133  .and().equal("entityPath", entityPath)
134  .and().equal("trackedPath", trackedPath)
135  .orderBy("tt.timeTrack asc")
136  .dao(getDao())
137  .forEach(TimeTracker.class, tt -> {
138  if(tt.getTrackType().isStarts()) {
139  model.assignFrom(tt);
140  } else if(model.isValid()) {
141  tt.setHours(model.getFrom(tt));
142  toSave.add(tt);
143  }
144  });
145  getDao().saveCollection(toSave);
146  }
147 
148  public boolean hasFullIntegrity() {
149  WhereClause wc = new WhereClause();
150  wc.addClause("select tt from TimeTracker tt");
151  wc.addClause("where tt.entityPath = :entityPath");
152  wc.addNamedValue("entityPath", entityPath);
153  wc.addClause("and tt.trackedPath = :trackedPath");
154  wc.addNamedValue("trackedPath", trackedPath);
155  wc.addClause("order by tt.timeTrack ASC");
156  TimeTrackerType[] previousType = new TimeTrackerType[1];
157  try(Stream<TimeTracker> entries = getDao().stream(TimeTracker.class, wc)) {
158  return entries.noneMatch(e -> {
159  boolean match = !e.getTrackType().isPreviousValid(previousType[0]);
160  previousType[0] = e.getTrackType();
161  return match;
162  });
163  }
164  }
165 
166  public void repairWith(Duration max) {
167  WhereClause wc = new WhereClause();
168  wc.addClause("select tt from TimeTracker tt");
169  wc.addClause("where tt.entityPath = :entityPath");
170  wc.addNamedValue("entityPath", entityPath);
171  wc.addClause("and tt.trackedPath = :trackedPath");
172  wc.addNamedValue("trackedPath", trackedPath);
173  wc.addClause("order by tt.timeTrack ASC");
174  TimeTracker[] previous = new TimeTracker[1];
175  try(Stream<TimeTracker> entries = getDao().stream(TimeTracker.class, wc)) {
176  entries.forEach(e -> {
177  if(!e.isPreviousValid(previous[0])) {
178  repair(max, previous[0], e);
179  }
180  previous[0] = e;
181  });
182  }
183  }
184 
185  private void repair(Duration max, TimeTracker previous, TimeTracker current) {
186  TimeTracker tt = new TimeTracker();
187  tt.setEntityPath(current.getEntityPath());
188  tt.setTrackedPath(current.getTrackedPath());
189  tt.setTrackType(current.getTrackType().getValidRepair(previous));
190 // tt.setTimeTrack(null);
191 // if(!tt.isEmpty()) getDao().saveObject(tt);
192  }
193 
194  /* Instance */
195 
196  public static TimeTrackerQueue load(String entityPath, String trackedPath) {
197  TimeTrackerQueue ttq = new TimeTrackerQueue(entityPath, trackedPath);
198  ttq.loadLastActiveTrack();
199  return ttq;
200  }
201 
202  public static TimeTrackerQueue load(String entityPath, String trackedPath, int hours) {
203  TimeTrackerQueue ttq = new TimeTrackerQueue(entityPath, trackedPath);
204  ttq.loadLastHoursEntries(hours);
205  return ttq;
206  }
207 
208  private TimeTrackerQueue(String entityPath, String trackedPath) {
209  this.entityPath = entityPath;
210  this.trackedPath = trackedPath;
211  }
212 
213  private void loadLastActiveTrack() {
214  WhereClause wc = new WhereClause();
215  wc.addClause("select tt from TimeTracker tt");
216  wc.addClause("where tt.entityPath = :entityPath");
217  wc.addNamedValue("entityPath", entityPath);
218  wc.addClause("and tt.trackedPath = :trackedPath");
219  wc.addNamedValue("trackedPath", trackedPath);
220  wc.addClause("and tt.timeTrack >= (");
221  wc.addClause("select max(t2.timeTrack) from TimeTracker t2");
222  wc.addClause("where tt.entityPath = t2.entityPath");
223  wc.addClause("and tt.trackedPath = t2.trackedPath");
224  wc.addClause("and t2.trackType = :type");
225  wc.addNamedValue("type", TimeTrackerType.TRACK_START);
226  wc.addClause(")");
227  addAll(getDao().getResultList(TimeTracker.class, wc));
228  }
229 
230  private void loadLastHoursEntries(int hours) {
231  WhereClause wc = new WhereClause();
232  wc.addClause("select tt from TimeTracker tt");
233  wc.addClause("where tt.entityPath = :entityPath");
234  wc.addNamedValue("entityPath", entityPath);
235  wc.addClause("and tt.trackedPath = :trackedPath");
236  wc.addNamedValue("trackedPath", trackedPath);
237  wc.addClause("and tt.timeTrack > :date");
238  wc.addNamedValue("date", Date.from(Instant.now().minus(Duration.ofHours(hours))));
239  addAll(getDao().getResultList(TimeTracker.class, wc));
240  }
241 
242  /* Dao helper */
243 
244  private Dao _dao;
245 
246  private Dao getDao() {
247  if(_dao == null) {
248  _dao = new ElephantPU();
249  }
250  return _dao;
251  }
252 
253 }
void addNamedValue(String name, Object value)
static TimeTrackerPK from(TimeTracker tt)
void setTrackType(TimeTrackerType trackType)
void setTrackedPath(String trackedPath)
void deleteObject(Object obj)
Definition: Dao.java:162
static TimeTrackerQueue load(String entityPath, String trackedPath, int hours)
boolean correctDate(CheckDate dateTrack)
TimeTracker addTrack(TimeTrackerType type, Date dateTrack)
List< TimeTracker > getTracksByStatus(Set< TimeTrackerType > types)
static TimeTrackerQueue load(String entityPath, String trackedPath)
boolean isPreviousValid(TimeTrackerType previousType)
static EnumSet< TimeTrackerType > getValidNexts(TimeTrackerType type)
static EnumSet< TimeTrackerType > getEndTypes()
static EnumSet< TimeTrackerType > getInactiveTypes()
static EnumSet< TimeTrackerType > getActiveTypes()
TimeTrackerType getValidRepair(TimeTracker previous)