Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for Monday-Friday week #458

Merged
merged 8 commits into from
Apr 4, 2024
Merged
24 changes: 20 additions & 4 deletions timetagger/app/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3798,6 +3798,12 @@ def open(self, callback=None):
<option value='1'>Monday</option>
<option value='6'>Saturday</option>
</select>
<div>Workdays:</div>
<select>
<option value='2'>Monday - Friday</option>
<option value='1'>Monday - Saturday</option>
<option value='0'>Monday - Sunday</option>
</select>
<div>Show time as:</div>
<select>
<option value='auto'>Auto</option>
Expand Down Expand Up @@ -3900,27 +3906,33 @@ def open(self, callback=None):
self._first_day_of_week.value = first_day_of_week
self._first_day_of_week.onchange = self._on_first_day_of_week_change

# Workdays
workdays = window.simplesettings.get("workdays")
self._workdays = self._repr_form.children[3]
self._workdays.value = workdays
self._workdays.onchange = self._on_workdays_change

# Time representation
time_repr = window.simplesettings.get("time_repr")
self._time_repr = self._repr_form.children[3]
self._time_repr = self._repr_form.children[5]
self._time_repr.value = time_repr
self._time_repr.onchange = self._on_time_repr_change

# Duration representation
duration_repr = window.simplesettings.get("duration_repr")
self._duration_repr = self._repr_form.children[5]
self._duration_repr = self._repr_form.children[7]
self._duration_repr.value = duration_repr
self._duration_repr.onchange = self._on_duration_repr_change

# Today snap time/offset
today_snap_offset = window.simplesettings.get("today_snap_offset")
self._today_snap_offset = self._repr_form.children[7]
self._today_snap_offset = self._repr_form.children[9]
self._today_snap_offset.value = today_snap_offset
self._today_snap_offset.onchange = self._on_today_snap_offset_change

# Today number of hours
today_end_offset = window.simplesettings.get("today_end_offset")
self._today_end_offset = self._repr_form.children[9]
self._today_end_offset = self._repr_form.children[11]
self._today_end_offset.value = today_end_offset
self._today_end_offset.onchange = self._on_today_end_offset_change

Expand Down Expand Up @@ -3961,6 +3973,10 @@ def _on_first_day_of_week_change(self):
first_day_of_week = int(self._first_day_of_week.value)
window.simplesettings.set("first_day_of_week", first_day_of_week)

def _on_workdays_change(self):
workdays = int(self._workdays.value)
window.simplesettings.set("workdays", workdays)

def _on_time_repr_change(self):
time_repr = self._time_repr.value
window.simplesettings.set("time_repr", time_repr)
Expand Down
44 changes: 44 additions & 0 deletions timetagger/app/dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,50 @@ def get_weeknumber(t):
return res # noqa


def get_remaining_hours_of_day(t):
d = Date(t * 1000)
return 24 - d.getHours() - d.getMinutes() / 60 - d.getSeconds() / 3600


def get_elapsed_hours_of_day(t):
d = Date(t * 1000)
return d.getHours() + d.getMinutes() / 60 + d.getSeconds() / 3600


def get_free_hours_in_range(t1, t2, free_days):
free_hours = 0
if free_days >= 1:
d1 = Date(t1 * 1000)
d2 = Date(t2 * 1000)
hours_in_range = (t2 - t1) / 3600

if hours_in_range > 24: # scale > "1D"
while d1 < d2:
if d1.getDay() == 0: # sunday
free_hours += 24
elif d1.getDay() == 6 and free_days == 2: # saturday
free_hours += 24
d1.setDate(d1.getDate() + 1) # next day
else: # scale <= "1D"
# starts on sunday
if d1.getDay() == 0 and d2.getDay() != 0:
free_hours = get_remaining_hours_of_day(t1)
# only sunday
elif d1.getDay() == 0 and d2.getDay() == 0:
free_hours = hours_in_range
# ends on sunday
elif d1.getDay() != 0 and d2.getDay() == 0 and free_days != 2:
free_hours = get_elapsed_hours_of_day(t2)
# starts on saturday
elif d1.getDay() == 6 and free_days == 2:
free_hours = hours_in_range
# ends on saturday
elif d1.getDay() != 6 and d2.getDay() == 6 and free_days == 2:
free_hours = get_elapsed_hours_of_day(t2)

return free_hours


if __name__ == "__main__":
import pscript

Expand Down
64 changes: 46 additions & 18 deletions timetagger/app/front.py
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,16 @@ def _draw_stats(self, ctx, t1, t2, x1, y1, x2, y2, stat_period, hover):
fullwidth = x2 - x1 # * (sumcount_full / (t2 - t1)) # ** 0.5
fullheight = (y2 - y1) * (sumcount_full / (t2 - t1)) # ** 0.5

