Skip to content

Commit f1fafc9

Browse files
committed
#21 merged into cleanup branch
Merge branch 'feature/copy_project_report' into feature/sonar_issue_cleanup # Conflicts: # src/main/java/de/doubleslash/keeptime/view/ReportController.java
2 parents 381b3bc + 98deeba commit f1fafc9

File tree

6 files changed

+183
-26
lines changed

6 files changed

+183
-26
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ Main view (when you hover over the app):\
1717
**You need to close the application manualy before you shutdown your PC. Otherwise the last running project is not saved to database.**
1818

1919
# Install
20-
Download and execute .jar file (see [releases](https://github.com/doubleSlashde/KeepTime/releases)). You should put the .jar in an extra folder as a *logs* and a *db* folder will be created next to it.\
21-
It is recommended to run the application at windows start.
20+
Download the .jar and .bat file and execute the .bat (see [releases](https://github.com/doubleSlashde/KeepTime/releases)). The start may take up to one minute. You should put the .jar in an extra folder as a *logs* and a *db* folder will be created next to it.\
21+
It is recommended to run the application at windows start so you do not forget to track your time. To do this follow these steps:
2222
* Copy the keeptime.bat file from this repo next to the *.jar*. Adapt the path inside the *keeptime.bat* to the name of the *.jar* file (if needed). Try starting the application by executing the *keeptime.bat* file. Close the app
2323
* Open the autostart folder: Press *Windows+R*, execute *shell:startup*
2424
* Create a shortcut to the *.bat* in the autostart folder
2525

2626
**migrate from old version**
2727
If you used this application before with a *config.xml* you can import your old projects in the settings dialog. Place your config.xml next to the jar and press "parse config.xml". Otherwise no steps are needed.
2828
## Requirements
29-
Only works correct for windows currently. Tested on 7 and 10.
29+
* Windows 7 or 10 (Linux is not yet supported)
30+
* Java 8

keeptime.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
start "" "javaw" -Dprism.order=sw -jar keeptime-1.0.0-SNAPSHOT.jar
1+
start "" "javaw" -Dprism.order=sw -jar keeptime-v1.0.0.jar

src/main/java/de/doubleslash/keeptime/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class Main extends Application {
4646

4747
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
4848

49-
public static final String VERSION = "v0.0.2";
49+
public static final String VERSION = "v1.0.0";
5050

5151
private ConfigurableApplicationContext springContext;
5252

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package de.doubleslash.keeptime.view;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
import static de.doubleslash.keeptime.view.ReportController.EMPTY_NOTE;
7+
import static de.doubleslash.keeptime.view.ReportController.NOTE_DELIMETER;
8+
9+
import java.lang.invoke.MethodHandles;
10+
11+
public class ProjectReport {
12+
13+
/** The slf4j-logger for this class. */
14+
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
15+
16+
private int numberOfNotes;
17+
18+
private final int size;
19+
20+
private final StringBuilder sb;
21+
22+
public ProjectReport(final int size) {
23+
this.size = size;
24+
this.sb = new StringBuilder(2 * 1024);
25+
}
26+
27+
public void appendToWorkNotes(final String currentWorkNote) {
28+
this.numberOfNotes++;
29+
if (!currentWorkNote.equals(EMPTY_NOTE)) {
30+
if (this.numberOfNotes > 1) {
31+
this.sb.append(NOTE_DELIMETER);
32+
}
33+
this.sb.append(currentWorkNote.trim());
34+
} else {
35+
LOG.debug("Skipping empty note.");
36+
}
37+
}
38+
39+
public int getNumberOfNotes() {
40+
return this.numberOfNotes;
41+
}
42+
43+
public String getNotes(final boolean addNumberOfNotes) {
44+
if (addNumberOfNotes) {
45+
return Integer.toString(this.numberOfNotes) + " Notes: " + this.sb.toString();
46+
} else {
47+
return this.sb.toString();
48+
}
49+
}
50+
51+
}

src/main/java/de/doubleslash/keeptime/view/ReportController.java

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
import de.doubleslash.keeptime.model.Model;
1717
import de.doubleslash.keeptime.model.Project;
1818
import de.doubleslash.keeptime.model.Work;
19+
import javafx.event.ActionEvent;
20+
import javafx.event.EventHandler;
1921
import javafx.fxml.FXML;
2022
import javafx.scene.Node;
23+
import javafx.scene.control.Button;
2124
import javafx.scene.control.DateCell;
2225
import javafx.scene.control.DatePicker;
2326
import javafx.scene.control.Label;
2427
import javafx.scene.control.ScrollPane;
28+
import javafx.scene.input.Clipboard;
29+
import javafx.scene.input.ClipboardContent;
2530
import javafx.scene.layout.BorderPane;
2631
import javafx.scene.layout.GridPane;
2732
import javafx.scene.text.Font;
@@ -30,6 +35,10 @@
3035

3136
public class ReportController {
3237

38+
public static final String NOTE_DELIMETER = "; ";
39+
40+
public static final String EMPTY_NOTE = "- No notes -";
41+
3342
private static final String FX_BACKGROUND_COLOR_NOT_WORKED = "-fx-background-color: #BBBBBB;";
3443

3544
@FXML
@@ -57,22 +66,22 @@ public class ReportController {
5766
private void initialize() {
5867
LOG.info("Init reportController");
5968

60-
datePicker = new DatePicker(LocalDate.now());
61-
datePicker.valueProperty().addListener((observable, oldvalue, newvalue) -> {
69+
this.datePicker = new DatePicker(LocalDate.now());
70+
this.datePicker.valueProperty().addListener((observable, oldvalue, newvalue) -> {
6271
LOG.info("Datepicker selected value changed to {}", newvalue);
6372
updateReport(newvalue);
6473
});
6574
}
6675

6776
private void updateReport(final LocalDate newvalue) {
68-
currentDayLabel.setText(DateFormatter.toDayDateString(newvalue));
77+
this.currentDayLabel.setText(DateFormatter.toDayDateString(newvalue));
6978
final List<Work> currentWorkItems = model.getWorkRepository().findByCreationDate(newvalue);
7079

7180
final SortedSet<Project> workedProjectsSet = currentWorkItems.stream().map(Work::getProject)
7281
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Project::getName))));
7382

74-
gridPane.getChildren().clear();
75-
gridPane.getRowConstraints().clear();
83+
this.gridPane.getChildren().clear();
84+
this.gridPane.getRowConstraints().clear();
7685

7786
int rowIndex = 0;
7887
long currentWorkSeconds = 0;
@@ -82,9 +91,8 @@ private void updateReport(final LocalDate newvalue) {
8291

8392
for (final Project project : workedProjectsSet) {
8493
final Label projectName = new Label(project.getName());
85-
8694
projectName.setFont(labelFontBold);
87-
gridPane.add(projectName, 0, rowIndex);
95+
this.gridPane.add(projectName, 0, rowIndex);
8896

8997
final List<Work> onlyCurrentProjectWork = currentWorkItems.stream().filter(w -> w.getProject() == project)
9098
.collect(Collectors.toList());
@@ -99,46 +107,81 @@ private void updateReport(final LocalDate newvalue) {
99107

100108
final Label workedTimeLabel = new Label(DateFormatter.secondsToHHMMSS(todaysWorkSeconds));
101109
workedTimeLabel.setFont(labelFontBold);
110+
this.gridPane.add(workedTimeLabel, 2, rowIndex);
111+
112+
// text will be set later
113+
/*
114+
* final TextArea textArea = new TextArea(); textArea.setMaxHeight(20); textArea.setFont(Font.font("System",
115+
* FontWeight.NORMAL, 15)); textArea.setWrapText(true); this.gridPane.add(textArea, 1, rowIndex);
116+
*/
117+
final Button bProjectReport = createProjectReport();
118+
this.gridPane.add(bProjectReport, 1, rowIndex);
102119

103-
gridPane.add(workedTimeLabel, 2, rowIndex);
104120
rowIndex++;
105121

122+
final ProjectReport pr = new ProjectReport(onlyCurrentProjectWork.size());
106123
for (int j = 0; j < onlyCurrentProjectWork.size(); j++) {
107124
final Work work = onlyCurrentProjectWork.get(j);
108125
final String workedHours = DateFormatter
109126
.secondsToHHMMSS(DateFormatter.getSecondsBewtween(work.getStartTime(), work.getEndTime()));
110127

111-
final Label commentLabel = new Label(work.getNotes());
128+
final String currentWorkNote = work.getNotes();
129+
pr.appendToWorkNotes(currentWorkNote);
130+
final Label commentLabel = new Label(currentWorkNote);
112131
commentLabel.setFont(labelFontNormal);
113132
commentLabel.setWrapText(true);
114-
gridPane.add(commentLabel, 0, rowIndex);
133+
this.gridPane.add(commentLabel, 0, rowIndex);
115134

116135
final Label fromTillLabel = new Label(DateFormatter.toTimeString(work.getStartTime()) + " - "
117136
+ DateFormatter.toTimeString(work.getEndTime()));
118137
fromTillLabel.setFont(labelFontNormal);
119138
fromTillLabel.setWrapText(true);
120-
gridPane.add(fromTillLabel, 1, rowIndex);
139+
this.gridPane.add(fromTillLabel, 1, rowIndex);
121140

122141
final Label workedHoursLabel = new Label(workedHours);
123142
workedHoursLabel.setFont(labelFontNormal);
124-
gridPane.add(workedHoursLabel, 2, rowIndex);
143+
this.gridPane.add(workedHoursLabel, 2, rowIndex);
125144

126145
rowIndex++;
127146
}
147+
// textArea.setText(pr.getNotes(true));
148+
bProjectReport.setUserData(pr.getNotes(true));
128149
}
129-
scrollPane.setVvalue(0); // scroll to the top
150+
this.scrollPane.setVvalue(0); // scroll to the top
151+
152+
this.currentDayTimeLabel.setText(DateFormatter.secondsToHHMMSS(currentSeconds));
153+
this.currentDayWorkTimeLabel.setText(DateFormatter.secondsToHHMMSS(currentWorkSeconds));
154+
}
130155

131-
currentDayTimeLabel.setText(DateFormatter.secondsToHHMMSS(currentSeconds));
132-
currentDayWorkTimeLabel.setText(DateFormatter.secondsToHHMMSS(currentWorkSeconds));
156+
private Button createProjectReport() {
157+
final Button bProjectReport = new Button("Copy to clipboard");
158+
final EventHandler<ActionEvent> eventListener = new EventHandler<ActionEvent>() {
159+
160+
@Override
161+
public void handle(final ActionEvent event) {
162+
final Object source = event.getSource();
163+
final Button btn = (Button) source;
164+
final Object userData = btn.getUserData();
165+
final String notes = (String) userData;
166+
167+
final Clipboard clipboard = Clipboard.getSystemClipboard();
168+
final ClipboardContent content = new ClipboardContent();
169+
content.putString(notes);
170+
clipboard.setContent(content);
171+
}
172+
173+
};
174+
bProjectReport.setOnAction(eventListener);
175+
return bProjectReport;
133176
}
134177

135178
public void setModel(final Model model) {
136179
this.model = model;
137180

138181
// HACK to show calendar from datepicker
139182
// https://stackoverflow.com/questions/34681975/javafx-extract-calendar-popup-from-datepicker-only-show-popup
140-
final DatePickerSkin datePickerSkin = new DatePickerSkin(datePicker);
141-
final Callback<DatePicker, DateCell> dayCellFactory = callback -> new DateCell() {
183+
final DatePickerSkin datePickerSkin = new DatePickerSkin(this.datePicker);
184+
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
142185
@Override
143186
public void updateItem(final LocalDate item, final boolean empty) {
144187
super.updateItem(item, empty);
@@ -148,13 +191,12 @@ public void updateItem(final LocalDate item, final boolean empty) {
148191
}
149192
}
150193
};
151-
152-
datePicker.setDayCellFactory(dayCellFactory);
194+
this.datePicker.setDayCellFactory(dayCellFactory);
153195
final Node popupContent = datePickerSkin.getPopupContent();
154-
topBorderPane.setRight(popupContent);
196+
this.topBorderPane.setRight(popupContent);
155197
}
156198

157199
public void update() {
158-
updateReport(datePicker.getValue());
200+
updateReport(this.datePicker.getValue());
159201
}
160202
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package de.doubleslash.keeptime.view;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import static org.junit.Assert.assertEquals;
9+
10+
import java.lang.invoke.MethodHandles;
11+
12+
public class ProjectReportTest {
13+
14+
/** The slf4j-logger for this class. */
15+
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
16+
17+
private ProjectReport uut;
18+
19+
@Before
20+
public void setUp() throws Exception {
21+
this.uut = new ProjectReport(3);
22+
}
23+
24+
@Test
25+
public void testAppendToWorkNotes() {
26+
this.uut.appendToWorkNotes("note 1 ");
27+
this.uut.appendToWorkNotes(ReportController.EMPTY_NOTE);
28+
this.uut.appendToWorkNotes("note 2 ");
29+
final String expected = "note 1; note 2";
30+
assertEquals(expected, this.uut.getNotes(false));
31+
}
32+
33+
@Test
34+
public void testAppendToWorkNotesAddNumberOfNotes() {
35+
this.uut.appendToWorkNotes("note 1 ");
36+
this.uut.appendToWorkNotes(ReportController.EMPTY_NOTE);
37+
this.uut.appendToWorkNotes("note 2 ");
38+
final String expected = "3 Notes: note 1; note 2";
39+
assertEquals(expected, this.uut.getNotes(true));
40+
}
41+
42+
@Test
43+
public void testAppendToWorkNotesAddNumberOfNotes_2() {
44+
this.uut = new ProjectReport(3);
45+
this.uut.appendToWorkNotes("note 1");
46+
this.uut.appendToWorkNotes("note 2");
47+
this.uut.appendToWorkNotes("note 3");
48+
final String expected = "3 Notes: note 1; note 2; note 3";
49+
assertEquals(expected, this.uut.getNotes(true));
50+
}
51+
52+
@Test
53+
public void testAppendToWorkNotesAddNumberOfNotes_EmptyNotesAtTheEnd() {
54+
this.uut = new ProjectReport(4);
55+
this.uut.appendToWorkNotes("note 1");
56+
this.uut.appendToWorkNotes("note 2");
57+
this.uut.appendToWorkNotes(ReportController.EMPTY_NOTE);
58+
this.uut.appendToWorkNotes(ReportController.EMPTY_NOTE);
59+
final String expected = "4 Notes: note 1; note 2";
60+
assertEquals(expected, this.uut.getNotes(true));
61+
}
62+
63+
}

0 commit comments

Comments
 (0)