timeglance 1.0.0__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.
- timeglance-1.0.0/.dockerignore +9 -0
- timeglance-1.0.0/.github/dependabot.yml +13 -0
- timeglance-1.0.0/.github/workflows/bandit.yaml +22 -0
- timeglance-1.0.0/.github/workflows/pip-audit.yaml +22 -0
- timeglance-1.0.0/.github/workflows/pre-commit.yaml +25 -0
- timeglance-1.0.0/.github/workflows/release.yaml +68 -0
- timeglance-1.0.0/.github/workflows/tests.yaml +27 -0
- timeglance-1.0.0/.gitignore +17 -0
- timeglance-1.0.0/.pre-commit-config.yaml +16 -0
- timeglance-1.0.0/CHANGELOG.md +7 -0
- timeglance-1.0.0/CONTRIBUTING.md +43 -0
- timeglance-1.0.0/Dockerfile +29 -0
- timeglance-1.0.0/LICENSE +21 -0
- timeglance-1.0.0/PKG-INFO +112 -0
- timeglance-1.0.0/README.md +80 -0
- timeglance-1.0.0/SECURITY.md +14 -0
- timeglance-1.0.0/compose.yaml +29 -0
- timeglance-1.0.0/docs/configuration.md +46 -0
- timeglance-1.0.0/docs/control-panel.md +20 -0
- timeglance-1.0.0/docs/images/weekly.png +0 -0
- timeglance-1.0.0/docs/images/year.png +0 -0
- timeglance-1.0.0/docs/library.md +37 -0
- timeglance-1.0.0/docs/motivation.md +32 -0
- timeglance-1.0.0/docs/reference.md +61 -0
- timeglance-1.0.0/docs/wallpaper.md +13 -0
- timeglance-1.0.0/pyproject.toml +108 -0
- timeglance-1.0.0/src/timeglance/__init__.py +3 -0
- timeglance-1.0.0/src/timeglance/common/__init__.py +1 -0
- timeglance-1.0.0/src/timeglance/common/serve.py +507 -0
- timeglance-1.0.0/src/timeglance/common/wallpaper.py +194 -0
- timeglance-1.0.0/src/timeglance/common/web/app.js +420 -0
- timeglance-1.0.0/src/timeglance/common/web/favicon.ico +0 -0
- timeglance-1.0.0/src/timeglance/common/web/favicon.svg +14 -0
- timeglance-1.0.0/src/timeglance/common/web/icon.png +0 -0
- timeglance-1.0.0/src/timeglance/common/web/index.html +110 -0
- timeglance-1.0.0/src/timeglance/init.py +29 -0
- timeglance-1.0.0/src/timeglance/paths.py +32 -0
- timeglance-1.0.0/src/timeglance/weekly/__init__.py +1 -0
- timeglance-1.0.0/src/timeglance/weekly/build.py +253 -0
- timeglance-1.0.0/src/timeglance/weekly/config.yaml.in +66 -0
- timeglance-1.0.0/src/timeglance/weekly/templates/week.html.j2 +112 -0
- timeglance-1.0.0/src/timeglance/year/__init__.py +1 -0
- timeglance-1.0.0/src/timeglance/year/build.py +204 -0
- timeglance-1.0.0/src/timeglance/year/config.yaml.in +95 -0
- timeglance-1.0.0/src/timeglance/year/render.py +664 -0
- timeglance-1.0.0/src/timeglance/year/sources.py +150 -0
- timeglance-1.0.0/tests/examples/sample.ics +17 -0
- timeglance-1.0.0/tests/examples/weekly.yaml +18 -0
- timeglance-1.0.0/tests/examples/year_aligned.yaml +24 -0
- timeglance-1.0.0/tests/examples/year_packed.yaml +16 -0
- timeglance-1.0.0/tests/examples/year_sources.yaml +13 -0
- timeglance-1.0.0/tests/test_config.py +115 -0
- timeglance-1.0.0/tests/test_examples.py +64 -0
- timeglance-1.0.0/tests/test_sources.py +142 -0
- timeglance-1.0.0/tests/test_weekly.py +112 -0
- timeglance-1.0.0/tests/test_year.py +119 -0
- timeglance-1.0.0/uv.lock +1588 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Maintain dependencies for Python packages
|
|
4
|
+
- package-ecosystem: "uv"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
|
|
9
|
+
# Maintain dependencies for GitHub Actions
|
|
10
|
+
- package-ecosystem: "github-actions"
|
|
11
|
+
directory: "/"
|
|
12
|
+
schedule:
|
|
13
|
+
interval: "weekly"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: bandit
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: ["main*"]
|
|
6
|
+
push:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
bandit:
|
|
11
|
+
runs-on: ubuntu-24.04
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v7.0.1
|
|
14
|
+
- uses: actions/setup-python@v7
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- name: Install uv and sync dependencies
|
|
18
|
+
run: |
|
|
19
|
+
pip install uv
|
|
20
|
+
uv sync --frozen
|
|
21
|
+
- name: Run bandit
|
|
22
|
+
run: uv run bandit -r src/timeglance -c pyproject.toml
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: pip-audit
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: ["main*"]
|
|
6
|
+
push:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
pip-audit:
|
|
11
|
+
runs-on: ubuntu-24.04
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v7.0.1
|
|
14
|
+
- uses: actions/setup-python@v7
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- name: Install uv and sync dependencies
|
|
18
|
+
run: |
|
|
19
|
+
pip install uv
|
|
20
|
+
uv sync --frozen
|
|
21
|
+
- name: Run pip-audit
|
|
22
|
+
run: uv run pip-audit
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: pre-commit
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: ["main*"]
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pre-commit:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7.0.1
|
|
13
|
+
- uses: actions/setup-python@v7
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.13"
|
|
16
|
+
- name: Get python version
|
|
17
|
+
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
|
|
18
|
+
- uses: actions/cache@v6
|
|
19
|
+
with:
|
|
20
|
+
path: ~/.cache/pre-commit
|
|
21
|
+
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
|
|
22
|
+
- name: Install pre-commit
|
|
23
|
+
run: pip install pre-commit
|
|
24
|
+
- name: Run pre-commit
|
|
25
|
+
run: pre-commit run --all-files --show-diff-on-failure --color=always
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
# semantic-release pushes its `RELEASE: {version}` commit with a PAT, which re-triggers this
|
|
8
|
+
# workflow. That commit has nothing to test or release, so skip every job on it (the alternative,
|
|
9
|
+
# `[skip ci]` in the commit message, works too but is less explicit). On the RELEASE commit all
|
|
10
|
+
# jobs skip; `release` needs the skipped jobs, so it skips with them.
|
|
11
|
+
jobs:
|
|
12
|
+
pre-commit:
|
|
13
|
+
if: ${{ !startsWith(github.event.head_commit.message, 'RELEASE:') }}
|
|
14
|
+
uses: ./.github/workflows/pre-commit.yaml
|
|
15
|
+
tests:
|
|
16
|
+
if: ${{ !startsWith(github.event.head_commit.message, 'RELEASE:') }}
|
|
17
|
+
uses: ./.github/workflows/tests.yaml
|
|
18
|
+
release:
|
|
19
|
+
name: Release
|
|
20
|
+
needs: [pre-commit, tests] # a broken build never reaches the release step
|
|
21
|
+
if: ${{ !startsWith(github.event.head_commit.message, 'RELEASE:') }}
|
|
22
|
+
runs-on: ubuntu-24.04
|
|
23
|
+
concurrency:
|
|
24
|
+
group: ${{ github.workflow }}-release-${{ github.ref_name }}
|
|
25
|
+
cancel-in-progress: false
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write
|
|
28
|
+
outputs:
|
|
29
|
+
released: ${{ steps.release.outputs.released }}
|
|
30
|
+
version: ${{ steps.release.outputs.version }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v7.0.1
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
|
|
36
|
+
- name: Python Semantic Release
|
|
37
|
+
id: release
|
|
38
|
+
uses: python-semantic-release/python-semantic-release@v10
|
|
39
|
+
with:
|
|
40
|
+
github_token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
|
|
41
|
+
|
|
42
|
+
# Publish to PyPI only when a release was actually cut (gated on the action's structured
|
|
43
|
+
# `released` output, never log-grepping). It runs in the `pypi` environment, so the OIDC
|
|
44
|
+
# token - and thus publishing rights - is scoped to this job rather than the whole workflow.
|
|
45
|
+
# Trusted publishing means no API token, and gh-action-pypi-publish attaches sigstore
|
|
46
|
+
# attestations by default. Dormant until the first release (1.0.0) is cut.
|
|
47
|
+
publish:
|
|
48
|
+
name: Publish to PyPI
|
|
49
|
+
needs: release
|
|
50
|
+
if: needs.release.outputs.released == 'true'
|
|
51
|
+
runs-on: ubuntu-24.04
|
|
52
|
+
environment:
|
|
53
|
+
name: pypi
|
|
54
|
+
url: https://pypi.org/p/timeglance
|
|
55
|
+
permissions:
|
|
56
|
+
id-token: write
|
|
57
|
+
contents: read # checkout needs read access while the repo is private
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v7.0.1
|
|
60
|
+
with:
|
|
61
|
+
ref: ${{ needs.release.outputs.version }} # the tag semantic-release just pushed
|
|
62
|
+
- uses: actions/setup-python@v7
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.13"
|
|
65
|
+
- name: Build distributions
|
|
66
|
+
run: pip install uv && uv build
|
|
67
|
+
- name: Publish to PyPI
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: ["main*"]
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
tests:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7.0.1
|
|
13
|
+
- uses: actions/setup-python@v7
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.13"
|
|
16
|
+
- name: Install system libraries for WeasyPrint & CairoSVG
|
|
17
|
+
run: |
|
|
18
|
+
sudo apt-get update
|
|
19
|
+
sudo apt-get install -y --no-install-recommends \
|
|
20
|
+
libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 \
|
|
21
|
+
libffi8 shared-mime-info fonts-dejavu-core fonts-noto-core
|
|
22
|
+
- name: Install uv and sync dependencies
|
|
23
|
+
run: |
|
|
24
|
+
pip install uv
|
|
25
|
+
uv sync --frozen
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: uv run pytest
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
.venv/
|
|
4
|
+
.ruff_cache/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
dist/
|
|
7
|
+
weekly/out/
|
|
8
|
+
year/out/
|
|
9
|
+
state/
|
|
10
|
+
|
|
11
|
+
# Private working configs; scaffold them with `timeglance-init`. The year config can hold
|
|
12
|
+
# calendar share links / secrets; the weekly config is your personal schedule.
|
|
13
|
+
weekly/config.yaml
|
|
14
|
+
year/config.yaml
|
|
15
|
+
|
|
16
|
+
# Backups written before a "reset to template" (config.yaml.<stamp>.bak etc.)
|
|
17
|
+
*.yaml.*.bak
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.16.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/rbubley/mirrors-prettier
|
|
9
|
+
rev: v3.3.3
|
|
10
|
+
hooks:
|
|
11
|
+
- id: prettier
|
|
12
|
+
types_or: [markdown]
|
|
13
|
+
# CHANGELOG.md is generated by semantic-release; prettier would reformat it and fail
|
|
14
|
+
# the release commit's CI. `exclude` (not .prettierignore) also avoids prettier's
|
|
15
|
+
# "No matching files" error when a commit touches only the changelog.
|
|
16
|
+
exclude: ^CHANGELOG\.md$
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Patches welcome! Open an issue for bugs and ideas, or a PR for changes.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uv sync
|
|
9
|
+
pre-commit install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Hooks: **ruff** (lint + format) for Python, **prettier** for Markdown.
|
|
13
|
+
|
|
14
|
+
The code lives under `src/timeglance/` (`common`, `year`, `weekly` subpackages); packaged assets (web UI, Jinja
|
|
15
|
+
templates, the `config.yaml.in` files) ship inside it. Working data - each tool's `config.yaml` and `out/`, plus
|
|
16
|
+
shared `state/` - resolves from the current working directory (override with `TIMEGLANCE_HOME`).
|
|
17
|
+
|
|
18
|
+
## Before you push
|
|
19
|
+
|
|
20
|
+
`uv run pytest` and `pre-commit run --all-files` should pass. Commit messages follow
|
|
21
|
+
[Conventional Commits](https://www.conventionalcommits.org) (`feat:`, `fix:`, `docs:`, …) - they drive the automated
|
|
22
|
+
version bumps and changelog.
|
|
23
|
+
|
|
24
|
+
## Continuous integration
|
|
25
|
+
|
|
26
|
+
Every pull request runs **pre-commit** (ruff + prettier) and the **pytest** suite (with the WeasyPrint/CairoSVG
|
|
27
|
+
system libraries installed), plus **pip-audit** (dependency CVEs) and **bandit** (security lint). Pushes to `main`
|
|
28
|
+
run those again and then the release job.
|
|
29
|
+
|
|
30
|
+
## Releasing
|
|
31
|
+
|
|
32
|
+
Versioning is automated with [python-semantic-release](https://python-semantic-release.readthedocs.io), driven by
|
|
33
|
+
[Conventional Commits](https://www.conventionalcommits.org):
|
|
34
|
+
|
|
35
|
+
- `feat: …` → minor bump, `fix: …` → patch bump, breaking change (`feat!: …` or a `BREAKING CHANGE:` footer) →
|
|
36
|
+
major bump; `docs:` / `refactor:` / `perf:` show up in the changelog.
|
|
37
|
+
- An optional scope narrows the area, e.g. `feat(weekly): …` or `fix(year): …`.
|
|
38
|
+
|
|
39
|
+
On every push to `main`, CI runs semantic-release. When the new commits warrant a bump (per the rules above) it
|
|
40
|
+
raises the version in `pyproject.toml` (and `src/timeglance/__init__.py:__version__`), updates the changelog, tags,
|
|
41
|
+
and cuts a GitHub Release; otherwise it does nothing. This needs a `SEMANTIC_RELEASE_TOKEN` secret (a PAT with
|
|
42
|
+
`contents: write`). That same version bump then publishes the build to PyPI via
|
|
43
|
+
[trusted publishing](https://docs.pypi.org/trusted-publishers/) (OIDC, no API token) from the `pypi` environment.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# timeglance: renders weekly sheets (WeasyPrint) and the year planner (CairoSVG).
|
|
2
|
+
FROM python:3.13-slim-bookworm
|
|
3
|
+
|
|
4
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
5
|
+
UV_LINK_MODE=copy \
|
|
6
|
+
UV_COMPILE_BYTECODE=1 \
|
|
7
|
+
PATH="/app/.venv/bin:$PATH"
|
|
8
|
+
|
|
9
|
+
# Native libraries for WeasyPrint + CairoSVG, plus the fonts the templates reference.
|
|
10
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
11
|
+
libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0 \
|
|
12
|
+
libffi8 shared-mime-info fonts-dejavu-core fonts-noto-core \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
COPY --from=ghcr.io/astral-sh/uv:0.8.2 /uv /uvx /bin/
|
|
16
|
+
|
|
17
|
+
WORKDIR /app
|
|
18
|
+
|
|
19
|
+
# Install dependencies first (cached from the lockfile), then the project itself.
|
|
20
|
+
COPY pyproject.toml uv.lock README.md ./
|
|
21
|
+
RUN uv sync --frozen --no-dev --no-install-project
|
|
22
|
+
COPY src ./src
|
|
23
|
+
RUN uv sync --frozen --no-dev
|
|
24
|
+
|
|
25
|
+
EXPOSE 8753
|
|
26
|
+
|
|
27
|
+
# Default: the timeglance control panel (both tools). Override for a one-shot render, e.g.
|
|
28
|
+
# docker compose run --rm timeglance timeglance-weekly
|
|
29
|
+
CMD ["timeglance", "--host", "0.0.0.0"]
|
timeglance-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Danny W. Adair
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: timeglance
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Printable weekly routine sheets and a year-at-a-glance planner / desktop wallpaper.
|
|
5
|
+
Project-URL: Homepage, https://github.com/dannyadair/timeglance
|
|
6
|
+
Project-URL: Repository, https://github.com/dannyadair/timeglance
|
|
7
|
+
Project-URL: Issues, https://github.com/dannyadair/timeglance/issues
|
|
8
|
+
Author-email: "Danny W. Adair" <danny.adair@unfold.nz>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: calendar,ical,kde,pdf,planner,plasma,schedule,svg,wallpaper,yaml
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
19
|
+
Classifier: Topic :: Office/Business :: Scheduling
|
|
20
|
+
Requires-Python: >=3.13
|
|
21
|
+
Requires-Dist: cairosvg>=2.7.1
|
|
22
|
+
Requires-Dist: holidays>=0.105
|
|
23
|
+
Requires-Dist: icalendar>=7.3.0
|
|
24
|
+
Requires-Dist: jinja2>=3.1.6
|
|
25
|
+
Requires-Dist: pillow>=10.0.0
|
|
26
|
+
Requires-Dist: pypdfium2>=4.30.0
|
|
27
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
29
|
+
Requires-Dist: recurring-ical-events>=3.8.2
|
|
30
|
+
Requires-Dist: weasyprint>=62.3
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# timeglance
|
|
34
|
+
|
|
35
|
+
[](https://github.com/dannyadair/timeglance/blob/main/pyproject.toml)
|
|
36
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/tests.yaml)
|
|
37
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/pre-commit.yaml)
|
|
38
|
+
[](https://github.com/astral-sh/ruff)
|
|
39
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/pip-audit.yaml)
|
|
40
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/bandit.yaml)
|
|
41
|
+
[](https://github.com/dannyadair/timeglance/commits/main)
|
|
42
|
+
[](https://github.com/dannyadair/timeglance/blob/main/LICENSE)
|
|
43
|
+
|
|
44
|
+
<!-- Add once published to PyPI (and if you wire up these services), matching pdfbaker:
|
|
45
|
+
[](https://pypi.org/project/timeglance/)
|
|
46
|
+
[](https://pypistats.org/packages/timeglance)
|
|
47
|
+
[](https://github.com/dannyadair/timeglance/releases)
|
|
48
|
+
[](https://codecov.io/gh/dannyadair/timeglance)
|
|
49
|
+
[](https://scorecard.dev/viewer/?uri=github.com/dannyadair/timeglance)
|
|
50
|
+
-->
|
|
51
|
+
|
|
52
|
+
Config-driven time planning at a glance - one web UI controls two tools:
|
|
53
|
+
|
|
54
|
+
- **Weekly** - your routine sheet with activity layers. Print it or set it as a desktop wallpaper.
|
|
55
|
+
- **Year** - a wide year-at-a-glance planner, with layers fed by your own calendars, public holidays, and manually
|
|
56
|
+
added events. Print it or set it as a desktop wallpaper.
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
## Quick start (Docker)
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
docker compose run --rm timeglance timeglance-init # first run: scaffold year/ + weekly/ config.yaml
|
|
66
|
+
docker compose up # then open http://localhost:8753
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Config, outputs and scheduler state are bind-mounted under `./year`, `./weekly` and `./state`, so the UI edits
|
|
70
|
+
files on your host. Outputs are written as your host user — the compose file defaults to `1000:1000` (the usual
|
|
71
|
+
first Linux user), so it just works for most; if your uid differs, run `UID=$(id -u) GID=$(id -g) docker compose up`.
|
|
72
|
+
|
|
73
|
+
> Wallpaper-setting needs a live Plasma session, so it works only with the **local install** (next section), not in
|
|
74
|
+
> Docker; the container UI auto-hides it.
|
|
75
|
+
|
|
76
|
+
## Quick start (local)
|
|
77
|
+
|
|
78
|
+
Needs Python 3.13 and [uv](https://docs.astral.sh/uv/). WeasyPrint and CairoSVG load native libraries (Cairo, Pango,
|
|
79
|
+
GDK-PixBuf) at runtime; most Linux desktops already have these, but on a minimal or server install add them first
|
|
80
|
+
(Debian/Ubuntu):
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
sudo apt-get install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0
|
|
84
|
+
uv sync
|
|
85
|
+
uv run timeglance-init # scaffold year/ + weekly/ config.yaml from the templates
|
|
86
|
+
uv run timeglance # the control panel at http://localhost:8753
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Headless renders (each flag has a YAML equivalent):
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
uv run timeglance-year --theme light --layout vertical # -> year/out/
|
|
93
|
+
uv run timeglance-weekly --format both # -> weekly/out/
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Docs
|
|
97
|
+
|
|
98
|
+
- [Motivation](docs/motivation.md) - why this exists, and how it fits a GTD/calendar setup
|
|
99
|
+
- [The control panel](docs/control-panel.md) - preview, config editor, scheduler & activity log
|
|
100
|
+
- [Configuration](docs/configuration.md) - year & weekly YAML, calendar (`.ics`) sources
|
|
101
|
+
- [Reference](docs/reference.md) - every CLI flag and YAML key
|
|
102
|
+
- [Wallpaper](docs/wallpaper.md) - KDE Plasma, per-screen native-resolution rendering
|
|
103
|
+
- [Use as a library](docs/library.md) - import and render PNG/PDF/SVG from Python
|
|
104
|
+
- [Contributing](CONTRIBUTING.md) - setup, CI, releasing
|
|
105
|
+
|
|
106
|
+
## Limitations
|
|
107
|
+
|
|
108
|
+
- **Wallpaper-setting is host-only.** The container can't reach a Plasma session, so inside Docker the feature
|
|
109
|
+
self-hides and a scheduled "set wallpaper" renders the files but skips the set (logged as
|
|
110
|
+
`wallpaper skipped (no backend)`). Run the server/CLI on the host to set wallpapers.
|
|
111
|
+
- **Weekly blocks can't overlap in time.** Two activities over the same slot on the same day render on top of each
|
|
112
|
+
other, so only one reads clearly.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# timeglance
|
|
2
|
+
|
|
3
|
+
[](https://github.com/dannyadair/timeglance/blob/main/pyproject.toml)
|
|
4
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/tests.yaml)
|
|
5
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/pre-commit.yaml)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/pip-audit.yaml)
|
|
8
|
+
[](https://github.com/dannyadair/timeglance/actions/workflows/bandit.yaml)
|
|
9
|
+
[](https://github.com/dannyadair/timeglance/commits/main)
|
|
10
|
+
[](https://github.com/dannyadair/timeglance/blob/main/LICENSE)
|
|
11
|
+
|
|
12
|
+
<!-- Add once published to PyPI (and if you wire up these services), matching pdfbaker:
|
|
13
|
+
[](https://pypi.org/project/timeglance/)
|
|
14
|
+
[](https://pypistats.org/packages/timeglance)
|
|
15
|
+
[](https://github.com/dannyadair/timeglance/releases)
|
|
16
|
+
[](https://codecov.io/gh/dannyadair/timeglance)
|
|
17
|
+
[](https://scorecard.dev/viewer/?uri=github.com/dannyadair/timeglance)
|
|
18
|
+
-->
|
|
19
|
+
|
|
20
|
+
Config-driven time planning at a glance - one web UI controls two tools:
|
|
21
|
+
|
|
22
|
+
- **Weekly** - your routine sheet with activity layers. Print it or set it as a desktop wallpaper.
|
|
23
|
+
- **Year** - a wide year-at-a-glance planner, with layers fed by your own calendars, public holidays, and manually
|
|
24
|
+
added events. Print it or set it as a desktop wallpaper.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
## Quick start (Docker)
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
docker compose run --rm timeglance timeglance-init # first run: scaffold year/ + weekly/ config.yaml
|
|
34
|
+
docker compose up # then open http://localhost:8753
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Config, outputs and scheduler state are bind-mounted under `./year`, `./weekly` and `./state`, so the UI edits
|
|
38
|
+
files on your host. Outputs are written as your host user — the compose file defaults to `1000:1000` (the usual
|
|
39
|
+
first Linux user), so it just works for most; if your uid differs, run `UID=$(id -u) GID=$(id -g) docker compose up`.
|
|
40
|
+
|
|
41
|
+
> Wallpaper-setting needs a live Plasma session, so it works only with the **local install** (next section), not in
|
|
42
|
+
> Docker; the container UI auto-hides it.
|
|
43
|
+
|
|
44
|
+
## Quick start (local)
|
|
45
|
+
|
|
46
|
+
Needs Python 3.13 and [uv](https://docs.astral.sh/uv/). WeasyPrint and CairoSVG load native libraries (Cairo, Pango,
|
|
47
|
+
GDK-PixBuf) at runtime; most Linux desktops already have these, but on a minimal or server install add them first
|
|
48
|
+
(Debian/Ubuntu):
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
sudo apt-get install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0
|
|
52
|
+
uv sync
|
|
53
|
+
uv run timeglance-init # scaffold year/ + weekly/ config.yaml from the templates
|
|
54
|
+
uv run timeglance # the control panel at http://localhost:8753
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Headless renders (each flag has a YAML equivalent):
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
uv run timeglance-year --theme light --layout vertical # -> year/out/
|
|
61
|
+
uv run timeglance-weekly --format both # -> weekly/out/
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Docs
|
|
65
|
+
|
|
66
|
+
- [Motivation](docs/motivation.md) - why this exists, and how it fits a GTD/calendar setup
|
|
67
|
+
- [The control panel](docs/control-panel.md) - preview, config editor, scheduler & activity log
|
|
68
|
+
- [Configuration](docs/configuration.md) - year & weekly YAML, calendar (`.ics`) sources
|
|
69
|
+
- [Reference](docs/reference.md) - every CLI flag and YAML key
|
|
70
|
+
- [Wallpaper](docs/wallpaper.md) - KDE Plasma, per-screen native-resolution rendering
|
|
71
|
+
- [Use as a library](docs/library.md) - import and render PNG/PDF/SVG from Python
|
|
72
|
+
- [Contributing](CONTRIBUTING.md) - setup, CI, releasing
|
|
73
|
+
|
|
74
|
+
## Limitations
|
|
75
|
+
|
|
76
|
+
- **Wallpaper-setting is host-only.** The container can't reach a Plasma session, so inside Docker the feature
|
|
77
|
+
self-hides and a scheduled "set wallpaper" renders the files but skips the set (logged as
|
|
78
|
+
`wallpaper skipped (no backend)`). Run the server/CLI on the host to set wallpapers.
|
|
79
|
+
- **Weekly blocks can't overlap in time.** Two activities over the same slot on the same day render on top of each
|
|
80
|
+
other, so only one reads clearly.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Please report security issues privately via GitHub's
|
|
6
|
+
[private vulnerability reporting](https://github.com/dannyadair/timeglance/security/advisories/new)
|
|
7
|
+
rather than opening a public issue.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
timeglance renders local YAML/ICS config to SVG/PDF/PNG and, on KDE Plasma, sets the desktop
|
|
12
|
+
wallpaper via `kscreen-doctor`/`dbus-send`. It fetches the `.ics` URLs you configure and starts a
|
|
13
|
+
control-panel server bound to localhost (`0.0.0.0` only inside the container you run). It exposes no
|
|
14
|
+
other network service.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# The whole app is one service: the timeglance control panel.
|
|
2
|
+
#
|
|
3
|
+
# docker compose run --rm timeglance timeglance-init # first time: scaffold the configs
|
|
4
|
+
# docker compose up # open http://localhost:8753
|
|
5
|
+
#
|
|
6
|
+
# Config and output are bind-mounted, so the UI (and the scheduler) edit files on your
|
|
7
|
+
# host and results appear in ./*/out. One-shot CLI renders still work, e.g.
|
|
8
|
+
# docker compose run --rm timeglance timeglance-year
|
|
9
|
+
# docker compose run --rm timeglance timeglance-weekly --format both
|
|
10
|
+
#
|
|
11
|
+
# Wallpaper-setting is host-only (needs a Plasma session); it is auto-hidden in the UI here.
|
|
12
|
+
|
|
13
|
+
# Output and state are written as your host user (defaults to 1000:1000); override with
|
|
14
|
+
# UID=$(id -u) GID=$(id -g) docker compose up
|
|
15
|
+
x-user: &user "${UID:-1000}:${GID:-1000}"
|
|
16
|
+
|
|
17
|
+
services:
|
|
18
|
+
timeglance:
|
|
19
|
+
build: .
|
|
20
|
+
command: timeglance --host 0.0.0.0
|
|
21
|
+
user: *user
|
|
22
|
+
environment:
|
|
23
|
+
XDG_CACHE_HOME: /tmp # writable fontconfig cache when running as a non-root uid
|
|
24
|
+
ports:
|
|
25
|
+
- "8753:8753"
|
|
26
|
+
volumes:
|
|
27
|
+
- ./year:/app/year
|
|
28
|
+
- ./weekly:/app/weekly
|
|
29
|
+
- ./state:/app/state
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Working configs (`year/config.yaml`, `weekly/config.yaml`) are git-ignored (personal schedule / calendar
|
|
4
|
+
secrets); the packaged `config.yaml.in` files are annotated templates. Run `timeglance-init` to scaffold the
|
|
5
|
+
working configs from them. Precedence is **built-in defaults < YAML < CLI flags**, so you can comment defaults out
|
|
6
|
+
rather than delete them.
|
|
7
|
+
|
|
8
|
+
See the [Reference](reference.md) for the full list of flags and YAML keys.
|
|
9
|
+
|
|
10
|
+
## Year (`year/config.yaml`)
|
|
11
|
+
|
|
12
|
+
- **`range`** - `start`/`end` accept `today`, `today±N` (months), `year-start`, `year-end`, or `YYYY-MM`.
|
|
13
|
+
A shorter range renders larger. Or set a `span` preset instead of `start`/`end`: `this_year`,
|
|
14
|
+
`upcoming_year` (rolling 12 from this month), or `custom` with `before`/`after` month counts.
|
|
15
|
+
- **`holidays`** - auto-populated via the [`holidays`](https://pypi.org/project/holidays/) package
|
|
16
|
+
(`country` + `subdiv`, e.g. `NZ` / `WGN`).
|
|
17
|
+
- **`sources`** - external iCalendar feeds (see below).
|
|
18
|
+
- **`layers`** / **`events`** - a single day uses `date:`, a span uses `start:`+`end:` (inclusive); recurrence via
|
|
19
|
+
`repeat: yearly|weekly|monthly` or a full `rrule:` (RFC 5545 / dateutil).
|
|
20
|
+
|
|
21
|
+
### Calendar sources (.ics)
|
|
22
|
+
|
|
23
|
+
Let your existing calendars _be_ the editor. Any number of `.ics` feeds are merged into the event stream and mapped
|
|
24
|
+
to a layer:
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
sources:
|
|
28
|
+
- url: "https://calendar.proton.me/api/calendar/v1/url/…/calendar.ics" # a Proton share link
|
|
29
|
+
layer: birthdays
|
|
30
|
+
- path: "~/calendars/my-year.ics" # a local file
|
|
31
|
+
label: My Year # creates a layer if `layer:` is omitted
|
|
32
|
+
color: "#7A7F87"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Recurrences, `EXDATE`s and all-day/timed spans are expanded within the render window. URL feeds are TTL-cached
|
|
36
|
+
(`refresh:` seconds) under `state/ics-cache`, and a failed refresh falls back to the last good copy. Nice trick:
|
|
37
|
+
keep a private "year" calendar you never show online and only export here.
|
|
38
|
+
|
|
39
|
+
> **A share link is a secret.** Proton links embed a passphrase key. Keep them in your git-ignored `config.yaml`,
|
|
40
|
+
> never in the tracked `config.yaml.in`.
|
|
41
|
+
|
|
42
|
+
## Weekly (`weekly/config.yaml`)
|
|
43
|
+
|
|
44
|
+
`activities` (label + colour; `visible: false` hides one by default) and a flat list of `blocks`. Toggle any
|
|
45
|
+
activity in the UI (or with `--hide`/`--only`); comment out a single block to drop a one-off. `wallpaper:` tunes
|
|
46
|
+
the canvas `background`, `margin`, `shadow`, and `screens`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# The control panel
|
|
2
|
+
|
|
3
|
+
Open <http://localhost:8753>. Switch **Weekly / Year**, and within each, switch the view:
|
|
4
|
+
|
|
5
|
+
- **Preview** - live render with quick controls (year: theme, layout, week start, range, size, layer toggles;
|
|
6
|
+
weekly: paper, activity toggles). **Export** (PNG / PDF) and **Set as wallpaper** act on the current controls.
|
|
7
|
+
- **Config** - the tool's full YAML in an editor. **Save** validates and writes it; **Reset to template** restores
|
|
8
|
+
the annotated template. This is the source of truth - anything not in the quick controls lives here.
|
|
9
|
+
|
|
10
|
+
Two shared, app-wide views sit alongside them:
|
|
11
|
+
|
|
12
|
+
- **Schedule** - a daily re-render loop: pick a time and which tools to refresh (optionally setting the wallpaper),
|
|
13
|
+
**Start / Stop / Run now**. With more than one screen you can assign each tool to specific outputs (e.g. weekly on
|
|
14
|
+
the laptop, year on the ultrawide); leave all ticked to target every screen. Runs are isolated so one failing feed
|
|
15
|
+
can't sink the rest.
|
|
16
|
+
- **Log** - a persistent activity log of every render, export and wallpaper change, scheduled or manual (live
|
|
17
|
+
previews aren't logged). **Clear log** wipes it (with confirmation). Schedule config and the log persist under
|
|
18
|
+
`state/`.
|
|
19
|
+
|
|
20
|
+
The YAML is re-read on every request, so external edits show up on reload.
|
|
Binary file
|
|
Binary file
|