swegov-opendata 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.
- swegov_opendata-0.1.0/PKG-INFO +58 -0
- swegov_opendata-0.1.0/README.md +47 -0
- swegov_opendata-0.1.0/pyproject.toml +36 -0
- swegov_opendata-0.1.0/pyproject.toml.orig +32 -0
- swegov_opendata-0.1.0/src/swegov_opendata/__init__.py +0 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/__init__.py +0 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/aktivitet.py +24 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/bilaga.py +44 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/datasetlista.py +59 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/debatt.py +54 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/dokument.py +235 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/dokumentlista.py +147 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/forslag.py +73 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/intressent.py +19 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/media.py +33 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/person.py +0 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/referens.py +23 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/uppgift.py +22 -0
- swegov_opendata-0.1.0/src/swegov_opendata/dokument/votering.py +34 -0
- swegov_opendata-0.1.0/src/swegov_opendata/shared.py +56 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swegov-opendata
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Data models for handling Swedish riksdagens öppna data (open data)
|
|
5
|
+
Author: Språkbanken
|
|
6
|
+
Author-email: Språkbanken <sb-info@svenska.gu.se>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Dist: pydantic>=2.12.5
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# swegov-opendata
|
|
13
|
+
|
|
14
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
15
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
16
|
+
[](https://pypi.org/project/swegov-opendata)
|
|
17
|
+
|
|
18
|
+
[](https://github.com/spraakbanken/getting-started/blob/main/scorecard.md)
|
|
19
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
20
|
+
|
|
21
|
+
[](https://codecov.io/gh/spraakbanken/swegov-opendata-py)
|
|
22
|
+
|
|
23
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/check.yml)
|
|
24
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/release.yml)
|
|
25
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/rolling.yml)
|
|
26
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/test.yml)
|
|
27
|
+
|
|
28
|
+
Pydantic models for [Riksdagens Öppna data](http://data.riksdagen.se/).
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
Add this to your project with
|
|
33
|
+
|
|
34
|
+
```shell
|
|
35
|
+
uv add swegov-opendata
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Minimum Supported Python Version Policy
|
|
39
|
+
|
|
40
|
+
The Minimum Supported Python Version is fixed for a given minor (1.x)
|
|
41
|
+
version. However it can be increased when bumping minor versions, i.e. going
|
|
42
|
+
from 1.0 to 1.1 allows us to increase the Minimum Supported Python Version. Users unable to increase their
|
|
43
|
+
Python version can use an older minor version instead. Below is a list of sparv-sbx-corpus-statistics versions
|
|
44
|
+
and their Minimum Supported Python Version:
|
|
45
|
+
|
|
46
|
+
- v0.1: Python 3.11.
|
|
47
|
+
|
|
48
|
+
Note however that sparv-sbx-corpus-statistics also has dependencies, which might have different MSRV
|
|
49
|
+
policies. We try to stick to the above policy when updating dependencies, but
|
|
50
|
+
this is not always possible.
|
|
51
|
+
|
|
52
|
+
## Changelog
|
|
53
|
+
|
|
54
|
+
This project keeps a [changelog](https://github.com/spraakbanken/swegov-opendata-py/blob/0.1.0/CHANGELOG.md).
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
This repository is licensed under the [MIT](https://github.com/spraakbanken/swegov-opendata-py/blob/0.1.0/LICENSE) license.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# swegov-opendata
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
4
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
5
|
+
[](https://pypi.org/project/swegov-opendata)
|
|
6
|
+
|
|
7
|
+
[](https://github.com/spraakbanken/getting-started/blob/main/scorecard.md)
|
|
8
|
+
[](https://pypi.org/project/swegov-opendata/)
|
|
9
|
+
|
|
10
|
+
[](https://codecov.io/gh/spraakbanken/swegov-opendata-py)
|
|
11
|
+
|
|
12
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/check.yml)
|
|
13
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/release.yml)
|
|
14
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/rolling.yml)
|
|
15
|
+
[](https://github.com/spraakbanken/swegov-opendata-py/actions/workflows/test.yml)
|
|
16
|
+
|
|
17
|
+
Pydantic models for [Riksdagens Öppna data](http://data.riksdagen.se/).
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
Add this to your project with
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
uv add swegov-opendata
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Minimum Supported Python Version Policy
|
|
28
|
+
|
|
29
|
+
The Minimum Supported Python Version is fixed for a given minor (1.x)
|
|
30
|
+
version. However it can be increased when bumping minor versions, i.e. going
|
|
31
|
+
from 1.0 to 1.1 allows us to increase the Minimum Supported Python Version. Users unable to increase their
|
|
32
|
+
Python version can use an older minor version instead. Below is a list of sparv-sbx-corpus-statistics versions
|
|
33
|
+
and their Minimum Supported Python Version:
|
|
34
|
+
|
|
35
|
+
- v0.1: Python 3.11.
|
|
36
|
+
|
|
37
|
+
Note however that sparv-sbx-corpus-statistics also has dependencies, which might have different MSRV
|
|
38
|
+
policies. We try to stick to the above policy when updating dependencies, but
|
|
39
|
+
this is not always possible.
|
|
40
|
+
|
|
41
|
+
## Changelog
|
|
42
|
+
|
|
43
|
+
This project keeps a [changelog](https://github.com/spraakbanken/swegov-opendata-py/blob/0.1.0/CHANGELOG.md).
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
This repository is licensed under the [MIT](https://github.com/spraakbanken/swegov-opendata-py/blob/0.1.0/LICENSE) license.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "swegov-opendata"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Data models for handling Swedish riksdagens öppna data (open data)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">= 3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
dependencies = ["pydantic>=2.12.5"]
|
|
9
|
+
|
|
10
|
+
[[project.authors]]
|
|
11
|
+
name = "Språkbanken"
|
|
12
|
+
email = "sb-info@svenska.gu.se"
|
|
13
|
+
|
|
14
|
+
[dependency-groups]
|
|
15
|
+
lint = [
|
|
16
|
+
"ruff>=0.16.9",
|
|
17
|
+
"ty>=0.0.84",
|
|
18
|
+
]
|
|
19
|
+
test = [
|
|
20
|
+
"pytest>=9.0.3",
|
|
21
|
+
"pytest-cov>=7.0.0",
|
|
22
|
+
"syrupy>=6.0.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[[dependency-groups.dev]]
|
|
26
|
+
include-group = "lint"
|
|
27
|
+
|
|
28
|
+
[[dependency-groups.dev]]
|
|
29
|
+
include-group = "test"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
33
|
+
build-backend = "uv_build"
|
|
34
|
+
|
|
35
|
+
[tool.uv]
|
|
36
|
+
exclude-newer = "P7D"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "swegov-opendata"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Data models for handling Swedish riksdagens öppna data (open data)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">= 3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Språkbanken", email = "sb-info@svenska.gu.se" },
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pydantic>=2.12.5",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = [{ include-group = "lint" }, { include-group = "test" }]
|
|
17
|
+
lint = [
|
|
18
|
+
"ruff>=0.16.9",
|
|
19
|
+
"ty>=0.0.84",
|
|
20
|
+
]
|
|
21
|
+
test = [
|
|
22
|
+
"pytest>=9.0.3",
|
|
23
|
+
"pytest-cov>=7.0.0",
|
|
24
|
+
"syrupy>=6.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.uv]
|
|
32
|
+
exclude-newer = "P7D"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Aktivitet(pydantic.BaseModel):
|
|
10
|
+
datum: datetime.datetime
|
|
11
|
+
hangar_id: str | None = None
|
|
12
|
+
kod: str
|
|
13
|
+
namn: str
|
|
14
|
+
status: str | None
|
|
15
|
+
ordning: str
|
|
16
|
+
process: str | None
|
|
17
|
+
|
|
18
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DokAktivitet(pydantic.BaseModel):
|
|
22
|
+
aktivitet: t.Annotated[list[Aktivitet], pydantic.BeforeValidator(shared.ensure_list)]
|
|
23
|
+
|
|
24
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FilTyp(enum.StrEnum):
|
|
10
|
+
Doc = "doc"
|
|
11
|
+
Docx = "docx"
|
|
12
|
+
Htm = "htm"
|
|
13
|
+
Pdf = "pdf"
|
|
14
|
+
|
|
15
|
+
def __str__(self) -> str:
|
|
16
|
+
return self.value
|
|
17
|
+
|
|
18
|
+
def __repr__(self) -> str:
|
|
19
|
+
return str(self)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def _missing_(cls, value) -> t.Self:
|
|
23
|
+
for member in cls:
|
|
24
|
+
if member.value == value.lower():
|
|
25
|
+
return member
|
|
26
|
+
raise ValueError("Unknown value '{value}'")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Bilaga(pydantic.BaseModel):
|
|
30
|
+
dok_id: str
|
|
31
|
+
titel: str | None = None
|
|
32
|
+
subtitel: str | None
|
|
33
|
+
filnamn: str
|
|
34
|
+
filstorlek: str
|
|
35
|
+
filtyp: FilTyp
|
|
36
|
+
fil_url: pydantic.HttpUrl
|
|
37
|
+
|
|
38
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DokBilaga(pydantic.BaseModel):
|
|
42
|
+
bilaga: t.Annotated[list[Bilaga], pydantic.BeforeValidator(shared.ensure_list)]
|
|
43
|
+
|
|
44
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import xml.etree.ElementTree as ET
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from xml.etree.ElementTree import Element
|
|
5
|
+
|
|
6
|
+
from pydantic import HttpUrl
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DataFormat(enum.StrEnum):
|
|
10
|
+
json = "json"
|
|
11
|
+
csv = "csv"
|
|
12
|
+
csvt = "csvt"
|
|
13
|
+
html = "html"
|
|
14
|
+
sql = "sql"
|
|
15
|
+
text = "text"
|
|
16
|
+
xml = "xml"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Dataset:
|
|
21
|
+
format: DataFormat
|
|
22
|
+
url: HttpUrl
|
|
23
|
+
uppdaterad: str
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_elem(cls, elem: Element) -> "Dataset":
|
|
27
|
+
if elem.tag != "dataset":
|
|
28
|
+
raise RuntimeError(f"Expected <dataset>, found <{elem.tag}>")
|
|
29
|
+
format_ = None
|
|
30
|
+
url = None
|
|
31
|
+
uppdaterad = None
|
|
32
|
+
for child in elem:
|
|
33
|
+
if child.tag == "format":
|
|
34
|
+
format_ = DataFormat(child.text)
|
|
35
|
+
elif child.tag == "url":
|
|
36
|
+
url = child.text
|
|
37
|
+
elif child.tag == "uppdaterad":
|
|
38
|
+
uppdaterad = child.text
|
|
39
|
+
|
|
40
|
+
if format_ is None:
|
|
41
|
+
raise RuntimeError("Dataset is missing 'format'")
|
|
42
|
+
if url is None:
|
|
43
|
+
raise RuntimeError("Dataset is missing 'url'")
|
|
44
|
+
if uppdaterad is None:
|
|
45
|
+
raise RuntimeError("Dataset is missing 'uppdaterad'")
|
|
46
|
+
return cls(format=format_, url=HttpUrl(url), uppdaterad=uppdaterad)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class DatasetLista:
|
|
51
|
+
datasets: list[Dataset]
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_xml(cls, xml: str) -> "DatasetLista":
|
|
55
|
+
root = ET.fromstring(xml)
|
|
56
|
+
if root.tag != "datasetlista":
|
|
57
|
+
raise RuntimeError(f"Expected <datasetlista>, found <{root.tag}>")
|
|
58
|
+
datasets = [Dataset.from_elem(child) for child in root]
|
|
59
|
+
return cls(datasets=datasets)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Anforande(pydantic.BaseModel):
|
|
10
|
+
anf_beteckning: str | None
|
|
11
|
+
anf_datum: datetime.datetime
|
|
12
|
+
anf_hangar_id: t.Annotated[int | None, pydantic.BeforeValidator(shared.ensure_int_opt)]
|
|
13
|
+
anf_id: str | None = None
|
|
14
|
+
anf_klockslag: str | None
|
|
15
|
+
anf_nummer: str
|
|
16
|
+
anf_rm: str | None
|
|
17
|
+
anf_sekunder: int
|
|
18
|
+
anf_text: str | None = None
|
|
19
|
+
anf_typ: str | None
|
|
20
|
+
anf_video_id: str
|
|
21
|
+
datumtid: datetime.datetime
|
|
22
|
+
debatt_id: str | None = None
|
|
23
|
+
debatt_titel: str | None = None
|
|
24
|
+
debatt_typ: str | None = None
|
|
25
|
+
dok_beteckning: str | None = None
|
|
26
|
+
dok_id: str | None = None
|
|
27
|
+
dok_intressent: str | None = None
|
|
28
|
+
intressent_id: str | None = None
|
|
29
|
+
kon: str | None = None
|
|
30
|
+
parent_id: str | None = None
|
|
31
|
+
parti: str | None = None
|
|
32
|
+
startpos: int | None = None
|
|
33
|
+
systemdatum: datetime.datetime | None = None
|
|
34
|
+
talare: str | None
|
|
35
|
+
talare_kort: str | None = None
|
|
36
|
+
tumnagel: t.Annotated[list[str] | None, pydantic.BeforeValidator(shared.ensure_list_opt)] = (
|
|
37
|
+
None
|
|
38
|
+
)
|
|
39
|
+
tumnagel_mini: str | None = None
|
|
40
|
+
tumnagel_liten: str | None = None
|
|
41
|
+
tumnagel_medium: str | None = None
|
|
42
|
+
tumnagel_stor: str | None = None
|
|
43
|
+
video_id: str | None
|
|
44
|
+
video_url: str
|
|
45
|
+
videostatus: int | None = None
|
|
46
|
+
voteringspunkt: str | None = None
|
|
47
|
+
|
|
48
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Debatt(pydantic.BaseModel):
|
|
52
|
+
anforande: t.Annotated[list[Anforande], pydantic.BeforeValidator(shared.ensure_list)]
|
|
53
|
+
|
|
54
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import enum
|
|
3
|
+
import typing as t
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pydantic
|
|
7
|
+
|
|
8
|
+
from swegov_opendata.dokument.aktivitet import DokAktivitet
|
|
9
|
+
from swegov_opendata.dokument.bilaga import DokBilaga
|
|
10
|
+
from swegov_opendata.dokument.debatt import Debatt
|
|
11
|
+
from swegov_opendata.dokument.forslag import (
|
|
12
|
+
DokForslag,
|
|
13
|
+
DokMotForslag,
|
|
14
|
+
DokUtskottsForslag,
|
|
15
|
+
)
|
|
16
|
+
from swegov_opendata.dokument.intressent import DokIntressent
|
|
17
|
+
from swegov_opendata.dokument.media import WebbMedia
|
|
18
|
+
from swegov_opendata.dokument.referens import DokReferens
|
|
19
|
+
from swegov_opendata.dokument.uppgift import DokUppgift
|
|
20
|
+
|
|
21
|
+
_DOKUMENT_TYP_MAP: dict[str, str] = {
|
|
22
|
+
"bet": "betänkande",
|
|
23
|
+
"dir": "kommittéedirektiv",
|
|
24
|
+
"ds": "departementsserien",
|
|
25
|
+
"eundok": "utskottsdokument",
|
|
26
|
+
"f-lista": "föredragningslista",
|
|
27
|
+
"fr": "skriftlig fråga",
|
|
28
|
+
"frs": "svar på skriftlig fråga",
|
|
29
|
+
"frsrdg": "framställningar",
|
|
30
|
+
"ip": "interpellation",
|
|
31
|
+
"kf-lista": "kallelser och föredragningslistor",
|
|
32
|
+
"KOM": "EU-förslag",
|
|
33
|
+
"komm": "kommittéeberättelser",
|
|
34
|
+
"prop": "proposition",
|
|
35
|
+
"prot": "protokoll",
|
|
36
|
+
"rfr": "rapporter från riksdagen",
|
|
37
|
+
"rir": "riksrevisionens granskningsrapporter",
|
|
38
|
+
"rskr": "riksdagsskrivelse",
|
|
39
|
+
"sou": "statens offentliga utredningar",
|
|
40
|
+
"t-lista": "talarlista",
|
|
41
|
+
"urf": "utredningar från Riksdagsförvaltningen",
|
|
42
|
+
"utskottsdokument": "utskottsdokument",
|
|
43
|
+
"yttr": "yttrande",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DokumentTyp(enum.StrEnum):
|
|
48
|
+
Bet = "bet"
|
|
49
|
+
Diarie = "diarie"
|
|
50
|
+
Dir = "dir"
|
|
51
|
+
Ds = "ds"
|
|
52
|
+
EuDokument = "eu-dokument"
|
|
53
|
+
Eunbil = "eunbil"
|
|
54
|
+
Eundok = "eundok"
|
|
55
|
+
FLista = "f-lista"
|
|
56
|
+
Fpm = "fpm"
|
|
57
|
+
Fr = "fr"
|
|
58
|
+
Frs = "frs"
|
|
59
|
+
Frsrdg = "frsrdg"
|
|
60
|
+
Ip = "ip"
|
|
61
|
+
Kalakt = "kalakt"
|
|
62
|
+
Kammakt = "kammakt"
|
|
63
|
+
KfLista = "kf-lista"
|
|
64
|
+
Kom = "KOM"
|
|
65
|
+
Komm = "komm"
|
|
66
|
+
Mot = "mot"
|
|
67
|
+
Prop = "prop"
|
|
68
|
+
Prot = "prot"
|
|
69
|
+
Rfr = "rfr"
|
|
70
|
+
Rir = "rir"
|
|
71
|
+
Rskr = "rskr"
|
|
72
|
+
Samtr = "samtr"
|
|
73
|
+
Sfs = "sfs"
|
|
74
|
+
Skrsam = "skrsam"
|
|
75
|
+
Sou = "sou"
|
|
76
|
+
TLista = "t-lista"
|
|
77
|
+
Urf = "urf"
|
|
78
|
+
Utskottsdokument = "utskottsdokument"
|
|
79
|
+
Uttag = "uttag"
|
|
80
|
+
Yttr = "yttr"
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def _missing_(cls, value) -> t.Self:
|
|
84
|
+
for member in cls:
|
|
85
|
+
if member.value == value.lower():
|
|
86
|
+
return member
|
|
87
|
+
raise ValueError(f"Unknown value '{value}'.")
|
|
88
|
+
|
|
89
|
+
def short_name(self) -> str:
|
|
90
|
+
return self.value
|
|
91
|
+
|
|
92
|
+
def long_name(self) -> str:
|
|
93
|
+
return _DOKUMENT_TYP_MAP.get(self.value) or self.value
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def split_space_take_first(value: t.Any) -> t.Any:
|
|
97
|
+
if isinstance(value, str):
|
|
98
|
+
return "" if not value else value.split()[0]
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def ensure_none_if_empty(value: t.Any) -> t.Any:
|
|
103
|
+
if isinstance(value, str):
|
|
104
|
+
return None if value == "" else value
|
|
105
|
+
return value
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def ensure_string(value: t.Any) -> t.Any:
|
|
109
|
+
def _ensure_str(v: t.Any) -> str:
|
|
110
|
+
return v if isinstance(v, str) else str(v)
|
|
111
|
+
|
|
112
|
+
if isinstance(value, list):
|
|
113
|
+
if len(value) == 0:
|
|
114
|
+
return ""
|
|
115
|
+
return _ensure_str(value[0])
|
|
116
|
+
return _ensure_str(value)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Dokument(pydantic.BaseModel):
|
|
120
|
+
beteckning: str | None
|
|
121
|
+
datum: t.Annotated[
|
|
122
|
+
datetime.date | None,
|
|
123
|
+
pydantic.BeforeValidator(split_space_take_first),
|
|
124
|
+
pydantic.BeforeValidator(ensure_none_if_empty),
|
|
125
|
+
]
|
|
126
|
+
debattnamn: str | None = None
|
|
127
|
+
dok_id: str
|
|
128
|
+
doktyp: t.Annotated[str, pydantic.BeforeValidator(ensure_string)]
|
|
129
|
+
dokument_url_html: pydantic.HttpUrl
|
|
130
|
+
dokument_url_text: pydantic.HttpUrl
|
|
131
|
+
dokumentnamn: str | None = None
|
|
132
|
+
dokumentstatus_url_xml: pydantic.HttpUrl
|
|
133
|
+
hangar_id: str
|
|
134
|
+
html: str | None = None
|
|
135
|
+
htmlformat: str | None
|
|
136
|
+
images: str | None = None
|
|
137
|
+
metadata: str | None = None
|
|
138
|
+
mottagare: str | None
|
|
139
|
+
nummer: str | None
|
|
140
|
+
organ: str | None
|
|
141
|
+
pretext: str | None = None
|
|
142
|
+
publicerad: t.Annotated[
|
|
143
|
+
datetime.datetime | None, pydantic.BeforeValidator(ensure_none_if_empty)
|
|
144
|
+
]
|
|
145
|
+
relaterat_id: str | None
|
|
146
|
+
rm: str | None
|
|
147
|
+
rubriker: str | None = None
|
|
148
|
+
slutnummer: str | None
|
|
149
|
+
source: str | None
|
|
150
|
+
sourceid: str | None
|
|
151
|
+
status: str | None
|
|
152
|
+
subtitel: str | None
|
|
153
|
+
subtyp: str | None
|
|
154
|
+
# systemdatum: datetime.datetime
|
|
155
|
+
systemdatum: t.Annotated[
|
|
156
|
+
datetime.datetime | None,
|
|
157
|
+
pydantic.BeforeValidator(split_space_take_first),
|
|
158
|
+
pydantic.BeforeValidator(ensure_none_if_empty),
|
|
159
|
+
]
|
|
160
|
+
tempbeteckning: str | None
|
|
161
|
+
text: str | None = None
|
|
162
|
+
titel: str | None
|
|
163
|
+
typ: t.Annotated[DokumentTyp | None, pydantic.BeforeValidator(ensure_none_if_empty)]
|
|
164
|
+
typrubrik: str | None = None
|
|
165
|
+
utskottsforslag_url_xml: str | None = None
|
|
166
|
+
|
|
167
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
168
|
+
|
|
169
|
+
def get_rm(self) -> str:
|
|
170
|
+
return self.rm or "missing"
|
|
171
|
+
|
|
172
|
+
def get_typ_short_name(self) -> str:
|
|
173
|
+
return self.typ.short_name() if self.typ else "missing"
|
|
174
|
+
|
|
175
|
+
def get_typ_long_name(self) -> str:
|
|
176
|
+
return self.typ.long_name() if self.typ else "missing"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class DokumentStatus(pydantic.BaseModel):
|
|
180
|
+
debatt: Debatt | None = None
|
|
181
|
+
dokaktivitet: DokAktivitet | None = None
|
|
182
|
+
dokbilaga: DokBilaga | None = None
|
|
183
|
+
dokforslag: DokForslag | None = None
|
|
184
|
+
dokintressent: DokIntressent | None = None
|
|
185
|
+
dokmotforslag: DokMotForslag | None = None
|
|
186
|
+
dokreferens: DokReferens | None = None
|
|
187
|
+
dokument: Dokument
|
|
188
|
+
dokuppgift: DokUppgift | None = None
|
|
189
|
+
dokutskottsforslag: DokUtskottsForslag | None = None
|
|
190
|
+
webbmedia: WebbMedia | None = None
|
|
191
|
+
|
|
192
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class DokumentStatusPage(pydantic.BaseModel):
|
|
196
|
+
dokumentstatus: DokumentStatus | None
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def deserialize_from_str(cls, src: str | bytes | bytearray) -> "DokumentStatusPage":
|
|
200
|
+
try:
|
|
201
|
+
return DokumentStatusPage.model_validate_json(src)
|
|
202
|
+
except pydantic.ValidationError as exc:
|
|
203
|
+
print(f"error type: '{exc.errors()[0]['type']}'")
|
|
204
|
+
exc_msg = exc.errors()[0]["msg"]
|
|
205
|
+
print(f"error msg: '{exc_msg}'")
|
|
206
|
+
print(f"{exc=}")
|
|
207
|
+
if exc.errors()[0]["type"] != "json_invalid":
|
|
208
|
+
raise
|
|
209
|
+
column = int(exc_msg.split()[-1])
|
|
210
|
+
print(f"{src[column - 50:column + 50]=}")
|
|
211
|
+
new_src: str = src if isinstance(src, str) else src.decode("utf-8")
|
|
212
|
+
new_src = (
|
|
213
|
+
new_src.replace("{/* RESERVATIONSTEXT */}", r'""')
|
|
214
|
+
.replace("/* RESERVATIONSTEXT */", "")
|
|
215
|
+
.replace('\\"', "TEMPSLASH")
|
|
216
|
+
.replace(r"\"", '"')
|
|
217
|
+
.replace("TEMPSLASH", r"\"")
|
|
218
|
+
.replace(r"\\r\\n", r"\r\n")
|
|
219
|
+
.replace('"p":}', '"p":""}')
|
|
220
|
+
)
|
|
221
|
+
try:
|
|
222
|
+
return DokumentStatusPage.model_validate_json(new_src)
|
|
223
|
+
except pydantic.ValidationError as exc:
|
|
224
|
+
dt = datetime.datetime.now(tz=datetime.UTC).strftime("%Y-%m-%dT%H%M%S")
|
|
225
|
+
Path(f"new_src-{dt}.json").write_text(new_src)
|
|
226
|
+
print(f"error type: '{exc.errors()[0]['type']}'")
|
|
227
|
+
exc_msg = exc.errors()[0]["msg"]
|
|
228
|
+
print(f"error msg: '{exc_msg}'")
|
|
229
|
+
print(f"{exc=}")
|
|
230
|
+
if exc.errors()[0]["type"] != "json_invalid":
|
|
231
|
+
raise
|
|
232
|
+
column = int(exc_msg.split()[-1])
|
|
233
|
+
print(f"{new_src[column - 50:column + 50]=}")
|
|
234
|
+
# new_src = src.replace("{/* RESERVATIONSTEXT */}", "")
|
|
235
|
+
raise
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
from swegov_opendata.dokument.dokument import DokumentTyp
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Fil(pydantic.BaseModel):
|
|
11
|
+
typ: str
|
|
12
|
+
namn: str
|
|
13
|
+
storlek: int
|
|
14
|
+
url: str
|
|
15
|
+
|
|
16
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FilBilaga(pydantic.BaseModel):
|
|
20
|
+
fil: t.Annotated[list[Fil] | None, pydantic.BeforeValidator(shared.ensure_list_opt)]
|
|
21
|
+
|
|
22
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SokData(pydantic.BaseModel):
|
|
26
|
+
titel: str
|
|
27
|
+
undertitel: str
|
|
28
|
+
soktyp: str | None
|
|
29
|
+
statusrad: str
|
|
30
|
+
brodsmula: str | None
|
|
31
|
+
parti_kod: str | None
|
|
32
|
+
parti_namn: str | None
|
|
33
|
+
parti_website_url: str | None
|
|
34
|
+
parti_website_namn: str | None
|
|
35
|
+
parti_epost: str | None
|
|
36
|
+
parti_telefon: str | None
|
|
37
|
+
parti_telefontider: str | None
|
|
38
|
+
parti_logotyp_img_id: str | None
|
|
39
|
+
parti_logotyp_img_url: str | None
|
|
40
|
+
parti_logotyp_img_alt: str | None
|
|
41
|
+
parti_mandat: str | None
|
|
42
|
+
kalenderprio: str | None
|
|
43
|
+
|
|
44
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DokumentListaDokument(pydantic.BaseModel):
|
|
48
|
+
ardometyp: str | None
|
|
49
|
+
audio: str | None
|
|
50
|
+
avdelning: str
|
|
51
|
+
avdelningar: shared.Avdelningar | None
|
|
52
|
+
beredningsdag: str | None
|
|
53
|
+
beslutad: str | None
|
|
54
|
+
beslutsdag: str | None
|
|
55
|
+
beteckning: str | None
|
|
56
|
+
database: str
|
|
57
|
+
datum: datetime.date
|
|
58
|
+
debatt: str | None
|
|
59
|
+
debattdag: str | None
|
|
60
|
+
debattgrupp: str | None
|
|
61
|
+
debattnamn: str
|
|
62
|
+
debattsekunder: str | None
|
|
63
|
+
dok_id: str
|
|
64
|
+
dokintressent: str | None
|
|
65
|
+
doktyp: str
|
|
66
|
+
dokument_url_html: str
|
|
67
|
+
dokument_url_text: str
|
|
68
|
+
dokumentformat: str | None
|
|
69
|
+
dokumentnamn: str
|
|
70
|
+
domain: str
|
|
71
|
+
filbilaga: FilBilaga | None
|
|
72
|
+
id: str
|
|
73
|
+
inlamnad: str | None
|
|
74
|
+
justeringsdag: str | None
|
|
75
|
+
kall_id: str | None
|
|
76
|
+
kalla: str | None
|
|
77
|
+
klockslag: str | None
|
|
78
|
+
lang: str | None
|
|
79
|
+
motionstid: str | None
|
|
80
|
+
notis: str | None
|
|
81
|
+
notisrubrik: str
|
|
82
|
+
nummer: str | None
|
|
83
|
+
organ: str | None
|
|
84
|
+
plats: str | None
|
|
85
|
+
# TODO this field can contain date (2018-03-07) and datetime (2016-02-11 15:28:15)
|
|
86
|
+
publicerad: str
|
|
87
|
+
rddata: str | None
|
|
88
|
+
rdrest: str | None
|
|
89
|
+
relaterat_id: str | None
|
|
90
|
+
relurl: str | None
|
|
91
|
+
reservationer: str | None
|
|
92
|
+
rm: str
|
|
93
|
+
score: str
|
|
94
|
+
slutdatum: str | None
|
|
95
|
+
sokdata: SokData
|
|
96
|
+
status: str | None
|
|
97
|
+
struktur: str | None
|
|
98
|
+
subtyp: str
|
|
99
|
+
summary: str | None
|
|
100
|
+
systemdatum: datetime.datetime
|
|
101
|
+
tempbeteckning: str | None
|
|
102
|
+
tilldelat: str | None
|
|
103
|
+
titel: str
|
|
104
|
+
traff: int
|
|
105
|
+
typ: DokumentTyp
|
|
106
|
+
undertitel: str | None
|
|
107
|
+
url: str | None
|
|
108
|
+
video: str | None
|
|
109
|
+
|
|
110
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
111
|
+
|
|
112
|
+
def get_rm(self) -> str:
|
|
113
|
+
return self.rm or "missing"
|
|
114
|
+
|
|
115
|
+
def get_typ_short_name(self) -> str:
|
|
116
|
+
return self.typ.short_name() if self.typ else "missing"
|
|
117
|
+
|
|
118
|
+
def get_typ_long_name(self) -> str:
|
|
119
|
+
return self.typ.long_name() if self.typ else "missing"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class DokumentLista(pydantic.BaseModel):
|
|
123
|
+
d_dt: str = pydantic.Field(alias="@dDt")
|
|
124
|
+
d_pre: str = pydantic.Field(alias="@dPre")
|
|
125
|
+
d_r: str = pydantic.Field(alias="@dR")
|
|
126
|
+
d_sol: str = pydantic.Field(alias="@dSol")
|
|
127
|
+
datum: datetime.datetime = pydantic.Field(alias="@datum")
|
|
128
|
+
ms: str = pydantic.Field(alias="@ms")
|
|
129
|
+
nasta_sida: str | None = pydantic.Field(alias="@nasta_sida")
|
|
130
|
+
q: str = pydantic.Field(alias="@q")
|
|
131
|
+
sida: int = pydantic.Field(alias="@sida")
|
|
132
|
+
sidor: int = pydantic.Field(alias="@sidor")
|
|
133
|
+
traff_fran: int = pydantic.Field(alias="@traff_fran")
|
|
134
|
+
traff_till: int = pydantic.Field(alias="@traff_till")
|
|
135
|
+
traffar: int = pydantic.Field(alias="@traffar")
|
|
136
|
+
varning: str | None = pydantic.Field(None, alias="@varning")
|
|
137
|
+
version: str = pydantic.Field(alias="@version")
|
|
138
|
+
facettlista: str | None
|
|
139
|
+
dokument: list[DokumentListaDokument]
|
|
140
|
+
|
|
141
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class DokumentListaPage(pydantic.BaseModel):
|
|
145
|
+
dokumentlista: DokumentLista
|
|
146
|
+
|
|
147
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
5
|
+
from swegov_opendata import shared
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Forslag(pydantic.BaseModel):
|
|
9
|
+
hangar_id: str | None = None
|
|
10
|
+
nummer: int
|
|
11
|
+
beteckning: str | None
|
|
12
|
+
lydelse: t.Annotated[str, pydantic.BeforeValidator(shared.ensure_str)]
|
|
13
|
+
lydelse2: str | None
|
|
14
|
+
utskottet: str | None
|
|
15
|
+
kammaren: str | None
|
|
16
|
+
behandlas_i: str | None
|
|
17
|
+
behandlas_i_punkt: str | None = None
|
|
18
|
+
kammarbeslutstyp: str | None
|
|
19
|
+
intressent: str | None = None
|
|
20
|
+
avsnitt: str | None = None
|
|
21
|
+
grundforfattning: str | None = None
|
|
22
|
+
andringsforfattning: str | None = None
|
|
23
|
+
|
|
24
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DokForslag(pydantic.BaseModel):
|
|
28
|
+
forslag: t.Annotated[list[Forslag], pydantic.BeforeValidator(shared.ensure_list)]
|
|
29
|
+
|
|
30
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MotForslag(pydantic.BaseModel):
|
|
34
|
+
nummer: int
|
|
35
|
+
rubrik: str | None = None
|
|
36
|
+
forslag: str | None = None
|
|
37
|
+
partier: str | None
|
|
38
|
+
typ: str
|
|
39
|
+
utskottsforslag_punkt: int
|
|
40
|
+
id: str | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DokMotForslag(pydantic.BaseModel):
|
|
44
|
+
motforslag: t.Annotated[list[MotForslag], pydantic.BeforeValidator(shared.ensure_list)]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UtskottsForslag(pydantic.BaseModel):
|
|
48
|
+
punkt: int
|
|
49
|
+
rubrik: str | None
|
|
50
|
+
forslag: str | None = None
|
|
51
|
+
forslag_del2: str | None = None
|
|
52
|
+
beteckning: str | None = None
|
|
53
|
+
beslut: str | None = None
|
|
54
|
+
beslutstyp: str | None = None
|
|
55
|
+
motforslag_nummer: int
|
|
56
|
+
motforslag_partier: str | None = None
|
|
57
|
+
votering_id: str | None = None
|
|
58
|
+
votering_sammanfattning_html: dict[str, t.Any] | None = None
|
|
59
|
+
votering_ledamot_url_xml: str | None = None
|
|
60
|
+
votering_url_xml: str | None = None
|
|
61
|
+
rm: str
|
|
62
|
+
bet: str
|
|
63
|
+
vinnare: str | None = None
|
|
64
|
+
voteringskrav: str | None = None
|
|
65
|
+
beslutsregelkvot: str | None = None
|
|
66
|
+
beslutsregelparagraf: str | None = None
|
|
67
|
+
punkttyp: str | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class DokUtskottsForslag(pydantic.BaseModel):
|
|
71
|
+
utskottsforslag: t.Annotated[
|
|
72
|
+
list[UtskottsForslag], pydantic.BeforeValidator(shared.ensure_list)
|
|
73
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
5
|
+
from swegov_opendata import shared
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Intressent(pydantic.BaseModel):
|
|
9
|
+
roll: str | None
|
|
10
|
+
namn: str | None
|
|
11
|
+
partibet: str | None
|
|
12
|
+
intressent_id: str | None
|
|
13
|
+
ordning: str
|
|
14
|
+
|
|
15
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DokIntressent(pydantic.BaseModel):
|
|
19
|
+
intressent: t.Annotated[list[Intressent], pydantic.BeforeValidator(shared.ensure_list)]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Media(pydantic.BaseModel):
|
|
10
|
+
dok_id: str
|
|
11
|
+
url: str
|
|
12
|
+
videofileurl: str
|
|
13
|
+
audiofileurl: str | None
|
|
14
|
+
downloadurl: str | None
|
|
15
|
+
thumbnailurl: str | None
|
|
16
|
+
debateurl: str
|
|
17
|
+
debattsekunder: int
|
|
18
|
+
debatt_rm: str | None
|
|
19
|
+
debatt_typ: str | None
|
|
20
|
+
dok_beteckning: str | None
|
|
21
|
+
datum: datetime.datetime | None
|
|
22
|
+
videostatus: int
|
|
23
|
+
source: str
|
|
24
|
+
inspelningstyp: str
|
|
25
|
+
systemdatum: datetime.datetime
|
|
26
|
+
|
|
27
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class WebbMedia(pydantic.BaseModel):
|
|
31
|
+
media: t.Annotated[list[Media], pydantic.BeforeValidator(shared.ensure_list)]
|
|
32
|
+
|
|
33
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
5
|
+
from swegov_opendata import shared
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Referens(pydantic.BaseModel):
|
|
9
|
+
hangar_id: str | None = None
|
|
10
|
+
referenstyp: str | None
|
|
11
|
+
uppgift: str | None
|
|
12
|
+
ref_dok_id: str
|
|
13
|
+
ref_dok_typ: str
|
|
14
|
+
ref_dok_rm: str | None
|
|
15
|
+
ref_dok_bet: str | None
|
|
16
|
+
ref_dok_titel: str | None = pydantic.Field(default="")
|
|
17
|
+
ref_dok_subtitel: str | None
|
|
18
|
+
ref_dok_subtyp: str | None = None
|
|
19
|
+
ref_dok_dokumentnamn: str | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DokReferens(pydantic.BaseModel):
|
|
23
|
+
referens: t.Annotated[list[Referens], pydantic.BeforeValidator(shared.ensure_list)]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
|
|
6
|
+
from swegov_opendata import shared
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Uppgift(pydantic.BaseModel):
|
|
10
|
+
systemdatum: datetime.datetime | None = None
|
|
11
|
+
dok_id: str | None = None
|
|
12
|
+
kod: str
|
|
13
|
+
namn: str
|
|
14
|
+
text: str | None
|
|
15
|
+
|
|
16
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DokUppgift(pydantic.BaseModel):
|
|
20
|
+
uppgift: t.Annotated[list[Uppgift], pydantic.BeforeValidator(shared.ensure_list)]
|
|
21
|
+
|
|
22
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import pydantic
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Votering(pydantic.BaseModel):
|
|
5
|
+
rm: str
|
|
6
|
+
beteckning: str
|
|
7
|
+
hangar_id: str
|
|
8
|
+
votering_id: str
|
|
9
|
+
punkt: str
|
|
10
|
+
namn: str
|
|
11
|
+
intressent_id: str
|
|
12
|
+
parti: str
|
|
13
|
+
valkrets: str | None
|
|
14
|
+
valkretsnummer: str
|
|
15
|
+
iort: str | None
|
|
16
|
+
rost: str
|
|
17
|
+
avser: str
|
|
18
|
+
votering: str
|
|
19
|
+
banknummer: str
|
|
20
|
+
fornamn: str
|
|
21
|
+
efternamn: str
|
|
22
|
+
kon: str
|
|
23
|
+
fodd: str
|
|
24
|
+
datum: str | None
|
|
25
|
+
|
|
26
|
+
model_config = pydantic.ConfigDict(extra="forbid")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DokVotering(pydantic.BaseModel):
|
|
30
|
+
votering: list[Votering]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DokVoteringPage(pydantic.BaseModel):
|
|
34
|
+
dokvotering: DokVotering | None
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ensure_int_opt(value: t.Any) -> int | None:
|
|
7
|
+
if value == "":
|
|
8
|
+
return None
|
|
9
|
+
return value
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def ensure_list(value: t.Any) -> list[t.Any]:
|
|
13
|
+
if not isinstance(value, list):
|
|
14
|
+
return [value]
|
|
15
|
+
return value
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ensure_list_opt(value: t.Any) -> list[t.Any] | None:
|
|
19
|
+
if value is None:
|
|
20
|
+
return None
|
|
21
|
+
return ensure_list(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def ensure_str(value: t.Any) -> str:
|
|
25
|
+
if value is None:
|
|
26
|
+
return ""
|
|
27
|
+
if isinstance(value, str):
|
|
28
|
+
return value
|
|
29
|
+
if isinstance(value, list):
|
|
30
|
+
if len(value) == 0:
|
|
31
|
+
return ""
|
|
32
|
+
if len(value) == 1:
|
|
33
|
+
return ensure_str(value[0])
|
|
34
|
+
nonempty_values: set[str] = {v for v in value if isinstance(v, str) and len(v) > 0}
|
|
35
|
+
if len(nonempty_values) == 0:
|
|
36
|
+
return ""
|
|
37
|
+
if len(nonempty_values) == 1:
|
|
38
|
+
return next(iter(nonempty_values))
|
|
39
|
+
raise ValueError(f"Too many unique values: {list(nonempty_values)}")
|
|
40
|
+
|
|
41
|
+
return str(value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def ensure_str_opt(value: t.Any) -> str | None:
|
|
45
|
+
if value is None:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
return ensure_str(value)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Avdelning(pydantic.BaseModel):
|
|
52
|
+
text: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Avdelningar(pydantic.BaseModel):
|
|
56
|
+
avdelning: list[str]
|