freeagent-cli 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/PKG-INFO +1 -1
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/pyproject.toml +1 -1
- freeagent_cli-0.2.1/src/freeagent_cli/__init__.py +1 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/src/freeagent_cli/cli.py +23 -5
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/uv.lock +1 -1
- freeagent_cli-0.2.0/src/freeagent_cli/__init__.py +0 -1
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/.gitignore +0 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/LICENSE +0 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/README.md +0 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/src/freeagent_cli/api.py +0 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/src/freeagent_cli/auth.py +0 -0
- {freeagent_cli-0.2.0 → freeagent_cli-0.2.1}/src/freeagent_cli/config.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.1"
|
|
@@ -37,6 +37,22 @@ def parse_hours(s: str) -> float:
|
|
|
37
37
|
raise ValueError(f"Cannot parse duration {s!r}; try 1.5, 90m, 1h30m, or 1:30")
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def format_hours(value) -> str:
|
|
41
|
+
"""Format a decimal-hours value as 40m / 1h / 1h30m, rounding to the nearest minute."""
|
|
42
|
+
try:
|
|
43
|
+
total_min = round(float(value) * 60)
|
|
44
|
+
except (TypeError, ValueError):
|
|
45
|
+
return str(value)
|
|
46
|
+
if total_min == 0:
|
|
47
|
+
return "0m"
|
|
48
|
+
h, m = divmod(total_min, 60)
|
|
49
|
+
if h == 0:
|
|
50
|
+
return f"{m}m"
|
|
51
|
+
if m == 0:
|
|
52
|
+
return f"{h}h"
|
|
53
|
+
return f"{h}h{m}m"
|
|
54
|
+
|
|
55
|
+
|
|
40
56
|
def _resolve(items: list[dict], query: str, label: str) -> dict:
|
|
41
57
|
if query.startswith("http"):
|
|
42
58
|
match = [i for i in items if i["url"] == query]
|
|
@@ -73,9 +89,9 @@ def _pick_task(tasks: list[dict], task_q: str | None, project_name: str) -> dict
|
|
|
73
89
|
@click.group(epilog="""
|
|
74
90
|
\b
|
|
75
91
|
Typical flow:
|
|
76
|
-
freeagent-cli
|
|
92
|
+
freeagent-cli recent # check what you've already logged (avoids duplicates)
|
|
77
93
|
freeagent-cli log <project> <duration> [comment] [--task <name>] [--dry-run]
|
|
78
|
-
freeagent-cli
|
|
94
|
+
freeagent-cli projects # first-time / discovery: projects + tasks
|
|
79
95
|
|
|
80
96
|
\b
|
|
81
97
|
Examples:
|
|
@@ -209,6 +225,8 @@ def log(project_q, duration, comment_parts, task_q, date_, dry_run):
|
|
|
209
225
|
freeagent-cli log Acme 1h30m "fixed the thing"
|
|
210
226
|
freeagent-cli log Acme 90m fixed the thing # comment without quotes
|
|
211
227
|
freeagent-cli log "Big Co" 1.5 --task Coding --date 2026-05-01
|
|
228
|
+
|
|
229
|
+
Tip: run `freeagent-cli recent` first to avoid duplicate entries.
|
|
212
230
|
"""
|
|
213
231
|
try:
|
|
214
232
|
hours = parse_hours(duration)
|
|
@@ -222,7 +240,7 @@ def log(project_q, duration, comment_parts, task_q, date_, dry_run):
|
|
|
222
240
|
dated_on = date_ or _dt.date.today().isoformat()
|
|
223
241
|
|
|
224
242
|
if dry_run:
|
|
225
|
-
click.echo(f"DRY RUN — would submit {hours}
|
|
243
|
+
click.echo(f"DRY RUN — would submit {format_hours(hours)} on {dated_on} → {project['name']} / {task['name']}")
|
|
226
244
|
click.echo(f" project: {project['url']}")
|
|
227
245
|
click.echo(f" task: {task['url']}")
|
|
228
246
|
if comment:
|
|
@@ -235,7 +253,7 @@ def log(project_q, duration, comment_parts, task_q, date_, dry_run):
|
|
|
235
253
|
dated_on=dated_on, hours=hours, comment=comment,
|
|
236
254
|
)
|
|
237
255
|
ts = result["timeslips"][0] if "timeslips" in result else result.get("timeslip", result)
|
|
238
|
-
click.echo(f"Submitted {hours}
|
|
256
|
+
click.echo(f"Submitted {format_hours(hours)} on {dated_on} → {project['name']} / {task['name']}")
|
|
239
257
|
if isinstance(ts, dict) and "url" in ts:
|
|
240
258
|
click.echo(ts["url"])
|
|
241
259
|
|
|
@@ -265,7 +283,7 @@ def recent(limit, days, all_users):
|
|
|
265
283
|
pname = proj["name"] if isinstance(proj, dict) else "?"
|
|
266
284
|
tname = task_["name"] if isinstance(task_, dict) else "?"
|
|
267
285
|
comment = (s.get("comment") or "").replace("\n", " ")
|
|
268
|
-
click.echo(f"{s.get('dated_on','?')}\t{s.get('hours'
|
|
286
|
+
click.echo(f"{s.get('dated_on','?')}\t{format_hours(s.get('hours'))}\t{pname}\t{tname}\t{comment}")
|
|
269
287
|
|
|
270
288
|
|
|
271
289
|
if __name__ == "__main__":
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|