git: 674c4d4fee24 - main - misc/auto-tempo: Manage Jira Tempo Timesheets worklogs from a text file
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 03 Jul 2026 04:26:08 UTC
The branch main has been updated by eugen:
URL: https://cgit.FreeBSD.org/ports/commit/?id=674c4d4fee24a7dbaaa18142874a3e1d76c40949
commit 674c4d4fee24a7dbaaa18142874a3e1d76c40949
Author: Vyacheslav Olkhovchenkov <slw@zxy.spb.ru>
AuthorDate: 2026-07-03 04:21:13 +0000
Commit: Eugene Grosbein <eugen@FreeBSD.org>
CommitDate: 2026-07-03 04:25:29 +0000
misc/auto-tempo: Manage Jira Tempo Timesheets worklogs from a text file
AutoTempo manages Jira worklogs through the Tempo Timesheets REST API
using a simple, version-controllable text file.
It can generate a monthly worklog template pre-populated
with working days, expand keyword shortcuts and recurring entries
defined in config.toml, validate daily totals, and apply worklogs
idempotently to Jira. A helper can draft worklogs from local
git commit history.
Targets Tempo Timesheets on Jira Server/Data Center;
authentication uses a Jira personal access token.
The tool is run from a directory containing a config.toml file.
PR: 296427
---
misc/Makefile | 1 +
misc/auto-tempo/Makefile | 35 +++++++++
misc/auto-tempo/distinfo | 3 +
misc/auto-tempo/files/patch-autotempo.py | 128 +++++++++++++++++++++++++++++++
misc/auto-tempo/pkg-descr | 10 +++
5 files changed, 177 insertions(+)
diff --git a/misc/Makefile b/misc/Makefile
index 3f4d323dab1c..d28378ad36ba 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -21,6 +21,7 @@
SUBDIR += asbutton
SUBDIR += asr-manpages
SUBDIR += astc-encoder
+ SUBDIR += auto-tempo
SUBDIR += bb
SUBDIR += bbjd
SUBDIR += bdelta
diff --git a/misc/auto-tempo/Makefile b/misc/auto-tempo/Makefile
new file mode 100644
index 000000000000..aa94087889ad
--- /dev/null
+++ b/misc/auto-tempo/Makefile
@@ -0,0 +1,35 @@
+PORTNAME= auto-tempo
+DISTVERSION= g20250902
+CATEGORIES= misc python
+PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX}
+
+MAINTAINER= slw@zxy.spb.ru
+COMMENT= Manage Jira Tempo Timesheets worklogs from a text file
+WWW= https://github.com/atteo/Auto-Tempo
+
+LICENSE= APACHE20
+
+RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}requests>0:www/py-requests@${PY_FLAVOR} \
+ ${PYTHON_PKGNAMEPREFIX}toml>0:textproc/py-toml@${PY_FLAVOR} \
+ git:devel/git
+
+USES= python shebangfix
+SHEBANG_FILES= autotempo.py
+
+USE_PYTHON= flavors
+USE_GITHUB= yes
+
+GH_ACCOUNT= atteo
+GH_PROJECT= Auto-Tempo
+GH_TAGNAME= 0eb4159
+
+NO_ARCH= yes
+NO_BUILD= yes
+
+PLIST_FILES= bin/autotempo
+
+do-install:
+ ${INSTALL_SCRIPT} ${WRKSRC}/autotempo.py \
+ ${STAGEDIR}${PREFIX}/bin/autotempo
+
+.include <bsd.port.mk>
diff --git a/misc/auto-tempo/distinfo b/misc/auto-tempo/distinfo
new file mode 100644
index 000000000000..365b90ef9492
--- /dev/null
+++ b/misc/auto-tempo/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1782911686
+SHA256 (atteo-Auto-Tempo-g20250902-0eb4159_GH0.tar.gz) = afa9c97cb130a299fd485b164d74ed7643b206c8f79307d1f3ea9b9f697818ea
+SIZE (atteo-Auto-Tempo-g20250902-0eb4159_GH0.tar.gz) = 9111
diff --git a/misc/auto-tempo/files/patch-autotempo.py b/misc/auto-tempo/files/patch-autotempo.py
new file mode 100644
index 000000000000..3407d1037d8a
--- /dev/null
+++ b/misc/auto-tempo/files/patch-autotempo.py
@@ -0,0 +1,128 @@
+Local extensions plus a crash fix, carried as a port patch.
+
+Bug fix (upstreamable): query the days/search endpoint for the authenticated
+user instead of a user key hardcoded in the source. Without it the response
+is empty and the tool aborts with "list index out of range".
+
+Local feature: derive each working day's required hours from a configurable
+contract value (daily_hours) instead of a hardcoded 8, and shorten a working
+day by preholiday_reduction when the next calendar day is a Tempo HOLIDAY.
+Tempo is used only as the holiday calendar; the work scheme is not required.
+
+--- autotempo.py.orig
++++ autotempo.py
+@@ -25,6 +25,8 @@
+ API_TOKEN = config["JIRA"]["API_TOKEN"]
+ keywords = config["keyword"]
+ EMAIL = config["user"]["email"]
++ DAILY_HOURS = config.get("user", {}).get("daily_hours", 8.0)
++ PREHOLIDAY_REDUCTION = config.get("user", {}).get("preholiday_reduction", 1.0)
+ except KeyError as e:
+ print(f"Missing configuration key: {e}")
+ exit(1)
+@@ -36,6 +38,8 @@
+
+ [user]
+ email = "your-email@example.com" # Your email address (used for git inspect)
++daily_hours = 8.0 # Contract daily working hours
++preholiday_reduction = 1.0 # Hours subtracted from a pre-holiday day
+
+ # Define keywords for common tasks (optional)
+ [keyword.meeting]
+@@ -111,25 +115,41 @@
+ data = {
+ "from": start_date,
+ "to": end_date,
+- "userKeys": ["JIRAUSER55710"]
++ "userKeys": [WORKER]
+ }
+-
++
+ try:
+ datetime.datetime.strptime(start_date, "%Y-%m-%d")
+ datetime.datetime.strptime(end_date, "%Y-%m-%d")
+ except ValueError as e:
+ print(f"Invalid date format: {e}")
+- return set()
++ return {}
+
+ response = requests.post(url, headers=headers, data=json.dumps(data))
+
+- if response.status_code == 200:
+- days_info = response.json()
+- working_days = {day['date'] for day in days_info[0]['days'] if day['type'] == "WORKING_DAY"}
+- return working_days
+- else:
++ if response.status_code != 200:
+ raise FatalError(f"Failed to retrieve working days: {response.status_code} {response.text}", response)
+
++ days_info = response.json()
++ if not days_info:
++ raise FatalError(f"Tempo returned no day data for {WORKER}")
++
++ days = days_info[0]['days']
++ holidays = {d['date'] for d in days if d['type'] == "HOLIDAY"}
++
++ required = {}
++ for d in days:
++ if d['type'] != "WORKING_DAY":
++ continue
++ date = d['date']
++ hours = DAILY_HOURS
++ next_day = (datetime.datetime.strptime(date, "%Y-%m-%d")
++ + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
++ if next_day in holidays:
++ hours -= PREHOLIDAY_REDUCTION
++ required[date] = round(hours, 2)
++ return required
++
+ def get_existing_worklogs_for_date(date):
+ url = f"{JIRA_URL}/rest/tempo-timesheets/4/worklogs/search"
+ headers = {
+@@ -261,6 +281,7 @@
+ # Generate template
+ template_lines = []
+ for day in sorted(working_days):
++ base = working_days[day]
+ day_dt = datetime.datetime.strptime(day, "%Y-%m-%d")
+ day_of_week_name = day_dt.strftime('%A')
+
+@@ -284,14 +305,14 @@
+ print(f"Warning: could not parse hours from automatic worklog: '{worklog_line}'. Skipping for hour calculation.")
+
+ if day_lines:
+- remaining_hours = 8.0 - total_auto_hours
++ remaining_hours = base - total_auto_hours
+ if remaining_hours > 0:
+ day_lines.append(f"{day} {remaining_hours:.1f} jira-ticket \"comment\"")
+ elif remaining_hours < 0:
+- print(f"Warning: total hours for non-overtime automatic worklogs on {day} ({day_of_week_name}) exceeds 8 hours.")
++ print(f"Warning: non-overtime automatic worklogs on {day} ({day_of_week_name}) exceed {base} hours.")
+ template_lines.extend(day_lines)
+ else:
+- template_lines.append(f"{day} 8.0 jira-ticket \"comment\"")
++ template_lines.append(f"{day} {base} jira-ticket \"comment\"")
+
+ # Output template
+ template_content = (
+@@ -362,8 +383,9 @@
+ for date in sorted(all_dates):
+ total_hours = daily_hours.get(date, 0)
+ if date in working_days:
+- if total_hours != 8:
+- raise ValueError(f"Total non-overtime logged hours for {date} is {total_hours}, which is not equal to 8. Stopping worklog application.")
++ expected = working_days[date]
++ if total_hours != expected:
++ raise ValueError(f"Total non-overtime logged hours for {date} is {total_hours}, expected {expected}. Stopping worklog application.")
+ valid_dates.append(date)
+ else:
+ if total_hours != 0:
+@@ -518,7 +540,7 @@
+ worklog_lines = []
+ for commit in commits:
+ commit_hash, date, message = commit.split(" ", 2)
+- worklog_lines.append(f"{date} 8.0 {commit_hash} \"{message}\"")
++ worklog_lines.append(f"{date} {DAILY_HOURS} {commit_hash} \"{message}\"")
+
+ # Output worklog
+ worklog_content = "\n".join(worklog_lines)
diff --git a/misc/auto-tempo/pkg-descr b/misc/auto-tempo/pkg-descr
new file mode 100644
index 000000000000..daf30f098003
--- /dev/null
+++ b/misc/auto-tempo/pkg-descr
@@ -0,0 +1,10 @@
+AutoTempo manages Jira worklogs through the Tempo Timesheets REST API using a
+simple, version-controllable text file. It can generate a monthly worklog
+template pre-populated with working days, expand keyword shortcuts and
+recurring entries defined in config.toml, validate daily totals, and apply
+worklogs idempotently to Jira. A helper can draft worklogs from local git
+commit history.
+
+Targets Tempo Timesheets on Jira Server/Data Center; authentication uses a
+Jira personal access token. The tool is run from a directory containing a
+config.toml file.