Combined timesheet

Accepted total
{{ format_seconds(accepted) }}
Suggested, not counted
{{ format_seconds(suggested) }}
Export monthly report
Add manual time
Upload mobile-provider call log (PDF)

Uploads are extracted and retained for review. A provider-specific parser is added after a redacted sample statement is available.

Days

{% for day in days %}

{{ day.date.strftime('%a %d %b %Y') }}

accepted {{ format_seconds(day.accepted) }} · suggested {{ format_seconds(day.suggested) }}{% if day.commits %} · {{ day.commits|length }} commit{{ 's' if day.commits|length != 1 }}{% endif %} {% if day.suggested %}
{% endif %}
{% if day.entries %}
Show {{ day.entries|length }} entr{{ 'y' if day.entries|length == 1 else 'ies' }}
{% for entry in day.entries %} {% endfor %}
StartEndSourceTask / projectDurationStatusActions
{{ entry.start_at.astimezone(display_tz).strftime('%H:%M') }}{{ entry.end_at.astimezone(display_tz).strftime('%H:%M') }}{{ entry.source }}{% if entry.kind == 'zed_inferred' %}
inferred{% if overlaps_accepted(entry) %}; overlaps accepted time{% endif %}{% endif %}
{{ entry.task or '—' }}{% if entry.project_slug %}
{{ entry.project_slug }}{% endif %}
{{ format_seconds(entry.duration_seconds) }}{{ entry.status }}{% if entry.status == 'suggested' %}
{% endif %}{% if entry.status != 'rejected' %}
{% endif %}
Edit
{% else %}

No tracked entries this day.

{% endif %} {% if day.commits %}
Git commits ({{ day.commits|length }})
{% for commit in day.commits %} {% endfor %}
TimeProjectAuthorCommit
{{ commit.occurred_at.astimezone(display_tz).strftime('%H:%M') }}{{ commit.project_slug }}{{ commit.payload.get('author', '') }}{{ (commit.payload.get('sha', '') or '')[:7] }} {{ commit.payload.get('subject', '') }}
{% endif %}
{% else %}

No entries yet. Sync a tracker or add manual time.

{% endfor %}