aboutsummaryrefslogtreecommitdiff
path: root/gromo
blob: eb133dabb90dd86ae8055a56b46859a0c981e0b7 (plain) (blame)
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