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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: leancpms-cli
3
- Version: 0.1.0
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.0 --repo forsslund/leancpms --pattern '*.whl'
38
- uv tool install ./leancpms_cli-0.1.0-py3-none-any.whl
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.1 && git push origin cli-v0.1.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.0 --repo forsslund/leancpms --pattern '*.whl'
26
- uv tool install ./leancpms_cli-0.1.0-py3-none-any.whl
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.1 && git push origin cli-v0.1.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "leancpms-cli"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "The cpms command alone: a client of a LeanCPMS instance's JSON API"
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-or-later"
File without changes
File without changes