Skip to content

Commit 5cb3aa3

Browse files
author
Oleksandr Korol
committed
Return time tracking logic
1 parent e92d170 commit 5cb3aa3

File tree

2 files changed

+77
-16
lines changed

2 files changed

+77
-16
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ This [pre-commit](https://pre-commit.com/) hook transforms your Git commit messa
44

55
If your branch name contains a [Jira issue key](https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html) such as `ABC-123`, the hook will automatically format your commit message into a Jira smart commit:
66

7-
| Command | Log entry |
8-
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
9-
| git commit -m "Add extra validation" | ABC-123 #comment Add validation <br><br> _Effect:_ Posts a comment to issue Add extra validation |
10-
| git commit -m "Add validation <br> Due to request added extra validation to ...." | ABC-123 #comment Add validation<br><br>_Effect:_ Posts a comment to the issue |
7+
| Command | Log entry |
8+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9+
| git commit -m "release the kraken." | ABC-123 Release the kraken<br><br>ABC-123 #time 0w 0d 2h 8m Release the kraken<br><br>_Effect:_ Logs the time since your last commit on any branch in the Work Log tab. |
10+
| git commit -m "Release the kraken<br><br>A kraken lives in dark depths, usually a sunken rift or a cavern filled with detritus, treasure, and wrecked ships." | ABC-123 Release the kraken<br><br>ABC-123 #comment A kraken lives in dark depths, usually a sunken rift or a cavern filled with detritus, treasure, and wrecked ships.<br><br>ABC-123 #time 0w 0d 2h 8m Release the kraken<br><br>_Effect:_ Posts a comment to the Jira issue and logs the time since your last commit in the Work Log tab. |
1111

1212
If the branch name does not contain a Jira issue key, the commit message is not modified. The time logged takes into account non-working hours such as lunch breaks and nights.
1313

@@ -27,8 +27,8 @@ Add the following to your `.pre-commit-config.yaml` file:
2727

2828
```yaml
2929
repos:
30-
- repo: https://github.com/Dufran/auto-smart-commit
31-
rev: v0.0.1
30+
- repo: https://github.com/radix-ai/auto-smart-commit
31+
rev: v1.0.2
3232
hooks:
3333
- id: auto-smart-commit
3434
```

auto-smart-commit.py

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
#!/usr/bin/env python3
1+
#!/usr/bin/env python
22

33
import re
44
import sys
5+
from datetime import datetime
6+
from math import floor
57
from subprocess import check_output
68
from typing import NoReturn, Optional
79

@@ -26,14 +28,68 @@ def extract_jira_issue_key(message: str) -> Optional[str]:
2628
return None
2729

2830

31+
def last_commit_datetime() -> datetime:
32+
# https://git-scm.com/docs/git-log#_pretty_formats
33+
git_log = "git log -1 --branches --format=%aI"
34+
author = run_command("git config user.email")
35+
last_author_datetime = run_command(f"{git_log} --author={author}") or run_command(git_log)
36+
if "+" in last_author_datetime:
37+
return datetime.strptime(last_author_datetime.split("+")[0], "%Y-%m-%dT%H:%M:%S")
38+
return datetime.now()
39+
40+
41+
def num_lunches(start: datetime, end: datetime) -> int:
42+
n = (end.date() - start.date()).days - 1
43+
if start < start.replace(hour=12, minute=0, second=0):
44+
n += 1
45+
if end > end.replace(hour=12, minute=45, second=0):
46+
n += 1
47+
return max(n, 0)
48+
49+
50+
def num_nights(start: datetime, end: datetime) -> int:
51+
n = (end.date() - start.date()).days - 1
52+
if start < start.replace(hour=1, minute=0, second=0):
53+
n += 1
54+
if end > end.replace(hour=5, minute=0, second=0):
55+
n += 1
56+
return max(n, 0)
57+
58+
59+
def time_worked_on_commit() -> Optional[str]:
60+
now = datetime.now()
61+
last = last_commit_datetime()
62+
# Determine the number of minutes worked on this commit as the number of
63+
# minutes since the last commit minus the lunch breaks and nights.
64+
working_hours_per_day = 8
65+
working_days_per_week = 5
66+
minutes = max(
67+
round((now - last).total_seconds() / 60)
68+
- num_nights(last, now) * (24 - working_hours_per_day) * 60
69+
- num_lunches(last, now) * 45,
70+
0,
71+
)
72+
# Convert the number of minutes worked to working weeks, days, hours,
73+
# minutes.
74+
if minutes > 0:
75+
hours = floor(minutes / 60)
76+
minutes -= hours * 60
77+
days = floor(hours / working_hours_per_day)
78+
hours -= days * working_hours_per_day
79+
weeks = floor(days / working_days_per_week)
80+
days -= weeks * working_days_per_week
81+
return f"{weeks}w {days}d {hours}h {minutes}m"
82+
return None
83+
84+
2985
def main() -> NoReturn:
30-
# ? https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html
86+
# https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html
3187
# Exit if the branch name does not contain a Jira issue key.
3288
git_branch_name = current_git_branch_name()
3389
jira_issue_key = extract_jira_issue_key(git_branch_name)
3490
if not jira_issue_key:
3591
sys.exit(0)
36-
# * Read the commit message.
92+
# Read the commit message.
3793
commit_msg_filepath = sys.argv[1]
3894
with open(commit_msg_filepath, "r") as f:
3995
commit_msg = f.read()
@@ -43,15 +99,20 @@ def main() -> NoReturn:
4399
commit_subject = f"{commit_subject[:1].upper()}{commit_subject[1:]}"
44100
commit_subject = re.sub(r"\.+$", "", commit_subject)
45101
commit_body = None if len(commit_elements) == 1 else commit_elements[1].strip()
46-
# * Build the new commit message:
47-
# * 1. If there is a body, turn it into a comment on the issue.
102+
# Build the new commit message:
103+
# 1. If there is a body, turn it into a comment on the issue.
48104
if "#comment" not in commit_msg and commit_body:
49-
commit_body = f"{jira_issue_key} #comment {commit_subject}\n\n{commit_body}"
50-
# * 2. Make sure the subject starts with a Jira issue key.
105+
commit_body = f"{jira_issue_key} #comment {commit_body}"
106+
# 2. Add the time worked to the Work Log in the commit body.
107+
work_time = time_worked_on_commit()
108+
if "#time" not in commit_msg and work_time:
109+
work_log = f"{jira_issue_key} #time {work_time} {commit_subject}"
110+
commit_body = f"{commit_body}\n\n{work_log}" if commit_body else work_log
111+
# 3. Make sure the subject starts with a Jira issue key.
51112
if not extract_jira_issue_key(commit_subject):
52-
commit_subject = f"{jira_issue_key} {commit_body}"
53-
#! Override commit message.
54-
commit_msg = f"{commit_subject}" if commit_body else commit_subject
113+
commit_subject = f"{jira_issue_key} {commit_subject}"
114+
# Override commit message.
115+
commit_msg = f"{commit_subject}\n\n{commit_body}" if commit_body else commit_subject
55116
with open(commit_msg_filepath, "w") as f:
56117
f.write(commit_msg)
57118
sys.exit(0)

0 commit comments

Comments
 (0)