stanwell 0.1.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.
- stanwell-0.1.0/.github/workflows/ci.yml +39 -0
- stanwell-0.1.0/.github/workflows/docs.yml +41 -0
- stanwell-0.1.0/.github/workflows/release.yml +50 -0
- stanwell-0.1.0/.gitignore +28 -0
- stanwell-0.1.0/.readthedocs.yaml +16 -0
- stanwell-0.1.0/CHANGELOG.md +23 -0
- stanwell-0.1.0/LICENSE +21 -0
- stanwell-0.1.0/Makefile +31 -0
- stanwell-0.1.0/PKG-INFO +17 -0
- stanwell-0.1.0/README.md +76 -0
- stanwell-0.1.0/docs/_ext/cli_commands.py +64 -0
- stanwell-0.1.0/docs/_static/stanwell_favicon.png +0 -0
- stanwell-0.1.0/docs/_static/stanwell_logo.png +0 -0
- stanwell-0.1.0/docs/api/index.rst +11 -0
- stanwell-0.1.0/docs/changelog.rst +2 -0
- stanwell-0.1.0/docs/cli.rst +22 -0
- stanwell-0.1.0/docs/conf.py +79 -0
- stanwell-0.1.0/docs/configuration.rst +78 -0
- stanwell-0.1.0/docs/dashboard.rst +55 -0
- stanwell-0.1.0/docs/getting_started.rst +139 -0
- stanwell-0.1.0/docs/index.rst +56 -0
- stanwell-0.1.0/docs/toggl.rst +248 -0
- stanwell-0.1.0/pyproject.toml +91 -0
- stanwell-0.1.0/src/stanwell/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/analysis/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/analysis/projections.py +167 -0
- stanwell-0.1.0/src/stanwell/analysis/scoring.py +231 -0
- stanwell-0.1.0/src/stanwell/analysis/tag_analysis.py +579 -0
- stanwell-0.1.0/src/stanwell/analysis/working_patterns.py +369 -0
- stanwell-0.1.0/src/stanwell/analysis/working_time.py +496 -0
- stanwell-0.1.0/src/stanwell/configuration.py +107 -0
- stanwell-0.1.0/src/stanwell/data_repository/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/data_repository/data_coverage.py +290 -0
- stanwell-0.1.0/src/stanwell/data_repository/data_util.py +45 -0
- stanwell-0.1.0/src/stanwell/data_repository/database_schema.py +172 -0
- stanwell-0.1.0/src/stanwell/data_repository/db.py +89 -0
- stanwell-0.1.0/src/stanwell/data_repository/projects.py +127 -0
- stanwell-0.1.0/src/stanwell/data_repository/time_entries.py +183 -0
- stanwell-0.1.0/src/stanwell/py.typed +0 -0
- stanwell-0.1.0/src/stanwell/toggl/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/toggl/client.py +642 -0
- stanwell-0.1.0/src/stanwell/toggl/constants.py +12 -0
- stanwell-0.1.0/src/stanwell/toggl/exceptions.py +60 -0
- stanwell-0.1.0/src/stanwell/toggl/normalisation.py +219 -0
- stanwell-0.1.0/src/stanwell/toggl/sync.py +969 -0
- stanwell-0.1.0/src/stanwell/ui/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/ui/cli/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/ui/cli/cli.py +680 -0
- stanwell-0.1.0/src/stanwell/ui/cli/cli_util.py +61 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/constants.py +21 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/context.py +223 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/dashboard.py +94 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/helpers.py +90 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/sidebar.py +449 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/tabs/__init__.py +1 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/tabs/overview.py +868 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/tabs/projects.py +273 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/tabs/tag_balances.py +779 -0
- stanwell-0.1.0/src/stanwell/ui/dashboard/tabs/work_patterns.py +960 -0
- stanwell-0.1.0/src/stanwell/ui/date_periods.py +86 -0
- stanwell-0.1.0/tests/test_stanwell/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/test_projections.py +233 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/test_scoring.py +101 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/test_tag_analysis.py +184 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/test_working_patterns.py +218 -0
- stanwell-0.1.0/tests/test_stanwell/analysis/test_working_time.py +689 -0
- stanwell-0.1.0/tests/test_stanwell/conftest.py +4 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_data_coverage.py +305 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_data_util.py +76 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_database_schema.py +138 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_db.py +145 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_projects.py +224 -0
- stanwell-0.1.0/tests/test_stanwell/data_repository/test_time_entries.py +310 -0
- stanwell-0.1.0/tests/test_stanwell/integration/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/integration/test_toggle_api.py +129 -0
- stanwell-0.1.0/tests/test_stanwell/pytest_util/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/pytest_util/fixtures.py +24 -0
- stanwell-0.1.0/tests/test_stanwell/test_configuration.py +153 -0
- stanwell-0.1.0/tests/test_stanwell/toggl/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/toggl/test_client.py +559 -0
- stanwell-0.1.0/tests/test_stanwell/toggl/test_exceptions.py +84 -0
- stanwell-0.1.0/tests/test_stanwell/toggl/test_normalisation.py +203 -0
- stanwell-0.1.0/tests/test_stanwell/toggl/test_sync.py +748 -0
- stanwell-0.1.0/tests/test_stanwell/ui/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/ui/cli/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/ui/cli/test_cli.py +658 -0
- stanwell-0.1.0/tests/test_stanwell/ui/cli/test_cli_util.py +134 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/tabs/__init__.py +0 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/tabs/test_overview.py +605 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/tabs/test_projects.py +353 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/tabs/test_tag_balances.py +510 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/tabs/test_work_patterns.py +596 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/test_context.py +170 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/test_dashboard.py +158 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/test_dashboard_app.py +322 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/test_helpers.py +100 -0
- stanwell-0.1.0/tests/test_stanwell/ui/dashboard/test_sidebar.py +374 -0
- stanwell-0.1.0/tests/test_stanwell/ui/test_date_periods.py +67 -0
- stanwell-0.1.0/uv.lock +2161 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out repository
|
|
15
|
+
uses: actions/checkout@v6
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v6
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v2
|
|
24
|
+
with:
|
|
25
|
+
version: "latest"
|
|
26
|
+
|
|
27
|
+
- name: Cache uv
|
|
28
|
+
uses: actions/cache@v4
|
|
29
|
+
with:
|
|
30
|
+
path: ~/.cache/uv
|
|
31
|
+
key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }}
|
|
32
|
+
restore-keys: |
|
|
33
|
+
${{ runner.os }}-uv-
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: uv sync --dev
|
|
37
|
+
|
|
38
|
+
- name: Run CI checks
|
|
39
|
+
run: make ci
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build-docs:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Check out repository
|
|
17
|
+
uses: actions/checkout@v6
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v2
|
|
26
|
+
with:
|
|
27
|
+
version: "latest"
|
|
28
|
+
|
|
29
|
+
- name: Cache uv
|
|
30
|
+
uses: actions/cache@v4
|
|
31
|
+
with:
|
|
32
|
+
path: ~/.cache/uv
|
|
33
|
+
key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }}
|
|
34
|
+
restore-keys: |
|
|
35
|
+
${{ runner.os }}-uv-
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: uv sync --all-groups
|
|
39
|
+
|
|
40
|
+
- name: Build documentation
|
|
41
|
+
run: make docs-check
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- name: Check out repository
|
|
13
|
+
uses: actions/checkout@v6
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v2
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Upload distributions
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: python-package-distributions
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
publish:
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
|
|
36
|
+
environment:
|
|
37
|
+
name: pypi
|
|
38
|
+
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Download distributions
|
|
44
|
+
uses: actions/download-artifact@v5
|
|
45
|
+
with:
|
|
46
|
+
name: python-package-distributions
|
|
47
|
+
path: dist/
|
|
48
|
+
|
|
49
|
+
- name: Publish package
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Stanwell user data
|
|
2
|
+
config.yml
|
|
3
|
+
config.yaml
|
|
4
|
+
*.db
|
|
5
|
+
*.sqlite
|
|
6
|
+
*.sqlite3
|
|
7
|
+
|
|
8
|
+
# Secrets
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
|
|
12
|
+
# Python
|
|
13
|
+
__pycache__/
|
|
14
|
+
.venv/
|
|
15
|
+
|
|
16
|
+
# Sphinx
|
|
17
|
+
docs/_build/
|
|
18
|
+
docs/_latex/
|
|
19
|
+
docs/api/generated
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.env.integration
|
|
23
|
+
|
|
24
|
+
# Pycharm
|
|
25
|
+
.idea/
|
|
26
|
+
|
|
27
|
+
# Emacs
|
|
28
|
+
*~
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-11-04
|
|
4
|
+
Initial public release
|
|
5
|
+
|
|
6
|
+
<!---
|
|
7
|
+
TEMPLATE
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.0] - 20XX-XX-XX
|
|
11
|
+
### Added
|
|
12
|
+
- AAA
|
|
13
|
+
### Changed
|
|
14
|
+
- BBB
|
|
15
|
+
### Deprecated
|
|
16
|
+
- CCC
|
|
17
|
+
### Removed
|
|
18
|
+
- DDD
|
|
19
|
+
### Fixed
|
|
20
|
+
- EEE
|
|
21
|
+
### Security
|
|
22
|
+
- FFF
|
|
23
|
+
-->
|
stanwell-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jan Buermann
|
|
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.
|
stanwell-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.PHONY: test lint format docs docs-clean docs-check tidy ci
|
|
2
|
+
|
|
3
|
+
test:
|
|
4
|
+
uv run pytest
|
|
5
|
+
|
|
6
|
+
lint:
|
|
7
|
+
uv run ruff check . --fix
|
|
8
|
+
|
|
9
|
+
format:
|
|
10
|
+
uv run black src tests
|
|
11
|
+
|
|
12
|
+
docs:
|
|
13
|
+
rm -f docs/api/generated/*.rst
|
|
14
|
+
uv run sphinx-build -M html docs docs/_build
|
|
15
|
+
|
|
16
|
+
docs-clean:
|
|
17
|
+
rm -rf docs/api/generated
|
|
18
|
+
rm -rf docs/_build
|
|
19
|
+
uv run sphinx-build -M html docs docs/_build
|
|
20
|
+
|
|
21
|
+
docs-check:
|
|
22
|
+
uv run sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
23
|
+
|
|
24
|
+
tidy: lint format
|
|
25
|
+
|
|
26
|
+
ci:
|
|
27
|
+
uv run pytest
|
|
28
|
+
uv run ruff check .
|
|
29
|
+
uv run mypy -p stanwell
|
|
30
|
+
uv lock --locked
|
|
31
|
+
uv run black --check src tests
|
stanwell-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: stanwell
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Application to helps understand working time and distribution based on Toggl time tracking data.
|
|
5
|
+
Author: Jan Buermann
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: loguru>=0.7.3
|
|
10
|
+
Requires-Dist: matplotlib>=3.11.2
|
|
11
|
+
Requires-Dist: numpy>=2.5.3
|
|
12
|
+
Requires-Dist: pandas>=3.0.6
|
|
13
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
14
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
15
|
+
Requires-Dist: seaborn>=0.13.2
|
|
16
|
+
Requires-Dist: streamlit>=1.64.0
|
|
17
|
+
Requires-Dist: typer>=0.27.2
|
stanwell-0.1.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[](https://github.com/jbuerman/stanwell/actions/workflows/ci.yml)
|
|
2
|
+
[](LICENSE)
|
|
3
|
+
|
|
4
|
+
# Stanwell
|
|
5
|
+
|
|
6
|
+
*Have enthusiasm. Work sustainably.*
|
|
7
|
+
|
|
8
|
+
Stanwell analyses working time recorded in Toggl to help you understand how much you work and how that time is distributed across projects, activities and working patterns.
|
|
9
|
+
|
|
10
|
+
## Quick setup
|
|
11
|
+
|
|
12
|
+
Initialise Stanwell:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
stanwell init
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Synchronise historical data. Repeat until all missing data has been downloaded:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
stanwell sync-missing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Refresh recent entries when needed:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
stanwell refresh-recent
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open the interactive dashboard:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
stanwell dashboard
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Development
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv sync --all-groups
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Format and lint the project:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv run make tidy
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Run the CI checks locally:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run make ci
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Documentation
|
|
55
|
+
|
|
56
|
+
Build the Sphinx documentation:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv run make docs
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Open `docs/_build/html/index.html` to view the generated documentation.
|
|
63
|
+
|
|
64
|
+
### Integration tests
|
|
65
|
+
|
|
66
|
+
Create an environment file containing an API token and workspace ID using `.env.integration.example` as a template.
|
|
67
|
+
|
|
68
|
+
Then run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv run --env-file .env.integration pytest -m integration
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Licence
|
|
75
|
+
|
|
76
|
+
Stanwell is released under the [MIT Licence](LICENSE).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Generate CLI command documentation from the Typer application."""
|
|
2
|
+
|
|
3
|
+
from docutils import nodes
|
|
4
|
+
from docutils.parsers.rst import Directive
|
|
5
|
+
|
|
6
|
+
from stanwell.ui.cli.cli import app
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CliCommandsDirective(Directive):
|
|
10
|
+
"""Render registered Typer commands as a definition list."""
|
|
11
|
+
|
|
12
|
+
has_content = False
|
|
13
|
+
|
|
14
|
+
def run(self) -> list[nodes.Node]:
|
|
15
|
+
"""Build the definition list containing the registered CLI commands."""
|
|
16
|
+
definition_list = nodes.definition_list()
|
|
17
|
+
|
|
18
|
+
for command_info in app.registered_commands:
|
|
19
|
+
command_name = command_info.name
|
|
20
|
+
command_help = command_info.help
|
|
21
|
+
callback = command_info.callback
|
|
22
|
+
|
|
23
|
+
if command_name is None or callback is None:
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
target = (
|
|
27
|
+
f"{callback.__module__}."
|
|
28
|
+
f"{callback.__name__}"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
item = nodes.definition_list_item()
|
|
32
|
+
term = nodes.term()
|
|
33
|
+
|
|
34
|
+
reference_nodes, messages = self.state.inline_text(
|
|
35
|
+
f":py:func:`{command_name} <{target}>`",
|
|
36
|
+
self.lineno,
|
|
37
|
+
)
|
|
38
|
+
term.extend(reference_nodes)
|
|
39
|
+
|
|
40
|
+
definition = nodes.definition()
|
|
41
|
+
definition += nodes.paragraph(
|
|
42
|
+
text=command_help or "",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
item += term
|
|
46
|
+
item += definition
|
|
47
|
+
definition_list += item
|
|
48
|
+
|
|
49
|
+
definition_list.extend(messages)
|
|
50
|
+
|
|
51
|
+
return [definition_list]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def setup(app):
|
|
55
|
+
"""Register the CLI command directive."""
|
|
56
|
+
app.add_directive(
|
|
57
|
+
"cli-commands",
|
|
58
|
+
CliCommandsDirective,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
"parallel_read_safe": True,
|
|
63
|
+
"parallel_write_safe": True,
|
|
64
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Command-line interface
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
The ``stanwell`` command is used to set up Stanwell,
|
|
5
|
+
maintain the local copy of your Toggl data and launch the dashboard.
|
|
6
|
+
|
|
7
|
+
Commands
|
|
8
|
+
--------
|
|
9
|
+
|
|
10
|
+
.. cli-commands::
|
|
11
|
+
|
|
12
|
+
Use the built-in help at any time:
|
|
13
|
+
|
|
14
|
+
.. code-block:: console
|
|
15
|
+
|
|
16
|
+
stanwell --help
|
|
17
|
+
|
|
18
|
+
Help for an individual command is available in the same way:
|
|
19
|
+
|
|
20
|
+
.. code-block:: console
|
|
21
|
+
|
|
22
|
+
stanwell COMMAND --help
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import tomllib
|
|
5
|
+
from datetime import date
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
DOCS_DIR = Path(__file__).parent
|
|
9
|
+
sys.path.insert(
|
|
10
|
+
0,
|
|
11
|
+
str(DOCS_DIR / "_ext"),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
PROJECT_ROOT = os.path.abspath(os.path.join(__file__, "..", ".."))
|
|
15
|
+
sys.path.insert(0, os.path.join(PROJECT_ROOT, "src"))
|
|
16
|
+
|
|
17
|
+
def authors_to_comma_list(pyproject_data, include_emails=False):
|
|
18
|
+
"""Convert PEP 621 authors into a comma-separated string."""
|
|
19
|
+
authors = pyproject_data.get("project", {}).get("authors", []) or []
|
|
20
|
+
parts = []
|
|
21
|
+
|
|
22
|
+
for a in authors:
|
|
23
|
+
if isinstance(a, dict):
|
|
24
|
+
name = (a.get("name") or "").strip()
|
|
25
|
+
email = (a.get("email") or "").strip()
|
|
26
|
+
if name or email:
|
|
27
|
+
if include_emails and name and email:
|
|
28
|
+
parts.append(f"{name} <{email}>")
|
|
29
|
+
elif name:
|
|
30
|
+
parts.append(name)
|
|
31
|
+
elif email:
|
|
32
|
+
parts.append(f"<{email}>")
|
|
33
|
+
elif isinstance(a, str):
|
|
34
|
+
s = a.strip()
|
|
35
|
+
if s:
|
|
36
|
+
parts.append(s)
|
|
37
|
+
return ", ".join(parts)
|
|
38
|
+
|
|
39
|
+
with open(os.path.join(PROJECT_ROOT, "pyproject.toml"), "rb") as f:
|
|
40
|
+
pyproject_data = tomllib.load(f)
|
|
41
|
+
project_version = pyproject_data.get("project", {}).get("version", "-")
|
|
42
|
+
project_name = pyproject_data.get("project", {}).get("name", "-")
|
|
43
|
+
|
|
44
|
+
project = project_name
|
|
45
|
+
author = authors_to_comma_list(pyproject_data, include_emails=False)
|
|
46
|
+
copyright = f"{date.today().year}, {author}"
|
|
47
|
+
release = project_version
|
|
48
|
+
version = project_version
|
|
49
|
+
|
|
50
|
+
extensions = [
|
|
51
|
+
"sphinx.ext.autodoc",
|
|
52
|
+
"sphinx.ext.napoleon",
|
|
53
|
+
"sphinx.ext.autosummary",
|
|
54
|
+
"sphinx.ext.intersphinx",
|
|
55
|
+
"sphinx.ext.viewcode",
|
|
56
|
+
"sphinx_paramlinks",
|
|
57
|
+
"myst_parser",
|
|
58
|
+
"cli_commands",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
autosummary_generate = True
|
|
62
|
+
|
|
63
|
+
autodoc_typehints = "signature"
|
|
64
|
+
|
|
65
|
+
autodoc_default_options = {
|
|
66
|
+
"members": True,
|
|
67
|
+
"undoc-members": False,
|
|
68
|
+
"show-inheritance": True,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
|
72
|
+
templates_path = ["_templates"]
|
|
73
|
+
exclude_patterns = []
|
|
74
|
+
html_static_path = ["_static"]
|
|
75
|
+
|
|
76
|
+
html_theme = 'sphinx_rtd_theme'
|
|
77
|
+
|
|
78
|
+
html_logo = "_static/stanwell_logo.png"
|
|
79
|
+
html_favicon = "_static/stanwell_favicon.png"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Configuration
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
Stanwell uses a YAML configuration file to describe your
|
|
5
|
+
working pattern, local database and Toggl connection.
|
|
6
|
+
|
|
7
|
+
By default, the command-line interface looks for:
|
|
8
|
+
|
|
9
|
+
.. code-block:: text
|
|
10
|
+
|
|
11
|
+
~/.config/stanwell/config.yaml
|
|
12
|
+
|
|
13
|
+
A different configuration file can be selected with the global
|
|
14
|
+
`--config` (or `-c`) option.
|
|
15
|
+
|
|
16
|
+
Working pattern
|
|
17
|
+
---------------
|
|
18
|
+
|
|
19
|
+
The configuration describes how the application should interpret your
|
|
20
|
+
recorded time. Important settings include:
|
|
21
|
+
|
|
22
|
+
`workday_hours`
|
|
23
|
+
The number of hours in a standard working day.
|
|
24
|
+
|
|
25
|
+
`working_days`
|
|
26
|
+
The weekdays on which work is normally required.
|
|
27
|
+
|
|
28
|
+
`core_start` and `core_end`
|
|
29
|
+
The normal core-hours interval used when analysing working patterns.
|
|
30
|
+
|
|
31
|
+
`timezone`
|
|
32
|
+
The timezone used when interpreting recorded time.
|
|
33
|
+
|
|
34
|
+
`tracking_start_date`
|
|
35
|
+
The date from which working-time tracking begins.
|
|
36
|
+
|
|
37
|
+
For example:
|
|
38
|
+
|
|
39
|
+
.. code-block:: yaml
|
|
40
|
+
|
|
41
|
+
workday_hours: 7.5
|
|
42
|
+
working_days: [0, 1, 2, 3, 4]
|
|
43
|
+
timezone: Europe/London
|
|
44
|
+
core_start: "10:00"
|
|
45
|
+
core_end: "16:00"
|
|
46
|
+
|
|
47
|
+
Exception days
|
|
48
|
+
--------------
|
|
49
|
+
|
|
50
|
+
Exception days represent dates on which the normal working pattern does not
|
|
51
|
+
apply.
|
|
52
|
+
|
|
53
|
+
The standard exception types are:
|
|
54
|
+
|
|
55
|
+
`annual_leave`
|
|
56
|
+
Annual leave.
|
|
57
|
+
|
|
58
|
+
`sick`
|
|
59
|
+
Sick leave.
|
|
60
|
+
|
|
61
|
+
`public_holiday`
|
|
62
|
+
A public holiday.
|
|
63
|
+
|
|
64
|
+
`closure_day`
|
|
65
|
+
An organisation closure day.
|
|
66
|
+
|
|
67
|
+
`overtime_compensation`
|
|
68
|
+
Time taken off in lieu of overtime.
|
|
69
|
+
|
|
70
|
+
Annual leave, sickness, public holidays and closure days remove the normal
|
|
71
|
+
working-time requirement for that day.
|
|
72
|
+
|
|
73
|
+
Overtime compensation also removes the normal requirement, but differs in
|
|
74
|
+
one important respect: the compensated hours are deducted from the accumulated
|
|
75
|
+
working-time balance.
|
|
76
|
+
|
|
77
|
+
This allows a day taken in lieu to offset additional time worked previously,
|
|
78
|
+
rather than appearing simply as another non-working day.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Dashboard
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
The interactive dashboard turns the locally stored time entries into a set
|
|
5
|
+
of views of your working time.
|
|
6
|
+
|
|
7
|
+
Open it from the command line:
|
|
8
|
+
|
|
9
|
+
.. code-block:: console
|
|
10
|
+
|
|
11
|
+
stanwell dashboard
|
|
12
|
+
|
|
13
|
+
The date controls in the dashboard determine the period used for most
|
|
14
|
+
analyses.
|
|
15
|
+
|
|
16
|
+
Overview
|
|
17
|
+
--------
|
|
18
|
+
|
|
19
|
+
The Overview brings together the main working-time measures.
|
|
20
|
+
|
|
21
|
+
It shows recorded and required working time, working-time balance and
|
|
22
|
+
overtime compensation. Daily and weekly views make shorter-term variation
|
|
23
|
+
visible, while the longer-term balance view shows how additional work,
|
|
24
|
+
shortfalls and overtime compensation affect the accumulated balance.
|
|
25
|
+
|
|
26
|
+
The Overview also provides a weekday view to help identify differences in
|
|
27
|
+
working time across the week.
|
|
28
|
+
|
|
29
|
+
Projects
|
|
30
|
+
--------
|
|
31
|
+
|
|
32
|
+
The Projects tab shows how recorded working time is distributed between
|
|
33
|
+
projects.
|
|
34
|
+
|
|
35
|
+
This is useful for understanding where working time has gone during the
|
|
36
|
+
selected period rather than considering total working hours alone.
|
|
37
|
+
|
|
38
|
+
Working patterns
|
|
39
|
+
----------------
|
|
40
|
+
|
|
41
|
+
The Working patterns tab focuses on when work takes place.
|
|
42
|
+
|
|
43
|
+
It helps make patterns such as work outside core hours and work on
|
|
44
|
+
designated days off visible. These measures complement the simple
|
|
45
|
+
working-time balance: working the expected number of hours does not
|
|
46
|
+
necessarily mean those hours are distributed in a healthy way.
|
|
47
|
+
|
|
48
|
+
Tag balance
|
|
49
|
+
-----------
|
|
50
|
+
|
|
51
|
+
The Tag balance tab uses tags attached to Toggl entries to explore categories
|
|
52
|
+
of activity across the selected period.
|
|
53
|
+
|
|
54
|
+
Tags can overlap, so tagged time should be interpreted as different views of
|
|
55
|
+
the same recorded time rather than necessarily additive categories.
|