amazon-orders-de 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.
- amazon_orders_de-1.0.0/.github/workflows/integration.yml +45 -0
- amazon_orders_de-1.0.0/.github/workflows/lint.yml +91 -0
- amazon_orders_de-1.0.0/.github/workflows/mr-test.yml +74 -0
- amazon_orders_de-1.0.0/.github/workflows/mr-title.yml +21 -0
- amazon_orders_de-1.0.0/.github/workflows/publish.yml +32 -0
- amazon_orders_de-1.0.0/.github/workflows/release-automerge-weekly.yml +31 -0
- amazon_orders_de-1.0.0/.github/workflows/release-please.yml +21 -0
- amazon_orders_de-1.0.0/.gitignore +21 -0
- amazon_orders_de-1.0.0/.markdownlint-cli2.yaml +14 -0
- amazon_orders_de-1.0.0/.release-please-manifest.json +3 -0
- amazon_orders_de-1.0.0/.yamllint.yml +18 -0
- amazon_orders_de-1.0.0/CHANGELOG.md +13 -0
- amazon_orders_de-1.0.0/LICENSE +21 -0
- amazon_orders_de-1.0.0/PKG-INFO +78 -0
- amazon_orders_de-1.0.0/README.md +64 -0
- amazon_orders_de-1.0.0/amazon_orders_de/__init__.py +11 -0
- amazon_orders_de-1.0.0/amazon_orders_de/constants.py +21 -0
- amazon_orders_de-1.0.0/amazon_orders_de/order.py +46 -0
- amazon_orders_de-1.0.0/amazon_orders_de/selectors.py +30 -0
- amazon_orders_de-1.0.0/amazon_orders_de/transactions.py +157 -0
- amazon_orders_de-1.0.0/lychee.toml +18 -0
- amazon_orders_de-1.0.0/mise.toml +31 -0
- amazon_orders_de-1.0.0/pyproject.toml +63 -0
- amazon_orders_de-1.0.0/release-please-config.json +21 -0
- amazon_orders_de-1.0.0/renovate.json +23 -0
- amazon_orders_de-1.0.0/scripts/anonymize-test-resources.py +719 -0
- amazon_orders_de-1.0.0/tests/__init__.py +0 -0
- amazon_orders_de-1.0.0/tests/integration/__init__.py +0 -0
- amazon_orders_de-1.0.0/tests/integration/test_integration.py +61 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-302-1023958-8934207.html +734 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-302-6964083-0174099.html +638 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-305-1022088-4781474.html +650 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-305-1086546-4564812.html +515 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-305-1455896-8790496.html +573 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-305-9788051-7308445.html +602 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-306-9272323-1520652.html +638 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-details-D01-7485423-1791348.html +527 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-history-2024-0.html +899 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-history-2026-0.html +1135 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-history-2026-120.html +408 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-history-2026-140.html +419 -0
- amazon_orders_de-1.0.0/tests/resources/orders/order-history-2026-60.html +385 -0
- amazon_orders_de-1.0.0/tests/resources/transactions/transactions-api-page-2.json +1 -0
- amazon_orders_de-1.0.0/tests/resources/transactions/transactions.html +268 -0
- amazon_orders_de-1.0.0/tests/unit/__init__.py +0 -0
- amazon_orders_de-1.0.0/tests/unit/test_amazon_de.py +415 -0
- amazon_orders_de-1.0.0/uv.lock +993 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Integration Test
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
schedule:
|
|
7
|
+
# Weekly, Monday morning: a login every day makes Amazon more likely to challenge it.
|
|
8
|
+
- cron: "0 7 * * 1"
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: amazon-orders-de-integration-singleton
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 30
|
|
20
|
+
env:
|
|
21
|
+
PYTHONUNBUFFERED: 1
|
|
22
|
+
# The logs of a public repo are public, and debug output logs every URL, with the account's Order numbers.
|
|
23
|
+
DEBUG: "False"
|
|
24
|
+
AMAZON_USERNAME: ${{ secrets.AMAZON_USERNAME }}
|
|
25
|
+
AMAZON_PASSWORD: ${{ secrets.AMAZON_PASSWORD }}
|
|
26
|
+
AMAZON_OTP_SECRET_KEY: ${{ secrets.AMAZON_OTP_SECRET_KEY }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
|
+
with:
|
|
30
|
+
persist-credentials: false
|
|
31
|
+
|
|
32
|
+
- name: Verify credentials are set
|
|
33
|
+
run: |
|
|
34
|
+
if [ -z "$AMAZON_USERNAME" ] || [ -z "$AMAZON_PASSWORD" ]; then
|
|
35
|
+
echo "::error::Set the AMAZON_USERNAME and AMAZON_PASSWORD repo secrets."
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: mise run install
|
|
43
|
+
|
|
44
|
+
- name: Integration test
|
|
45
|
+
run: mise run integration-test
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lint
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
# Python is linted in mr-test.yml. Each linter here is its own job so a failure names the file type. The linters'
|
|
16
|
+
# versions are in mise.toml, where Renovate updates them.
|
|
17
|
+
jobs:
|
|
18
|
+
markdown:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
|
|
25
|
+
# Files and rules come from .markdownlint-cli2.yaml, whose globs add to the action's default of the root files.
|
|
26
|
+
- uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
|
|
27
|
+
|
|
28
|
+
markdown-links:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
|
|
35
|
+
- name: Restore lychee cache
|
|
36
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
37
|
+
with:
|
|
38
|
+
path: .lycheecache
|
|
39
|
+
key: lychee-${{ github.sha }}
|
|
40
|
+
restore-keys: lychee-
|
|
41
|
+
|
|
42
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
43
|
+
with:
|
|
44
|
+
install_args: lychee
|
|
45
|
+
|
|
46
|
+
- name: lychee
|
|
47
|
+
env:
|
|
48
|
+
# Authenticated requests to GitHub links are not rate limited as quickly.
|
|
49
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
50
|
+
run: lychee --config lychee.toml --no-progress README.md
|
|
51
|
+
|
|
52
|
+
yaml:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
56
|
+
with:
|
|
57
|
+
persist-credentials: false
|
|
58
|
+
|
|
59
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
60
|
+
with:
|
|
61
|
+
install_args: python uv pypi:yamllint
|
|
62
|
+
|
|
63
|
+
# Files and rules come from .yamllint.yml.
|
|
64
|
+
- name: yamllint
|
|
65
|
+
run: yamllint --strict --format github .
|
|
66
|
+
|
|
67
|
+
actions:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
71
|
+
with:
|
|
72
|
+
persist-credentials: false
|
|
73
|
+
|
|
74
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
75
|
+
with:
|
|
76
|
+
install_args: actionlint shellcheck
|
|
77
|
+
|
|
78
|
+
# Also runs shellcheck over every `run:` block.
|
|
79
|
+
- name: actionlint
|
|
80
|
+
run: actionlint
|
|
81
|
+
|
|
82
|
+
json:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
86
|
+
with:
|
|
87
|
+
persist-credentials: false
|
|
88
|
+
|
|
89
|
+
# Syntax only: release-please owns the formatting of the files it writes.
|
|
90
|
+
- name: jq
|
|
91
|
+
run: git ls-files -z '*.json' | xargs -0 -n1 jq empty
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: MR Test
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
test:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 15
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
python:
|
|
28
|
+
# The oldest supported version (requires-python).
|
|
29
|
+
- "3.10"
|
|
30
|
+
# The one of mise.toml. Renovate updates both in the same PR.
|
|
31
|
+
# renovate: python
|
|
32
|
+
- "3.15.0"
|
|
33
|
+
env:
|
|
34
|
+
UV_PYTHON: ${{ matrix.python }}
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
37
|
+
with:
|
|
38
|
+
persist-credentials: false
|
|
39
|
+
|
|
40
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
41
|
+
|
|
42
|
+
- name: Install dependencies
|
|
43
|
+
run: mise run install
|
|
44
|
+
|
|
45
|
+
- name: Lint
|
|
46
|
+
run: mise run lint
|
|
47
|
+
|
|
48
|
+
- name: Test
|
|
49
|
+
run: mise run test
|
|
50
|
+
|
|
51
|
+
build:
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
timeout-minutes: 10
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
56
|
+
with:
|
|
57
|
+
persist-credentials: false
|
|
58
|
+
|
|
59
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
60
|
+
|
|
61
|
+
- name: Build
|
|
62
|
+
run: uv build
|
|
63
|
+
|
|
64
|
+
# One status check for branch protection, whatever jobs are added later.
|
|
65
|
+
required:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
needs: [test, build]
|
|
68
|
+
if: always()
|
|
69
|
+
steps:
|
|
70
|
+
- name: Check result
|
|
71
|
+
run: |
|
|
72
|
+
if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: MR Title
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
types:
|
|
7
|
+
- opened
|
|
8
|
+
- edited
|
|
9
|
+
- synchronize
|
|
10
|
+
- reopened
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
pull-requests: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
lint-pr-title:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
|
20
|
+
env:
|
|
21
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Publish to PyPI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 10
|
|
16
|
+
environment:
|
|
17
|
+
name: pypi
|
|
18
|
+
url: https://pypi.org/p/amazon-orders-de
|
|
19
|
+
permissions:
|
|
20
|
+
id-token: write
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
# Trusted publishing: PyPI trusts this workflow and the pypi environment, no token needed.
|
|
32
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Release Automerge
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: 0 18 * * 0
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
pull-requests: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
merge-latest-release:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
# OVERRIDE_TOKEN is required: a merge made with GITHUB_TOKEN would not
|
|
18
|
+
# trigger the release-please workflow that creates the release.
|
|
19
|
+
- name: Merge release PR
|
|
20
|
+
run: |
|
|
21
|
+
pr=$(gh pr list --repo "${GITHUB_REPOSITORY}" \
|
|
22
|
+
--head release-please--branches--main --state open \
|
|
23
|
+
--json number --jq '.[0].number // empty')
|
|
24
|
+
if [ -z "${pr}" ]; then
|
|
25
|
+
echo "No open release PR."
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
gh pr checks "${pr}" --repo "${GITHUB_REPOSITORY}"
|
|
29
|
+
gh pr merge "${pr}" --squash --repo "${GITHUB_REPOSITORY}"
|
|
30
|
+
env:
|
|
31
|
+
GH_TOKEN: ${{ secrets.OVERRIDE_TOKEN }}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-please
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release-please:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
# OVERRIDE_TOKEN is required: a release created with GITHUB_TOKEN would not trigger publish.yml.
|
|
19
|
+
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
|
|
20
|
+
with:
|
|
21
|
+
token: ${{ secrets.OVERRIDE_TOKEN }}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.lycheecache
|
|
12
|
+
.coverage
|
|
13
|
+
htmlcov/
|
|
14
|
+
|
|
15
|
+
# Debug output of amazon-orders (real, unanonymized pages)
|
|
16
|
+
output/
|
|
17
|
+
# Raw pages captured from a real account, before they are anonymized
|
|
18
|
+
tests/resources-raw/
|
|
19
|
+
tests/.integration-output/
|
|
20
|
+
|
|
21
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
config:
|
|
3
|
+
# Prose is written one paragraph per line, and tables and command lines can't be wrapped.
|
|
4
|
+
MD013: false
|
|
5
|
+
|
|
6
|
+
globs:
|
|
7
|
+
- "**/*.md"
|
|
8
|
+
|
|
9
|
+
ignores:
|
|
10
|
+
# Generated by release-please.
|
|
11
|
+
- CHANGELOG.md
|
|
12
|
+
- .venv/**
|
|
13
|
+
- venv/**
|
|
14
|
+
- .claude/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: default
|
|
3
|
+
|
|
4
|
+
ignore: |
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
.claude/
|
|
8
|
+
|
|
9
|
+
rules:
|
|
10
|
+
# `on:` is a workflow key, not a boolean.
|
|
11
|
+
truthy:
|
|
12
|
+
check-keys: false
|
|
13
|
+
# Workflow comments and step commands explain themselves on one line.
|
|
14
|
+
line-length:
|
|
15
|
+
max: 160
|
|
16
|
+
# SHA-pinned actions carry their version as `@sha # vX.Y.Z`.
|
|
17
|
+
comments:
|
|
18
|
+
min-spaces-from-content: 1
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2026-10-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* amazon.de language package that parses its pages in English ([#1](https://github.com/udondan/amazon-orders-de/issues/1)) ([197a205](https://github.com/udondan/amazon-orders-de/commit/197a205192dffa09924b12dedb80985fbd6e1f99))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Miscellaneous Chores
|
|
12
|
+
|
|
13
|
+
* release 1.0.0 ([#11](https://github.com/udondan/amazon-orders-de/issues/11)) ([809d9a1](https://github.com/udondan/amazon-orders-de/commit/809d9a153538a7e5e06326d4cc7842716379d0f5))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Schroeder
|
|
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,78 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: amazon-orders-de
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: An amazon-orders language package for amazon.de, which parses its pages in English
|
|
5
|
+
Project-URL: Source, https://github.com/udondan/amazon-orders-de
|
|
6
|
+
Author: Daniel Schroeder
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: amazon-orders<5,>=4.8.1
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# amazon-orders-de
|
|
16
|
+
|
|
17
|
+
A [language package](https://amazon-orders.readthedocs.io/language-packages.html) for
|
|
18
|
+
[amazon-orders](https://github.com/alexdlaird/amazon-orders) that adds support for amazon.de.
|
|
19
|
+
|
|
20
|
+
Rather than translating `amazon-orders` into German, it asks amazon.de for its pages in English and handles what's
|
|
21
|
+
different there:
|
|
22
|
+
|
|
23
|
+
- British English labels ("Postage & Packing", "Estimated VAT")
|
|
24
|
+
- Orders whose service was cancelled
|
|
25
|
+
- Recipient addresses rendered as a plain list
|
|
26
|
+
- The payment method of an Order paid with a Gift Card and a card
|
|
27
|
+
- The Transactions page, which embeds its first page as JSON and loads further pages from an API
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pip install amazon-orders-de
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Set `language_package` and `domain` in the `amazon-orders` config (`~/.config/amazonorders/config.yml`):
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
language_package: amazon_orders_de
|
|
41
|
+
domain: amazon.de
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
or pass them in Python:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from amazonorders.conf import AmazonOrdersConfig
|
|
48
|
+
from amazonorders.orders import AmazonOrders
|
|
49
|
+
from amazonorders.session import AmazonSession
|
|
50
|
+
|
|
51
|
+
config = AmazonOrdersConfig(data={"language_package": "amazon_orders_de", "domain": "amazon.de"})
|
|
52
|
+
amazon_session = AmazonSession(config=config)
|
|
53
|
+
amazon_session.login()
|
|
54
|
+
|
|
55
|
+
orders = AmazonOrders(amazon_session).get_order_history(year=2026)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Everything else works as described in the [amazon-orders docs](https://amazon-orders.readthedocs.io).
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
With [mise](https://mise.jdx.dev/), which installs Python and uv:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
mise run install # uv sync
|
|
66
|
+
mise run lint # ruff and mypy; `mise run lint-fix` fixes what it can
|
|
67
|
+
mise run test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org/), from which release-please creates the
|
|
71
|
+
releases and the changelog.
|
|
72
|
+
|
|
73
|
+
The unit tests parse real amazon.de pages in `tests/resources`. Before a page is added there, it's anonymized with
|
|
74
|
+
`scripts/anonymize-test-resources.py`, which removes personal and product details. The raw pages and the rules file
|
|
75
|
+
with the values to replace must never be committed.
|
|
76
|
+
|
|
77
|
+
The integration tests in `tests/integration` log in to amazon.de with `AMAZON_USERNAME`, `AMAZON_PASSWORD`, and
|
|
78
|
+
`AMAZON_OTP_SECRET_KEY`, and run weekly in CI.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# amazon-orders-de
|
|
2
|
+
|
|
3
|
+
A [language package](https://amazon-orders.readthedocs.io/language-packages.html) for
|
|
4
|
+
[amazon-orders](https://github.com/alexdlaird/amazon-orders) that adds support for amazon.de.
|
|
5
|
+
|
|
6
|
+
Rather than translating `amazon-orders` into German, it asks amazon.de for its pages in English and handles what's
|
|
7
|
+
different there:
|
|
8
|
+
|
|
9
|
+
- British English labels ("Postage & Packing", "Estimated VAT")
|
|
10
|
+
- Orders whose service was cancelled
|
|
11
|
+
- Recipient addresses rendered as a plain list
|
|
12
|
+
- The payment method of an Order paid with a Gift Card and a card
|
|
13
|
+
- The Transactions page, which embeds its first page as JSON and loads further pages from an API
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pip install amazon-orders-de
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Set `language_package` and `domain` in the `amazon-orders` config (`~/.config/amazonorders/config.yml`):
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
language_package: amazon_orders_de
|
|
27
|
+
domain: amazon.de
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
or pass them in Python:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from amazonorders.conf import AmazonOrdersConfig
|
|
34
|
+
from amazonorders.orders import AmazonOrders
|
|
35
|
+
from amazonorders.session import AmazonSession
|
|
36
|
+
|
|
37
|
+
config = AmazonOrdersConfig(data={"language_package": "amazon_orders_de", "domain": "amazon.de"})
|
|
38
|
+
amazon_session = AmazonSession(config=config)
|
|
39
|
+
amazon_session.login()
|
|
40
|
+
|
|
41
|
+
orders = AmazonOrders(amazon_session).get_order_history(year=2026)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Everything else works as described in the [amazon-orders docs](https://amazon-orders.readthedocs.io).
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
With [mise](https://mise.jdx.dev/), which installs Python and uv:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
mise run install # uv sync
|
|
52
|
+
mise run lint # ruff and mypy; `mise run lint-fix` fixes what it can
|
|
53
|
+
mise run test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org/), from which release-please creates the
|
|
57
|
+
releases and the changelog.
|
|
58
|
+
|
|
59
|
+
The unit tests parse real amazon.de pages in `tests/resources`. Before a page is added there, it's anonymized with
|
|
60
|
+
`scripts/anonymize-test-resources.py`, which removes personal and product details. The raw pages and the rules file
|
|
61
|
+
with the values to replace must never be committed.
|
|
62
|
+
|
|
63
|
+
The integration tests in `tests/integration` log in to amazon.de with `AMAZON_USERNAME`, `AMAZON_PASSWORD`, and
|
|
64
|
+
`AMAZON_OTP_SECRET_KEY`, and run weekly in CI.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
An ``amazon-orders`` language package for amazon.de, which requests its pages in English. Enable it with
|
|
3
|
+
``language_package: amazon_orders_de`` and ``domain: amazon.de`` in the config.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from amazon_orders_de.constants import Constants
|
|
7
|
+
from amazon_orders_de.order import Order
|
|
8
|
+
from amazon_orders_de.selectors import Selectors
|
|
9
|
+
from amazon_orders_de.transactions import TransactionsPage
|
|
10
|
+
|
|
11
|
+
__all__ = ["Constants", "Order", "Selectors", "TransactionsPage"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from amazonorders import constants
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Constants(constants.Constants):
|
|
5
|
+
"""
|
|
6
|
+
Constants for amazon.de, requested in English.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Ask for the English version of the site, so its pages are parsed with the English words of ``amazon-orders``
|
|
10
|
+
BASE_HEADERS = {**constants.Constants.BASE_HEADERS, "Accept-Language": "en-GB,en;q=0.9"}
|
|
11
|
+
|
|
12
|
+
# amazon.de picks its language from this cookie rather than from ``Accept-Language``, and sets it again from the
|
|
13
|
+
# account's language on every response
|
|
14
|
+
REQUEST_COOKIES = {"lc-acbde": "en_GB"}
|
|
15
|
+
|
|
16
|
+
#: The internal API that loads further pages of Transactions.
|
|
17
|
+
TRANSACTION_HISTORY_API_ROUTE = "/payments-portal/data/iris/live/v1/data/manage/get-transactions"
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def TRANSACTION_HISTORY_API_URL(self) -> str:
|
|
21
|
+
return f"{self.BASE_URL}{self.TRANSACTION_HISTORY_API_ROUTE}"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from amazonorders import util
|
|
4
|
+
from amazonorders.entity import order, recipient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Recipient(recipient.Recipient):
|
|
8
|
+
"""
|
|
9
|
+
A Recipient whose address is rendered as an unclassed list, one or more lines per list item.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def _parse_address(self) -> str | None:
|
|
13
|
+
return self._parse_address_lines() or super()._parse_address()
|
|
14
|
+
|
|
15
|
+
def _parse_address_lines(self) -> str | None:
|
|
16
|
+
lines: list[str] = []
|
|
17
|
+
for tag in util.select(self.parsed, self.config.selectors.FIELD_RECIPIENT_ADDRESS_LINES_SELECTOR):
|
|
18
|
+
# A list item may hold several lines separated by <br> or, when Amazon renders the address inline,
|
|
19
|
+
# by commas
|
|
20
|
+
lines.extend(line.strip() for line in re.split(r"[\n,]", tag.get_text("\n")) if line.strip())
|
|
21
|
+
|
|
22
|
+
return "\n".join(lines) or None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Order(order.Order):
|
|
26
|
+
"""
|
|
27
|
+
An Order on amazon.de.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def _parse_payment_method(self) -> str | None:
|
|
31
|
+
# When a Gift Card was used alongside a card, prefer the card (the instrument with a number), like its last
|
|
32
|
+
# digits are
|
|
33
|
+
selectors = self.config.selectors
|
|
34
|
+
for instrument in util.select(self.parsed, selectors.FIELD_ORDER_PAYMENT_INSTRUMENT_SELECTOR):
|
|
35
|
+
name_tag = util.select_one(instrument, selectors.FIELD_ORDER_PAYMENT_INSTRUMENT_NAME_SELECTOR)
|
|
36
|
+
if name_tag and util.select_one(instrument, selectors.FIELD_ORDER_PAYMENT_INSTRUMENT_NUMBER_SELECTOR):
|
|
37
|
+
return name_tag.text.strip() or None
|
|
38
|
+
|
|
39
|
+
return super()._parse_payment_method()
|
|
40
|
+
|
|
41
|
+
def _parse_recipient(self) -> recipient.Recipient | None:
|
|
42
|
+
value = super()._parse_recipient()
|
|
43
|
+
if value is None:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
return Recipient(value.parsed, self.config)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from amazonorders import selectors
|
|
2
|
+
from amazonorders.selectors import Selector
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Selectors(selectors.Selectors):
|
|
6
|
+
"""
|
|
7
|
+
Selectors for amazon.de, requested in English, which uses British English labels.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Rendered as "Postage & Packing"
|
|
11
|
+
FIELD_ORDER_SHIPPING_TOTAL_LABELS = [*selectors.Selectors.FIELD_ORDER_SHIPPING_TOTAL_LABELS, "postage"]
|
|
12
|
+
# Rendered as "Estimated VAT"
|
|
13
|
+
FIELD_ORDER_ESTIMATED_TAX_LABELS = [*selectors.Selectors.FIELD_ORDER_ESTIMATED_TAX_LABELS, "estimated vat"]
|
|
14
|
+
|
|
15
|
+
ORDER_SKIP_TOTALS = [
|
|
16
|
+
*selectors.Selectors.ORDER_SKIP_TOTALS,
|
|
17
|
+
# Identifies an Order of a cancelled service (e.g. a cancelled installation) on the history page
|
|
18
|
+
Selector("div.yohtmlc-shipment-status-primaryText", "Service cancelled"),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
#: The payment instruments an Order was paid with, and each one's name and masked number.
|
|
22
|
+
FIELD_ORDER_PAYMENT_INSTRUMENT_SELECTOR = "[data-testid='payment-instrument']"
|
|
23
|
+
FIELD_ORDER_PAYMENT_INSTRUMENT_NAME_SELECTOR = "[data-testid='payment-instrument-name']"
|
|
24
|
+
FIELD_ORDER_PAYMENT_INSTRUMENT_NUMBER_SELECTOR = "[data-testid='payment-instrument-number']"
|
|
25
|
+
|
|
26
|
+
#: The address lines that follow the Recipient's name in an unclassed list.
|
|
27
|
+
FIELD_RECIPIENT_ADDRESS_LINES_SELECTOR = "ul > li:not(:first-child)"
|
|
28
|
+
|
|
29
|
+
#: The Transactions, embedded as JSON in the Next.js page.
|
|
30
|
+
TRANSACTION_HISTORY_NEXT_DATA_SELECTOR = "script#__NEXT_DATA__"
|