python-appie 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.
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ quality:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.11", "3.12"]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Install dependencies
21
+ run: uv sync --extra dev
22
+ - name: Ruff format check
23
+ run: uv run ruff format --check .
24
+ - name: Ruff lint
25
+ run: uv run ruff check .
26
+ - name: Pyright
27
+ run: uv run pyright
28
+ - name: Pytest
29
+ run: uv run pytest
30
+
31
+ docs:
32
+ runs-on: ubuntu-latest
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: astral-sh/setup-uv@v4
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.11"
40
+ - name: Install dependencies
41
+ run: uv sync --extra dev
42
+ - name: Build docs
43
+ run: uv run mkdocs build --strict
@@ -0,0 +1,44 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: astral-sh/setup-uv@v4
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.11"
26
+ - name: Install dependencies
27
+ run: uv sync --extra dev
28
+ - name: Build docs
29
+ run: uv run mkdocs build --strict
30
+ - name: Upload pages artifact
31
+ uses: actions/upload-pages-artifact@v3
32
+ with:
33
+ path: site
34
+
35
+ deploy:
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment:
39
+ name: github-pages
40
+ url: ${{ steps.deployment.outputs.page_url }}
41
+ steps:
42
+ - name: Deploy to GitHub Pages
43
+ id: deployment
44
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,47 @@
1
+ name: Publish PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.11"
23
+ - name: Install dependencies
24
+ run: uv sync --extra dev
25
+ - name: Build package
26
+ run: uv build
27
+ - name: Upload distributions
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: python-appie-dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/p/python-appie
39
+
40
+ steps:
41
+ - name: Download distributions
42
+ uses: actions/download-artifact@v4
43
+ with:
44
+ name: python-appie-dist
45
+ path: dist/
46
+ - name: Publish to PyPI
47
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .venv/
6
+ .coverage
7
+ htmlcov/
8
+ site/
@@ -0,0 +1,28 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-format
5
+ name: ruff format
6
+ entry: uv run ruff format .
7
+ language: system
8
+ pass_filenames: false
9
+ - id: ruff-check
10
+ name: ruff check
11
+ entry: uv run ruff check .
12
+ language: system
13
+ pass_filenames: false
14
+ - id: pyright
15
+ name: pyright
16
+ entry: uv run pyright
17
+ language: system
18
+ pass_filenames: false
19
+ - id: pytest
20
+ name: pytest
21
+ entry: uv run pytest
22
+ language: system
23
+ pass_filenames: false
24
+ - id: mkdocs-build
25
+ name: mkdocs build
26
+ entry: uv run mkdocs build --strict
27
+ language: system
28
+ pass_filenames: false
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-appie
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python client for the Albert Heijn API.
5
+ Project-URL: Homepage, https://github.com/tijnschouten/appie
6
+ Project-URL: Documentation, https://tijnschouten.github.io/appie/
7
+ Project-URL: Repository, https://github.com/tijnschouten/appie
8
+ Project-URL: Issues, https://github.com/tijnschouten/appie/issues
9
+ Author: Codex
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: httpx
12
+ Requires-Dist: playwright>=1.52.0
13
+ Requires-Dist: pydantic>=2
14
+ Provides-Extra: dev
15
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
16
+ Requires-Dist: mkdocs>=1.6; extra == 'dev'
17
+ Requires-Dist: pre-commit>=4.0; extra == 'dev'
18
+ Requires-Dist: pyright>=1.1.390; extra == 'dev'
19
+ Requires-Dist: pytest; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio; extra == 'dev'
21
+ Requires-Dist: pytest-cov; extra == 'dev'
22
+ Requires-Dist: respx; extra == 'dev'
23
+ Requires-Dist: ruff; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # python-appie
27
+
28
+ `python-appie` is an unofficial async Python client for the Albert Heijn API.
29
+
30
+ Full documentation lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs), is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml), and is intended to be published at [tijnschouten.github.io/appie](https://tijnschouten.github.io/appie/).
31
+
32
+ Releases are intended to publish to PyPI as `python-appie` from version tags via GitHub Actions.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ uv add python-appie
38
+ ```
39
+
40
+ Or:
41
+
42
+ ```bash
43
+ pip install python-appie
44
+ ```
45
+
46
+ For local development in this repository:
47
+
48
+ ```bash
49
+ uv sync --extra dev
50
+ pre-commit install
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ Authenticate once:
56
+
57
+ ```bash
58
+ uv run appie-login
59
+ ```
60
+
61
+ This opens Chrome for an interactive AH login and captures the OAuth redirect code automatically. If automatic capture cannot start, the CLI falls back to asking for the redirect URL or raw code manually.
62
+
63
+ Then use the client:
64
+
65
+ ```python
66
+ import asyncio
67
+
68
+ from appie import AHClient
69
+
70
+
71
+ async def main() -> None:
72
+ async with AHClient() as client:
73
+ products = await client.products.search("melk", limit=3)
74
+ for product in products:
75
+ print(product)
76
+
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ Tokens are stored in `~/.config/appie/tokens.json` and refreshed automatically when they are close to expiring.
82
+
83
+ ## Features
84
+
85
+ ### Authentication
86
+
87
+ - `appie-login` CLI for browser-based login
88
+ - automatic code capture from the AH redirect flow
89
+ - token persistence in `~/.config/appie/tokens.json`
90
+ - automatic token refresh using the stored refresh token
91
+
92
+ ### Products
93
+
94
+ - search products via `client.products.search(query, limit=10)`
95
+ - fetch a single product via `client.products.get(product_id)`
96
+
97
+ Example:
98
+
99
+ ```python
100
+ import asyncio
101
+
102
+ from appie import AHClient
103
+
104
+
105
+ async def main() -> None:
106
+ async with AHClient() as client:
107
+ product = await client.products.get(1525)
108
+ print(product)
109
+
110
+
111
+ asyncio.run(main())
112
+ ```
113
+
114
+ ### Receipts
115
+
116
+ - list in-store POS receipt summaries via `client.receipts.list_all(limit=50)`
117
+ - fetch a receipt with line items via `client.receipts.get_pos_receipt(receipt_id)`
118
+
119
+ Important:
120
+ `list_all()` and `list_pos_receipts()` return receipt summaries. In those results, `products` is intentionally empty.
121
+ To retrieve line items, call `get_pos_receipt()` with a receipt ID from the summary list.
122
+
123
+ Example:
124
+
125
+ ```python
126
+ import asyncio
127
+
128
+ from appie import AHClient
129
+
130
+
131
+ async def main() -> None:
132
+ async with AHClient() as client:
133
+ receipts = await client.receipts.list_all(limit=5)
134
+ detailed = await client.receipts.get_pos_receipt(receipts[0].id)
135
+ print(detailed)
136
+
137
+
138
+ asyncio.run(main())
139
+ ```
140
+
141
+ ### Shopping lists
142
+
143
+ - add an item via `client.lists.add_item(description, quantity=1, product_id=None)`
144
+ - use `MockAHClient` for local development and tests without touching AH
145
+
146
+ Example:
147
+
148
+ ```python
149
+ import asyncio
150
+
151
+ from appie import AHClient
152
+
153
+
154
+ async def main() -> None:
155
+ async with AHClient() as client:
156
+ item = await client.lists.add_item("Halfvolle melk", quantity=2)
157
+ print(item)
158
+
159
+
160
+ asyncio.run(main())
161
+ ```
162
+
163
+ Current limitation:
164
+ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear()` still raise `NotImplementedError` until their live API shape is confirmed.
165
+
166
+ ## API overview
167
+
168
+ ### Main client
169
+
170
+ - `AHClient()`
171
+ - `MockAHClient()`
172
+ - `await client.login()`
173
+ - `await client.graphql(query, variables=None)`
174
+
175
+ ### Auth client
176
+
177
+ - `AHAuthClient.get_anonymous_token()`
178
+ - `AHAuthClient.login_with_code(code)`
179
+ - `AHAuthClient.refresh_token(refresh_token)`
180
+
181
+ ### Sub-APIs
182
+
183
+ - `client.products.search(query, limit=10)`
184
+ - `client.products.get(product_id)`
185
+ - `client.receipts.list_pos_receipts(limit=50)`
186
+ - `client.receipts.list_all(limit=50)`
187
+ - `client.receipts.get_pos_receipt(receipt_id)`
188
+ - `client.lists.add_item(description, quantity=1, product_id=None)`
189
+ - `client.lists.get_list()`
190
+ - `client.lists.remove_item(item_id)`
191
+ - `client.lists.clear()`
192
+
193
+ ## Development
194
+
195
+ Run checks locally:
196
+
197
+ ```bash
198
+ uv run ruff format .
199
+ uv run --extra dev ruff check .
200
+ uv run --extra dev pyright
201
+ uv run --extra dev pytest
202
+ uv run --extra dev mkdocs build --strict
203
+ ```
204
+
205
+ ## Notes
206
+
207
+ - This client is unofficial and may break when Albert Heijn changes its backend.
208
+ - Receipt support currently covers in-store POS receipts.
209
+ - Shopping-list support only implements the verified add-item mutation; other operations raise explicit `NotImplementedError` until their GraphQL shape is confirmed.
210
+ - Receipt summaries do not include line items; call `get_pos_receipt()` for a detailed receipt.
211
+ - Endpoint discovery for this package is inspired by [gwillem/appie-go](https://github.com/gwillem/appie-go).
@@ -0,0 +1,186 @@
1
+ # python-appie
2
+
3
+ `python-appie` is an unofficial async Python client for the Albert Heijn API.
4
+
5
+ Full documentation lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs), is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml), and is intended to be published at [tijnschouten.github.io/appie](https://tijnschouten.github.io/appie/).
6
+
7
+ Releases are intended to publish to PyPI as `python-appie` from version tags via GitHub Actions.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ uv add python-appie
13
+ ```
14
+
15
+ Or:
16
+
17
+ ```bash
18
+ pip install python-appie
19
+ ```
20
+
21
+ For local development in this repository:
22
+
23
+ ```bash
24
+ uv sync --extra dev
25
+ pre-commit install
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ Authenticate once:
31
+
32
+ ```bash
33
+ uv run appie-login
34
+ ```
35
+
36
+ This opens Chrome for an interactive AH login and captures the OAuth redirect code automatically. If automatic capture cannot start, the CLI falls back to asking for the redirect URL or raw code manually.
37
+
38
+ Then use the client:
39
+
40
+ ```python
41
+ import asyncio
42
+
43
+ from appie import AHClient
44
+
45
+
46
+ async def main() -> None:
47
+ async with AHClient() as client:
48
+ products = await client.products.search("melk", limit=3)
49
+ for product in products:
50
+ print(product)
51
+
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ Tokens are stored in `~/.config/appie/tokens.json` and refreshed automatically when they are close to expiring.
57
+
58
+ ## Features
59
+
60
+ ### Authentication
61
+
62
+ - `appie-login` CLI for browser-based login
63
+ - automatic code capture from the AH redirect flow
64
+ - token persistence in `~/.config/appie/tokens.json`
65
+ - automatic token refresh using the stored refresh token
66
+
67
+ ### Products
68
+
69
+ - search products via `client.products.search(query, limit=10)`
70
+ - fetch a single product via `client.products.get(product_id)`
71
+
72
+ Example:
73
+
74
+ ```python
75
+ import asyncio
76
+
77
+ from appie import AHClient
78
+
79
+
80
+ async def main() -> None:
81
+ async with AHClient() as client:
82
+ product = await client.products.get(1525)
83
+ print(product)
84
+
85
+
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ ### Receipts
90
+
91
+ - list in-store POS receipt summaries via `client.receipts.list_all(limit=50)`
92
+ - fetch a receipt with line items via `client.receipts.get_pos_receipt(receipt_id)`
93
+
94
+ Important:
95
+ `list_all()` and `list_pos_receipts()` return receipt summaries. In those results, `products` is intentionally empty.
96
+ To retrieve line items, call `get_pos_receipt()` with a receipt ID from the summary list.
97
+
98
+ Example:
99
+
100
+ ```python
101
+ import asyncio
102
+
103
+ from appie import AHClient
104
+
105
+
106
+ async def main() -> None:
107
+ async with AHClient() as client:
108
+ receipts = await client.receipts.list_all(limit=5)
109
+ detailed = await client.receipts.get_pos_receipt(receipts[0].id)
110
+ print(detailed)
111
+
112
+
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ ### Shopping lists
117
+
118
+ - add an item via `client.lists.add_item(description, quantity=1, product_id=None)`
119
+ - use `MockAHClient` for local development and tests without touching AH
120
+
121
+ Example:
122
+
123
+ ```python
124
+ import asyncio
125
+
126
+ from appie import AHClient
127
+
128
+
129
+ async def main() -> None:
130
+ async with AHClient() as client:
131
+ item = await client.lists.add_item("Halfvolle melk", quantity=2)
132
+ print(item)
133
+
134
+
135
+ asyncio.run(main())
136
+ ```
137
+
138
+ Current limitation:
139
+ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear()` still raise `NotImplementedError` until their live API shape is confirmed.
140
+
141
+ ## API overview
142
+
143
+ ### Main client
144
+
145
+ - `AHClient()`
146
+ - `MockAHClient()`
147
+ - `await client.login()`
148
+ - `await client.graphql(query, variables=None)`
149
+
150
+ ### Auth client
151
+
152
+ - `AHAuthClient.get_anonymous_token()`
153
+ - `AHAuthClient.login_with_code(code)`
154
+ - `AHAuthClient.refresh_token(refresh_token)`
155
+
156
+ ### Sub-APIs
157
+
158
+ - `client.products.search(query, limit=10)`
159
+ - `client.products.get(product_id)`
160
+ - `client.receipts.list_pos_receipts(limit=50)`
161
+ - `client.receipts.list_all(limit=50)`
162
+ - `client.receipts.get_pos_receipt(receipt_id)`
163
+ - `client.lists.add_item(description, quantity=1, product_id=None)`
164
+ - `client.lists.get_list()`
165
+ - `client.lists.remove_item(item_id)`
166
+ - `client.lists.clear()`
167
+
168
+ ## Development
169
+
170
+ Run checks locally:
171
+
172
+ ```bash
173
+ uv run ruff format .
174
+ uv run --extra dev ruff check .
175
+ uv run --extra dev pyright
176
+ uv run --extra dev pytest
177
+ uv run --extra dev mkdocs build --strict
178
+ ```
179
+
180
+ ## Notes
181
+
182
+ - This client is unofficial and may break when Albert Heijn changes its backend.
183
+ - Receipt support currently covers in-store POS receipts.
184
+ - Shopping-list support only implements the verified add-item mutation; other operations raise explicit `NotImplementedError` until their GraphQL shape is confirmed.
185
+ - Receipt summaries do not include line items; call `get_pos_receipt()` for a detailed receipt.
186
+ - Endpoint discovery for this package is inspired by [gwillem/appie-go](https://github.com/gwillem/appie-go).
@@ -0,0 +1,21 @@
1
+ # Authentication
2
+
3
+ ## Login flow
4
+
5
+ The package uses a browser-assisted login flow exposed through `appie-login`.
6
+
7
+ The CLI:
8
+
9
+ - opens a Chrome window
10
+ - waits for the AH redirect to `appie://login-exit?code=...`
11
+ - exchanges that code for tokens
12
+ - stores tokens in `~/.config/appie/tokens.json`
13
+
14
+ ## Token refresh
15
+
16
+ Access tokens are refreshed automatically when they are close to expiry. Under normal usage, you should only need to run `appie-login` again when the stored refresh token is no longer valid.
17
+
18
+ ## Notes
19
+
20
+ - This is not an official AH integration.
21
+ - AH may change login requirements at any time.
@@ -0,0 +1,11 @@
1
+ # CLI
2
+
3
+ ## `appie-login`
4
+
5
+ Authenticate and store tokens locally:
6
+
7
+ ```bash
8
+ uv run appie-login
9
+ ```
10
+
11
+ The CLI is intended as a one-time or occasional setup step. Day-to-day usage should normally rely on stored tokens and automatic refresh.
@@ -0,0 +1,28 @@
1
+ # Development
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ uv sync --extra dev
7
+ pre-commit install
8
+ ```
9
+
10
+ ## Quality checks
11
+
12
+ ```bash
13
+ uv run ruff format .
14
+ uv run ruff check .
15
+ uv run pyright
16
+ uv run pytest
17
+ uv run mkdocs build
18
+ ```
19
+
20
+ ## Pre-commit
21
+
22
+ This repository uses pre-commit hooks for:
23
+
24
+ - `ruff format`
25
+ - `ruff check`
26
+ - `pyright`
27
+ - `pytest`
28
+ - `mkdocs build`
@@ -0,0 +1,47 @@
1
+ # Getting Started
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ uv add python-appie
7
+ ```
8
+
9
+ Or:
10
+
11
+ ```bash
12
+ pip install python-appie
13
+ ```
14
+
15
+ For local development in this repository:
16
+
17
+ ```bash
18
+ uv sync --extra dev
19
+ ```
20
+
21
+ ## Login
22
+
23
+ Run:
24
+
25
+ ```bash
26
+ uv run appie-login
27
+ ```
28
+
29
+ This opens Chrome and captures the AH login redirect code automatically. Tokens are stored in `~/.config/appie/tokens.json`.
30
+
31
+ ## First request
32
+
33
+ ```python
34
+ import asyncio
35
+
36
+ from appie import AHClient
37
+
38
+
39
+ async def main() -> None:
40
+ async with AHClient() as client:
41
+ products = await client.products.search("melk", limit=3)
42
+ for product in products:
43
+ print(product)
44
+
45
+
46
+ asyncio.run(main())
47
+ ```
@@ -0,0 +1,29 @@
1
+ # python-appie
2
+
3
+ `python-appie` is an unofficial async Python client for the Albert Heijn API.
4
+
5
+ It currently supports:
6
+
7
+ - browser-based login via `appie-login`
8
+ - token persistence and refresh
9
+ - product search and product detail lookup
10
+ - receipt summary listing
11
+ - receipt detail retrieval with line items
12
+ - shopping-list item creation
13
+
14
+ ## Current status
15
+
16
+ This package talks to an unofficial API. That means:
17
+
18
+ - the API may change without notice
19
+ - fields and endpoints can break at any time
20
+ - conservative usage is recommended
21
+
22
+ ## Read next
23
+
24
+ - [Getting Started](getting-started.md)
25
+ - [Authentication](authentication.md)
26
+ - [Products](products.md)
27
+ - [Receipts](receipts.md)
28
+ - [Shopping Lists](lists.md)
29
+ - [Development](development.md)