feat: span task pertama dihitung dari jam 08:00
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -182,14 +182,25 @@ def get_commits_today(repo_path: str, author: str, today: str) -> list[dict]:
|
||||
# ── Pro-rata ──────────────────────────────────────────────────────────────────
|
||||
|
||||
SINGLE_COMMIT_MINUTES = 30 # bobot minimum untuk task dengan 1 commit
|
||||
WORK_START_HOUR = 8 # jam mulai kerja
|
||||
|
||||
|
||||
def span_minutes(timestamps: list[datetime]) -> int:
|
||||
"""Menit antara commit pertama dan terakhir. Minimum 30 menit."""
|
||||
if len(timestamps) <= 1:
|
||||
return SINGLE_COMMIT_MINUTES
|
||||
def span_minutes(timestamps: list[datetime], extend_to_work_start: bool = False) -> int:
|
||||
"""
|
||||
Menit dari start sampai commit terakhir. Minimum 30 menit.
|
||||
Jika extend_to_work_start=True, start dihitung dari jam 08:00
|
||||
(berlaku untuk task yang punya commit paling awal di hari itu).
|
||||
"""
|
||||
first = min(timestamps)
|
||||
last = max(timestamps)
|
||||
|
||||
if extend_to_work_start:
|
||||
start = first.replace(hour=WORK_START_HOUR, minute=0, second=0, microsecond=0)
|
||||
else:
|
||||
start = first
|
||||
|
||||
return max(
|
||||
int((max(timestamps) - min(timestamps)).total_seconds() / 60),
|
||||
int((last - start).total_seconds() / 60),
|
||||
SINGLE_COMMIT_MINUTES,
|
||||
)
|
||||
|
||||
@@ -295,25 +306,40 @@ def main():
|
||||
|
||||
# 3. Hitung pro-rata berdasarkan time span
|
||||
keys = list(groups.keys())
|
||||
spans = [span_minutes(groups[k]["timestamps"]) for k in keys]
|
||||
|
||||
# Task yang punya commit paling awal di hari itu → spannya dihitung dari 08:00
|
||||
global_first_dt = min(min(groups[k]["timestamps"]) for k in keys)
|
||||
first_task_key = next(
|
||||
k for k in keys if global_first_dt in groups[k]["timestamps"]
|
||||
)
|
||||
|
||||
spans = [
|
||||
span_minutes(groups[k]["timestamps"], extend_to_work_start=(k == first_task_key))
|
||||
for k in keys
|
||||
]
|
||||
hours = distribute_hours(spans, total=float(TOTAL_HOURS))
|
||||
|
||||
# 4. Tampilkan preview
|
||||
print(f"\n{'─' * 75}")
|
||||
print(f" {'PROJECT':<20} {'TASK':<10} {'WAKTU':<13} {'SPAN':>6} {'JAM':>5} DESKRIPSI")
|
||||
print(f"{'─' * 75}")
|
||||
print(f"\n{'─' * 78}")
|
||||
print(f" {'PROJECT':<20} {'TASK':<10} {'WAKTU':<15} {'SPAN':>6} {'JAM':>5} DESKRIPSI")
|
||||
print(f"{'─' * 78}")
|
||||
|
||||
entries = []
|
||||
for key, h, span in zip(keys, hours, spans):
|
||||
g = groups[key]
|
||||
ts = sorted(g["timestamps"])
|
||||
if len(ts) == 1:
|
||||
waktu = ts[0].strftime("%H:%M") + " (1 commit)"
|
||||
g = groups[key]
|
||||
ts = sorted(g["timestamps"])
|
||||
is_first = (key == first_task_key)
|
||||
|
||||
if is_first:
|
||||
start_label = f"08:00→{ts[-1].strftime('%H:%M')}"
|
||||
elif len(ts) == 1:
|
||||
start_label = ts[0].strftime("%H:%M") + " (1x)"
|
||||
else:
|
||||
waktu = f"{ts[0].strftime('%H:%M')}–{ts[-1].strftime('%H:%M')}"
|
||||
span_label = f"{span}m" if span > SINGLE_COMMIT_MINUTES else "30m*"
|
||||
start_label = f"{ts[0].strftime('%H:%M')}–{ts[-1].strftime('%H:%M')}"
|
||||
|
||||
span_label = f"{span}m{'↑' if is_first else ''}" if span > SINGLE_COMMIT_MINUTES else "30m*"
|
||||
desc = "; ".join(dict.fromkeys(g["descriptions"]))
|
||||
print(f" {g['project']:<20} {str(g['task_id']):<10} {waktu:<13} {span_label:>6} {h:>5.2f} {desc[:30]}")
|
||||
print(f" {g['project']:<20} {str(g['task_id']):<10} {start_label:<15} {span_label:>6} {h:>5.2f} {desc[:28]}")
|
||||
entries.append({
|
||||
"odoo": {
|
||||
"name": desc,
|
||||
@@ -327,10 +353,10 @@ def main():
|
||||
"display": g,
|
||||
})
|
||||
|
||||
print(f"{'─' * 75}")
|
||||
print(f"{'─' * 78}")
|
||||
total_span = sum(spans)
|
||||
print(f" {'TOTAL':<20} {'':<10} {'':<13} {total_span:>5}m {sum(hours):>5.2f}")
|
||||
print(f" * single commit → minimum {SINGLE_COMMIT_MINUTES} menit")
|
||||
print(f" {'TOTAL':<20} {'':<10} {'':<15} {total_span:>5}m {sum(hours):>5.2f}")
|
||||
print(f" ↑ span dihitung dari 08:00 | * single commit → minimum {SINGLE_COMMIT_MINUTES} menit")
|
||||
|
||||
if not_found:
|
||||
print(f"\n {len(not_found)} commit diabaikan (task tidak ditemukan di Odoo)")
|
||||
|
||||
Reference in New Issue
Block a user