Have a summary page that can give an overview over a long period.
So you can use it to track your work time.
E.g. an output can look like this:
╔═══════════╤══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ │ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ║
╟───────────┼── week 23 ───────────────────────────────────────────────────────────────────────────────────────╢
║ 09.06 Fr. │ . . . . . . . . . . . . . . .###. ### ## . ### . . . . ║
║ 10.06 Sa. │ . . . . . . . .######## .## ## #. .##### .###. . . . . . . . ║
║ 11.06 So. │ . . . . . . . ### ###### ## . #####. #### . . . . ## . . . . ║
╟───────────┼── week 24 ───────────────────────────────────────────────────────────────────────────────────────╢
║ 12.06 Mo. │ . . . . . . . . ### ####### ## ## ####.# ## ### ##. ## # . . . . ║
║ 13.06 Di. │ . . . . . . . #### . . . . . . . . . .# . .###### . . . ║
║ 14.06 Mi. │ . . . . . . .###.# #### # . . . . . . . . . . . . . . ║
║ 15.06 Do. │ . . . . . .###### . . ##### ##.#### ###### ### ####. ## ### . . . . ║
║ 16.06 Fr. │ . . . . . . . ############## ## . ### .## # ### . . #####. .######## ║
║ 17.06 Sa. │ . . . . . . . . . . ##. # . . . . . . . . . # #### . . ║
║ 18.06 So. │ . . . . . . . . .###. . . . . . . . . . . . . . . ║
╚═══════════╧══════════════════════════════════════════════════════════════════════════════════════════════════╝
I created this by a Java command line tool aggregating the CSV export. However it would be great to have it in the UI directly.
Here my tool:
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.IsoFields;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
public class AfkCalendar {
final int QUATER_HOURS_SECONDS = 60*15;
private String[] args;
private Map<LocalDate, ArrayList<Integer>> data = new TreeMap<>();
public AfkCalendar(String[] args) {
this.args = args;
}
public static void main(String[] args) throws IOException {
var proc = new AfkCalendar(args);
proc.run();
}
private void run() throws IOException {
System.out.println(args[0]);
try( var rd = Files.newBufferedReader(Paths.get(args[0]))){
var title = rd.readLine();
if( !"timestamp,duration,status".equals(title)) {
System.out.println("Unexpected title: "+title);
return;
}
String line = null;
while( (line = rd.readLine()) != null ) {
var parts = line.split(",");
var timeStr = parts[0];
var durStr = parts[1];
var afk = parts[2];
if( !"not-afk".equals(afk)) {
continue;
}
var dateTime = ZonedDateTime.parse(timeStr)
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
var durationRemaining = new BigDecimal(durStr)
.intValue();
var quaterRemaining = QUATER_HOURS_SECONDS
- (dateTime.toLocalTime().toSecondOfDay() % QUATER_HOURS_SECONDS);
var quaterValues = new ArrayList<Integer>();
while( durationRemaining > 0 ) {
var quater = Math.min(quaterRemaining, durationRemaining);
quaterValues.add(quater);
durationRemaining -= quater;
quaterRemaining = QUATER_HOURS_SECONDS;
var date = dateTime.toLocalDate();
var time = dateTime.toLocalTime();
var quaterIndex = time.getHour() * 4
+ time.getMinute() / 15;
var quaterList = data.computeIfAbsent(date, k -> createDayQuaters());
var value = quaterList.get(quaterIndex).intValue();
value += quater;
quaterList.set(quaterIndex, value);
dateTime = dateTime.plus(QUATER_HOURS_SECONDS, ChronoUnit.SECONDS);
}
}
}
System.out.println("╔═══════════╤══════════════════════════════════════════════════════════════════════════════════════════════════╗");
System.out.println("║ │ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ║");
int week = -1;
for( var entry : data.entrySet()) {
var date = entry.getKey();
var quaters = entry.getValue();
int thisWeek = date.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR );
if( week != thisWeek) {
week = thisWeek;
System.out.printf("╟───────────┼── week %2d ───────────────────────────────────────────────────────────────────────────────────────╢%n", week );
}
StringBuilder sb = new StringBuilder();
sb.append(String.format("║ %02d.%02d %s │ ", date.getDayOfMonth(), date.getMonthValue(), getDayOfWeek(date)));
for( int i = 0; i < quaters.size(); i++ ) {
var quater = quaters.get(i);
if( quater.intValue() >= QUATER_HOURS_SECONDS/2 )
sb.append( "#");
else if( i % 4 == 0 )
sb.append( ".");
else
sb.append( " ");
}
sb.append( " ║");
System.out.println(sb);
}
System.out.println("╚═══════════╧══════════════════════════════════════════════════════════════════════════════════════════════════╝");
}
private ArrayList<Integer> createDayQuaters(){
int QUATERS_PER_DAY = 24*4;
var res = new ArrayList<Integer>(QUATERS_PER_DAY);
for( int i = 0; i < QUATERS_PER_DAY; i++ )
res.add(0);
return res;
}
private String getDayOfWeek(LocalDate date) {
return date.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault());
}
}