python-picnic-api2 1.2.3__tar.gz → 1.3.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.
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/dependabot.yml +8 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/workflows/ci.yaml +2 -2
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/workflows/it.yaml +2 -2
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/workflows/release.yml +2 -2
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/PKG-INFO +1 -1
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/integration_tests/test_client.py +11 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/pyproject.toml +1 -1
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/src/python_picnic_api2/client.py +24 -8
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/src/python_picnic_api2/helper.py +8 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/tests/test_client.py +8 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/uv.lock +1 -1
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.devcontainer/devcontainer.json +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.env.example +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.github/release.yml +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/.gitignore +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/LICENSE.md +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/README.rst +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/codecov.yml +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/integration_tests/__init__.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/integration_tests/test_helper.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/integration_tests/test_session.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/src/python_picnic_api2/__init__.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/src/python_picnic_api2/session.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/tests/__init__.py +0 -0
- {python_picnic_api2-1.2.3 → python_picnic_api2-1.3.0}/tests/test_session.py +0 -0
|
@@ -17,10 +17,10 @@ jobs:
|
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
|
|
19
19
|
steps:
|
|
20
|
-
- uses: actions/checkout@
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
21
|
|
|
22
22
|
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
-
uses: actions/setup-python@
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
24
|
with:
|
|
25
25
|
python-version: ${{ matrix.python-version }}
|
|
26
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-picnic-api2
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Project-URL: homepage, https://github.com/codesalatdev/python-picnic-api
|
|
5
5
|
Project-URL: repository, https://github.com/codesalatdev/python-picnic-api
|
|
6
6
|
Author-email: Mike Brink <mjh.brink@icloud.com>, CodeSalat <pypi@codesalat.dev>
|
|
@@ -56,6 +56,17 @@ def test_get_article_with_category_name():
|
|
|
56
56
|
picnic.get_article("s1018620", add_category_name=True)
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def test_get_article_by_gtin():
|
|
60
|
+
response = picnic.get_article_by_gtin("4311501044209")
|
|
61
|
+
assert response["id"] == "s1018620"
|
|
62
|
+
assert response["name"] == "Gut&Günstig H-Milch 3,5%"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_get_article_by_gtin_unknown():
|
|
66
|
+
response = picnic.get_article_by_gtin("4311501040000")
|
|
67
|
+
assert response is None
|
|
68
|
+
|
|
69
|
+
|
|
59
70
|
def test_get_cart():
|
|
60
71
|
response = picnic.get_cart()
|
|
61
72
|
assert isinstance(response, dict)
|
|
@@ -15,6 +15,10 @@ DEFAULT_URL = "https://storefront-prod.{}.picnicinternational.com/api/{}"
|
|
|
15
15
|
GLOBAL_GATEWAY_URL = "https://gateway-prod.global.picnicinternational.com"
|
|
16
16
|
DEFAULT_COUNTRY_CODE = "NL"
|
|
17
17
|
DEFAULT_API_VERSION = "15"
|
|
18
|
+
_HEADERS = {
|
|
19
|
+
"x-picnic-agent": "30100;1.15.272-15295;",
|
|
20
|
+
"x-picnic-did": "3C417201548B2E3B",
|
|
21
|
+
}
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
class PicnicAPI:
|
|
@@ -47,14 +51,7 @@ class PicnicAPI:
|
|
|
47
51
|
url = self._base_url + path
|
|
48
52
|
|
|
49
53
|
# Make the request, add special picnic headers if needed
|
|
50
|
-
headers =
|
|
51
|
-
{
|
|
52
|
-
"x-picnic-agent": "30100;1.15.272-15295;",
|
|
53
|
-
"x-picnic-did": "3C417201548B2E3B",
|
|
54
|
-
}
|
|
55
|
-
if add_picnic_headers
|
|
56
|
-
else None
|
|
57
|
-
)
|
|
54
|
+
headers = _HEADERS if add_picnic_headers else None
|
|
58
55
|
response = self.session.get(url, headers=headers).json()
|
|
59
56
|
|
|
60
57
|
if self._contains_auth_error(response):
|
|
@@ -177,5 +174,24 @@ class PicnicAPI:
|
|
|
177
174
|
tree = "\n".join(_tree_generator(self.get_categories(depth=depth)))
|
|
178
175
|
print(tree)
|
|
179
176
|
|
|
177
|
+
def get_article_by_gtin(self, etan: str, maxRedirects: int = 5):
|
|
178
|
+
# Finds the article ID for a gtin/ean (barcode).
|
|
179
|
+
|
|
180
|
+
url = "https://picnic.app/" + self._country_code.lower() + "/qr/gtin/" + etan
|
|
181
|
+
while maxRedirects > 0:
|
|
182
|
+
if url == "http://picnic.app/nl/link/store/storefront":
|
|
183
|
+
# gtin unknown
|
|
184
|
+
return None
|
|
185
|
+
r = self.session.get(url, headers=_HEADERS, allow_redirects=False)
|
|
186
|
+
maxRedirects -= 1
|
|
187
|
+
if ";id=" in r.url:
|
|
188
|
+
# found the article id
|
|
189
|
+
return self.get_article(r.url.split(";id=", 1)[1])
|
|
190
|
+
if "Location" not in r.headers:
|
|
191
|
+
# article id not found but also no futher redirect
|
|
192
|
+
return None
|
|
193
|
+
url = r.headers["Location"]
|
|
194
|
+
return None
|
|
195
|
+
|
|
180
196
|
|
|
181
197
|
__all__ = ["PicnicAPI"]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import logging
|
|
2
3
|
import re
|
|
3
4
|
|
|
4
5
|
# prefix components:
|
|
@@ -8,6 +9,8 @@ branch = "│ "
|
|
|
8
9
|
tee = "├── "
|
|
9
10
|
last = "└── "
|
|
10
11
|
|
|
12
|
+
LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
11
14
|
IMAGE_SIZES = ["small", "medium", "regular", "large", "extra-large"]
|
|
12
15
|
IMAGE_BASE_URL = "https://storefront-prod.nl.picnicinternational.com/static/images"
|
|
13
16
|
|
|
@@ -88,6 +91,8 @@ def _extract_search_results(raw_results, max_items: int = 10):
|
|
|
88
91
|
search"""
|
|
89
92
|
search_results = []
|
|
90
93
|
|
|
94
|
+
LOGGER.debug(f"Extracting search results from {raw_results}")
|
|
95
|
+
|
|
91
96
|
def find_articles(node):
|
|
92
97
|
if len(search_results) >= max_items:
|
|
93
98
|
return
|
|
@@ -101,6 +106,7 @@ def _extract_search_results(raw_results, max_items: int = 10):
|
|
|
101
106
|
**selling_unit,
|
|
102
107
|
"sole_article_id": sole_article_id,
|
|
103
108
|
}
|
|
109
|
+
LOGGER.debug(f"Found article {result_entry}")
|
|
104
110
|
search_results.append(result_entry)
|
|
105
111
|
|
|
106
112
|
for child in node.get("children", []):
|
|
@@ -112,4 +118,6 @@ def _extract_search_results(raw_results, max_items: int = 10):
|
|
|
112
118
|
body = raw_results.get("body", {})
|
|
113
119
|
find_articles(body.get("child", {}))
|
|
114
120
|
|
|
121
|
+
LOGGER.debug(f"Found {len(search_results)}/{max_items} products after extraction")
|
|
122
|
+
|
|
115
123
|
return [{"items": search_results}]
|
|
@@ -106,6 +106,14 @@ class TestClient(unittest.TestCase):
|
|
|
106
106
|
headers=PICNIC_HEADERS,
|
|
107
107
|
)
|
|
108
108
|
|
|
109
|
+
def test_get_article_by_gtin(self):
|
|
110
|
+
self.client.get_article_by_gtin("123456789")
|
|
111
|
+
self.session_mock().get.assert_called_with(
|
|
112
|
+
"https://picnic.app/nl/qr/gtin/123456789",
|
|
113
|
+
headers=PICNIC_HEADERS,
|
|
114
|
+
allow_redirects=False,
|
|
115
|
+
)
|
|
116
|
+
|
|
109
117
|
def test_get_cart(self):
|
|
110
118
|
self.client.get_cart()
|
|
111
119
|
self.session_mock().get.assert_called_with(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|