1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
#!/bin/bash
#
# gromo - To count your daily Gromodoros :thumbsup. A Gromo is a short unit of
# time, usually 20 minutes, in which you are committed to do your work
# and only your work. It's basically the Pomodoro technique, but more
# opinionated. In particular, it's designed to implement the advice an
# optician once gave me, the "20/20/20" rule, which is: for each 20
# minutes looking at the screen, look away at least 20 meters away for
# at least 20 seconds. Also, not every type of work benefits from the
# interruptions 5-minute rests provoke. So, if the mainstream is not
# enough for you, enter the all-flexible, eye-careful, non-interrupting
# Gromodoros.
#
# Dependencies: curl and mpv for the 'ding' sound; slock to force you to rest :)
################################################################################
shopt -s nullglob
DEFAULT_DING=https://gramos.me/ding.opus
GROMO_SECONDS=$((60 * 20))
STOP_SECONDS=20
DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/gromo
CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/gromo
TODAY_DIR=$DATA_DIR/$(date -I)
DING_FILE=$CACHE_DIR/ding.opus
STATE_FILE=${XDG_STATE_HOME:-$HOME/.local/state}/gromo
cmd_help() {
cmd=$(basename "$0")
echo -e "Usage:
$cmd -h \tShow this help
$cmd -s \tShow status
$cmd -l \tList past gromos
$cmd -x \tReturn state formatted for display in xmobar
$cmd [task]\tStart task
Tasks can be divided in subtasks by using dots (.), e.g., 'gromo work.retro'. Only the first level
is taken into account for organization purposes: the top-level task is considered the 'main task'
and a file is created after it to account for its subtasks.
"
}
cmd_list() {
find "$DATA_DIR/" -type f -print0 | xargs -0 -n 1 basename | sort -u
}
cmd_status() {
format=$1
state=idle
[ -f "$STATE_FILE" ] && state=$(cat "$STATE_FILE")
if [ "$format" = "xmobar" ]; then
color=red
[ "$state" = "idle" ] && color=green
if [ -d "$TODAY_DIR" ]; then
today=""
# needs nullglob
for task_file in "$TODAY_DIR"/*; do
today+="$(basename "$task_file"): $(sum_subtasks "$task_file"), "
done
[ -n "$today" ] && today=" (${today%, })"
fi
echo "<fc=$color>$state</fc>$today"
else
echo -e "state: $state\n"
if [ -d "$TODAY_DIR" ]; then
# needs nullglob
for task_file in "$TODAY_DIR"/*; do
sort < "$task_file"
done
fi
fi
}
sum_subtasks() {
file=$1
awk -F: '{ sum += $2 } END { print sum }' "$file"
}
inc_subtask() {
task=$1
maintask="${fulltask%%.*}"
task_file="$TODAY_DIR/$maintask"
if [ -f "$task_file" ]; then
awk -F: "\$1 == \"$task\" { \$2 += 1; added=1 } { print } END { if (!added) { print \"$task\" FS 1 } }" "$task_file" > tmp
mv tmp "$task_file"
else
echo "$task 1" > "$task_file"
fi
}
pp_seconds() {
seconds=$1
if [ "$seconds" -lt 60 ]; then
echo "$seconds seconds"
elif [ "$seconds" -lt 3600 ]; then
echo "$((seconds / 60)) minutes"
else
echo "$((seconds / 3600)) hours"
fi
}
optspec="hslx"
while getopts "$optspec" optchar; do
case "$optchar" in
h)
cmd_help
exit 0
;;
s)
cmd_status
exit 0
;;
l)
cmd_list
exit 0
;;
x)
cmd_status xmobar
exit 0
;;
*)
cmd_help
exit 0
;;
esac
done
if [ "$#" -ge 1 ]; then
fulltask="$1"
else
cmd_help
exit 0
fi
mkdir -p "$DATA_DIR" "$TODAY_DIR"
[ -f "$DING_FILE" ] || curl $DEFAULT_DING --create-dirs -so "$DING_FILE"
if [ -f "$STATE_FILE" ]; then
echo "Another instance is currently running; exiting (remove $STATE_FILE to override)"
exit 1
fi
echo idle > "$STATE_FILE"
trap 'rm $STATE_FILE 2> /dev/null; exit' INT TERM EXIT
while true; do
echo -ne "\r\033[K[** IN PROGRESS: $fulltask **] "
echo "$fulltask" > "$STATE_FILE"
sleep $GROMO_SECONDS
echo idle > "$STATE_FILE"
inc_subtask "$fulltask"
(sleep $STOP_SECONDS && mpv --no-terminal "$DING_FILE") &
slock || exit
done
|