# Darken the colors for free days.
if stat_period == "1D":
workdays_setting = window.simplesettings.get("workdays")
is_free_day = (text == "Sat" and workdays_setting == 2) or (
text == "Sun" and workdays_setting >= 1
)
if is_free_day:
ctx.fillStyle = COLORS.button_text_disabled
ctx.fillRect(x1, y1, fullwidth, y2 - y1)

# Show amount of time spend on each tag
x = x1
for i in range(len(stats_list)):
Expand All @@ -2562,15 +2572,16 @@ def _draw_stats(self, ctx, t1, t2, x1, y1, x2, y2, stat_period, hover):
# Draw big text in stronger color if it is the timerange containing today

# Draw duration at the left
ctx.fillStyle = COLORS.prim1_clr if hover else COLORS.prim2_clr
fontsizeleft = bigfontsize * (0.7 if selected_tags else 0.9)
ctx.font = f"{fontsizeleft}px {FONT.default}"
ctx.textBaseline = "bottom"
ctx.textAlign = "left"
duration_text = dt.duration_string(sumcount_selected, False)
if selected_tags:
duration_text += " / " + dt.duration_string(sumcount_nominal, False)
ctx.fillText(duration_text, x1 + 10, y2 - ymargin)
if not stat_period == "1D" or sumcount_nominal > 0 or not is_free_day:
ctx.fillStyle = COLORS.prim1_clr if hover else COLORS.prim2_clr
fontsizeleft = bigfontsize * (0.7 if selected_tags else 0.9)
ctx.font = f"{fontsizeleft}px {FONT.default}"
ctx.textBaseline = "bottom"
ctx.textAlign = "left"
duration_text = dt.duration_string(sumcount_selected, False)
if selected_tags:
duration_text += " / " + dt.duration_string(sumcount_nominal, False)
ctx.fillText(duration_text, x1 + 10, y2 - ymargin)

# Draw time-range indication at the right
isnow = t1 < self._canvas.now() < t2
Expand Down Expand Up @@ -3417,14 +3428,28 @@ def _draw_container(self, ctx, total_time, x1, y1, x2, y2):
ctx.fillText(duration, x_ref_duration, ty)
# -- Row for target
tx, ty = x_ref_labels, ymid - 2 + npixels * 0.85

# Select the target that best matches the current time range
best_target = None
m = {"day": 24, "week": 168, "month": 720, "year": 8760}
for period, divisor in m.items():
free_days_per_week = window.simplesettings.get("workdays")
free_hours_in_range = dt.get_free_hours_in_range(t1, t2, free_days_per_week)
work_hours_in_range = self._hours_in_range - free_hours_in_range
for period in ["day", "week", "month", "year"]:
target_hours = self._current_targets.get(period, 0)
if target_hours <= 0:
continue
factor = self._hours_in_range / divisor

# hours in period -> "day": 24, "week": 168, "month": 720, "year": 8760
if period == "day":
work_hours_in_period = 24
elif period == "week":
work_hours_in_period = 168 - free_days_per_week * 24
elif period == "month": # ~4.33 weeks in a month
work_hours_in_period = 720 - free_days_per_week * 24 * 4.33
elif period == "year":
work_hours_in_period = 8760 - free_days_per_week * 24 * 52

factor = work_hours_in_range / work_hours_in_period
if factor > 0.93 or not best_target:
best_target = {
"period": period,
Expand All @@ -3440,13 +3465,16 @@ def _draw_container(self, ctx, total_time, x1, y1, x2, y2):
if best_target:
done_this_period = total_time
target_this_period = 3600 * best_target.hours * best_target.factor
perc = 100 * done_this_period / target_this_period
prefix = "" if 0.93 < best_target.factor < 1.034 else "~ "
ctx.fillText(
f"{best_target.period} target at {prefix}{perc:0.0f}%",
tx,
ty,
)
if target_this_period > 0:
perc = 100 * done_this_period / target_this_period
ctx.fillText(
f"{best_target.period} target at {prefix}{perc:0.0f}%",
tx,
ty,
)
else:
ctx.fillText("No target", tx, ty)
left = target_this_period - done_this_period
left_s = dt.duration_string(abs(left), False)
left_prefix = "left" if left >= 0 else "over"
Expand Down
1 change: 1 addition & 0 deletions timetagger/app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ def __init__(self):
}
self._synced_keys = {
"first_day_of_week": 1,
"workdays": 0,
"time_repr": "auto",
"duration_repr": "hms",
"today_snap_offset": "",
Expand Down
Loading