leancpms-cli 0.1.0__tar.gz → 0.1.2__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.
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/PKG-INFO +4 -4
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/README.md +3 -3
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/manifest/schema.py +43 -1
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/pyproject.toml +1 -1
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/.gitignore +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/LICENSE +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/__init__.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/cli/__init__.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/cli/client.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/cli/login.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/cli/main.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/manifest/__init__.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/cpms/manifest/documents.py +0 -0
- {leancpms_cli-0.1.0 → leancpms_cli-0.1.2}/hatch_build.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: leancpms-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: The cpms command alone: a client of a LeanCPMS instance's JSON API
|
|
5
5
|
License-Expression: AGPL-3.0-or-later
|
|
6
6
|
License-File: LICENSE
|
|
@@ -34,8 +34,8 @@ Until then, from a GitHub release (a tag `cli-v<version>`) or straight
|
|
|
34
34
|
from the repository:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
gh release download cli-v0.1.
|
|
38
|
-
uv tool install ./leancpms_cli-0.1.
|
|
37
|
+
gh release download cli-v0.1.2 --repo forsslund/leancpms --pattern '*.whl'
|
|
38
|
+
uv tool install ./leancpms_cli-0.1.2-py3-none-any.whl
|
|
39
39
|
# or
|
|
40
40
|
uv tool install "git+https://github.com/forsslund/leancpms#subdirectory=cli"
|
|
41
41
|
```
|
|
@@ -194,7 +194,7 @@ Raise `version` in both `cli/pyproject.toml` and the root `pyproject.toml`
|
|
|
194
194
|
(a test keeps them equal), then tag and push:
|
|
195
195
|
|
|
196
196
|
```bash
|
|
197
|
-
git tag cli-v0.1.
|
|
197
|
+
git tag cli-v0.1.2 && git push origin cli-v0.1.2
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
`.github/workflows/release-cli.yml` builds the wheel and sdist, smoke-tests
|
|
@@ -22,8 +22,8 @@ Until then, from a GitHub release (a tag `cli-v<version>`) or straight
|
|
|
22
22
|
from the repository:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
gh release download cli-v0.1.
|
|
26
|
-
uv tool install ./leancpms_cli-0.1.
|
|
25
|
+
gh release download cli-v0.1.2 --repo forsslund/leancpms --pattern '*.whl'
|
|
26
|
+
uv tool install ./leancpms_cli-0.1.2-py3-none-any.whl
|
|
27
27
|
# or
|
|
28
28
|
uv tool install "git+https://github.com/forsslund/leancpms#subdirectory=cli"
|
|
29
29
|
```
|
|
@@ -182,7 +182,7 @@ Raise `version` in both `cli/pyproject.toml` and the root `pyproject.toml`
|
|
|
182
182
|
(a test keeps them equal), then tag and push:
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
|
-
git tag cli-v0.1.
|
|
185
|
+
git tag cli-v0.1.2 && git push origin cli-v0.1.2
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
`.github/workflows/release-cli.yml` builds the wheel and sdist, smoke-tests
|
|
@@ -96,6 +96,26 @@ class Theme(Strict):
|
|
|
96
96
|
logo_url: Annotated[str, StringConstraints(pattern=r"^https://\S+$", max_length=500)] | None = None
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
# A bare host name as a browser sends it: lower case, no scheme, port or path (charge.example.org).
|
|
100
|
+
DriverHost = Annotated[
|
|
101
|
+
str,
|
|
102
|
+
StringConstraints(
|
|
103
|
+
max_length=253, pattern=r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$"
|
|
104
|
+
),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# A browser origin of the organization's own driver front end (architecture §3.1): https, a host and
|
|
109
|
+
# perhaps a port, nothing after it. http only for localhost, for development.
|
|
110
|
+
DriverOrigin = Annotated[
|
|
111
|
+
str,
|
|
112
|
+
StringConstraints(
|
|
113
|
+
max_length=300,
|
|
114
|
+
pattern=r"^(https://[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*|http://localhost)(:[0-9]{1,5})?$",
|
|
115
|
+
),
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
|
|
99
119
|
class Organization(Strict):
|
|
100
120
|
key: OrganizationKey
|
|
101
121
|
name: str
|
|
@@ -109,6 +129,19 @@ class Organization(Strict):
|
|
|
109
129
|
# The eMI3 operator id, e.g. "SE*KEL": the OCPI party (country code, party id) and the EVSE ids' prefix.
|
|
110
130
|
operator_id: Annotated[str, StringConstraints(pattern=r"^[A-Z]{2}\*[A-Z0-9]{3}$")] | None = None
|
|
111
131
|
theme: Theme | None = None
|
|
132
|
+
# The hosts on which this organization's driver pages are its own: `/` there lists its sites.
|
|
133
|
+
driver_hosts: list[DriverHost] = []
|
|
134
|
+
# The organization's own driver front ends, on domains of its own: they may call the driver API's
|
|
135
|
+
# start and session endpoints from a browser, and payment may return to them (architecture §3.1).
|
|
136
|
+
driver_origins: list[DriverOrigin] = []
|
|
137
|
+
|
|
138
|
+
@model_validator(mode="after")
|
|
139
|
+
def _unique_hosts(self): # type: ignore[no-untyped-def]
|
|
140
|
+
if len(set(self.driver_hosts)) != len(self.driver_hosts):
|
|
141
|
+
raise ValueError("driver_hosts: a host is listed twice")
|
|
142
|
+
if len(set(self.driver_origins)) != len(self.driver_origins):
|
|
143
|
+
raise ValueError("driver_origins: an origin is listed twice")
|
|
144
|
+
return self
|
|
112
145
|
|
|
113
146
|
|
|
114
147
|
class OrganizationDocument(Strict):
|
|
@@ -221,11 +254,17 @@ class PricelistDocument(Strict):
|
|
|
221
254
|
def _references(self): # type: ignore[no-untyped-def]
|
|
222
255
|
_unique(self.tariffs, "key", "tariff")
|
|
223
256
|
_unique(self.plans, "key", "plan")
|
|
224
|
-
tariffs = {t.key for t in self.tariffs}
|
|
257
|
+
tariffs = {t.key: t for t in self.tariffs}
|
|
225
258
|
plans = {p.key: p for p in self.plans}
|
|
226
259
|
for p in self.plans:
|
|
227
260
|
if p.tariff not in tariffs:
|
|
228
261
|
raise ValueError(f"plan {p.key!r} names unknown tariff {p.tariff!r}")
|
|
262
|
+
roaming = {p.key for p in self.plans if p.requires == "roaming"}
|
|
263
|
+
if self.channels.roaming:
|
|
264
|
+
roaming.add(self.channels.roaming.default)
|
|
265
|
+
for key in sorted(roaming & plans.keys()):
|
|
266
|
+
if tariffs[plans[key].tariff].kind != "flat": # partners take fixed prices (architecture §6.1)
|
|
267
|
+
raise ValueError(f"roaming plan {key!r} needs a flat tariff, not {plans[key].tariff!r}")
|
|
229
268
|
for key in self.channels.adhoc:
|
|
230
269
|
if key not in plans:
|
|
231
270
|
raise ValueError(f"channel adhoc names unknown plan {key!r}")
|
|
@@ -339,6 +378,9 @@ class Site(Strict):
|
|
|
339
378
|
postal_code: str | None = None
|
|
340
379
|
country: Annotated[str, StringConstraints(pattern=r"^[A-Z]{3}$")] = "SWE" # ISO 3166-1 alpha-3
|
|
341
380
|
coordinates: Coordinates | None = None
|
|
381
|
+
# OCPI 2.2.1 ParkingType, shown by Nobil and roaming apps; optional, also when published.
|
|
382
|
+
parking_type: Literal["along_motorway", "parking_garage", "parking_lot", "on_driveway", "on_street",
|
|
383
|
+
"underground_garage"] | None = None
|
|
342
384
|
timezone: TimeZone | None = None # defaults to the organization's display_timezone
|
|
343
385
|
publish: bool = False # in OCPI (Nobil, roaming) only when true
|
|
344
386
|
notes: str | None = None
|
|
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
|