# CAP-Tools
[![pipeline status](https://github.com/bjoern-reetz/cap-tools/actions/workflows/publish.yml/badge.svg?main)](https://github.com/bjoern-reetz/cap-tools/actions/workflows/publish.yml)
[![latest package version](https://img.shields.io/pypi/v/cap-tools)](https://pypi.org/project/cap-tools/)
[![supported python versions](https://img.shields.io/pypi/pyversions/cap-tools)](https://www.python.org/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/cap-tools)](https://pypistats.org/packages/cap-tools)
[![license](./images/license.svg)](./LICENSE)
[![source files coverage](./images/coverage.svg)](https://coverage.readthedocs.io/)
[![pyright](./images/pyright.svg)](https://microsoft.github.io/pyright)
[![ruff](./images/ruff.svg)](https://docs.astral.sh/ruff)
[![pre-commit](./images/pre-commit.svg)](https://pre-commit.com/)
Python data bindings for the [Common Alerting Protocol Version 1.2](https://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2.html).
## Getting started
This package contains a Python model for CAP XML documents that was generated using using [xsData](https://xsdata.readthedocs.io/) along with some convenience utilities.
To parse a CAP XML from a file into an instance of `cap_tools.models.Alert`, do as follows:
```python
from cap_tools.models import Alert
from xsdata.formats.dataclass.parsers import XmlParser
parser = XmlParser()
alert = parser.parse("path/to/my/cap.xml", Alert)
```
For advanced usage, just take a look at the [xsData](https://xsdata.readthedocs.io/en/latest/data_binding/basics/) docs.
### Convenience utilities
In addition to the code that was generated using xsData, this library adds some convenience utilities on top.
#### Transforming the system-specific key-value-pairs to mappings
The `Info.parameters`, `Info.event_codes` and `Area.geocode` fields generated by xsData are implemented as a list of (containers of) key-value-pairs.
```python
>>> import cap_tools
>>> from xsdata.formats.dataclass.parsers import XmlParser
>>> parser = XmlParser()
>>> alert = parser.parse("data/oasis/example2.xml")
>>> area = alert.infos[0].areas[0]
>>> area.geocodes
[Geocode(value_name=ValueName(value='SAME'), value=Value(value='006109')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006009')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006003'))]
```
This can more ergonomically be represented using a mapping. Use the `Info.parameters_to_dict`, `Info.event_codes_to_dict` and `Area.geocode_to_dict` methods to create instances of [MultiDict](https://multidict.aio-libs.org/en/stable/) from the data.
```python
>>> geocodes_multidict = area.geocodes_to_dict()
>>> geocodes_multidict
<MultiDict('SAME': '006109', 'SAME': '006009', 'SAME': '006003')>
>>> geocodes_multidict["SAME"]
'006109'
>>> geocodes_multidict.getall("SAME")
['006109', '006009', '006003']
```
Remember that `MultiDict.__getitem__()` uses the *first* occurence of the key while a `dict` would have used the *last* because of its overwrite rules.
You can also write any mapping to the instance fields using the `Info.parameters_from_dict`, `Info.event_codes_from_dict` and `Area.geocode_from_dict` methods.
Use the `MultiDict`:
```python
>>> geocodes_multidict.add("SAME", "123456")
>>> area.geocodes_from_dict(geocodes_multidict)
>>> area.geocodes
[Geocode(value_name=ValueName(value='SAME'), value=Value(value='006109')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006009')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006003')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='123456'))]
```
Or use a `dict`:
```python
>>> area.geocodes_from_dict({"foo": "bar", "lorem": "ipsum"})
>>> area.geocodes
[Geocode(value_name=ValueName(value='foo'), value=Value(value='bar')), Geocode(value_name=ValueName(value='lorem'), value=Value(value='ipsum'))]
```
The MultiDicts do not retain any connection to the model. In all cases, the internal state is always represented by the list of containers of key-value-pairs.
#### Splitting group listings
The attributes `addresses`, `references` and `incidents` of `Alert` store multiple values as "group listings", i.e. multiple values are space-delimited. You can split these attributes by using `Alert.addresses_to_list()`, `Alert.references_to_list()` and `Alert.incidents_to_list()` and write back to them with corresponding `Alert.*_from_list()` methods respectively.
#### Awareness of "en-US" as the default language
When no language is explicitly defined on an Info element, "en-US" is assumed per CAP spec. This library implements this behavior neither by using default values nor by using descriptor fields.
```python
>>> alert.infos[0].language = "en-US"
>>> alert.infos[0].language
'en-US'
>>> alert.infos[0].language = None
>>> alert.infos[0].language is None
True
>>> alert.infos[0].language == "en-US"
False
```
To still have this "absence means en-US" logic implemented, two convenience methods are added to Info.
```python
>>> alert.infos[0].set_language("de-DE")
>>> alert.infos[0].language
'de-DE'
>>> alert.infos[0].set_language("en-US")
>>> alert.infos[0].language is None
True
>>> alert.infos[0].get_language()
'en-US'
```
Using `Info.get_language()` returns `"en-US"` when `Info.language` is `None` and `Info.set_language("en-US")` sets `Info.language` to `None` (implicitly "en-US") instead of `"en-US"` (explicitly).
## Limitations
While this library is fully typed to enable Python type safety, it currently does neither implement the pattern restrictions from the [CAP v1.2 XSD specification](./CAP-v1.2.xsd) (i.e. the pattern restriction for the XmlDateTime fields) nor the additional restrictions imposed by the [normative alert message structure](https://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2.html#_Toc454352650) (e.g. Alert.identifier must not include spaces, commas or the characters "<" and "&").
This does not matter much when using this library for reading CAP messages - but when you are using this library to create CAP messages, **you are responsible** for respecting those additional restrictions yourself!
Raw data
{
"_id": null,
"home_page": "https://github.com/bjoern-reetz/cap-tools",
"name": "cap-tools",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.10",
"maintainer_email": null,
"keywords": "cap, common alerting protocol, read, write, tool, util",
"author": "Bj\u00f6rn Reetz",
"author_email": "git@bjoern-reetz.de",
"download_url": "https://files.pythonhosted.org/packages/a5/36/5bc4752ff211b9ff5cc3af9434017fc729fac295c8fb98e1a0b851b82b66/cap_tools-3.1.1.tar.gz",
"platform": null,
"description": "# CAP-Tools\n\n[![pipeline status](https://github.com/bjoern-reetz/cap-tools/actions/workflows/publish.yml/badge.svg?main)](https://github.com/bjoern-reetz/cap-tools/actions/workflows/publish.yml)\n[![latest package version](https://img.shields.io/pypi/v/cap-tools)](https://pypi.org/project/cap-tools/)\n[![supported python versions](https://img.shields.io/pypi/pyversions/cap-tools)](https://www.python.org/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/cap-tools)](https://pypistats.org/packages/cap-tools)\n[![license](./images/license.svg)](./LICENSE)\n[![source files coverage](./images/coverage.svg)](https://coverage.readthedocs.io/)\n[![pyright](./images/pyright.svg)](https://microsoft.github.io/pyright)\n[![ruff](./images/ruff.svg)](https://docs.astral.sh/ruff)\n[![pre-commit](./images/pre-commit.svg)](https://pre-commit.com/)\n\nPython data bindings for the [Common Alerting Protocol Version 1.2](https://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2.html).\n\n## Getting started\n\nThis package contains a Python model for CAP XML documents that was generated using using [xsData](https://xsdata.readthedocs.io/) along with some convenience utilities.\n\nTo parse a CAP XML from a file into an instance of `cap_tools.models.Alert`, do as follows:\n\n```python\nfrom cap_tools.models import Alert\nfrom xsdata.formats.dataclass.parsers import XmlParser\n\nparser = XmlParser()\nalert = parser.parse(\"path/to/my/cap.xml\", Alert)\n```\n\nFor advanced usage, just take a look at the [xsData](https://xsdata.readthedocs.io/en/latest/data_binding/basics/) docs.\n\n### Convenience utilities\n\nIn addition to the code that was generated using xsData, this library adds some convenience utilities on top.\n\n#### Transforming the system-specific key-value-pairs to mappings\n\nThe `Info.parameters`, `Info.event_codes` and `Area.geocode` fields generated by xsData are implemented as a list of (containers of) key-value-pairs.\n\n```python\n>>> import cap_tools\n>>> from xsdata.formats.dataclass.parsers import XmlParser\n>>> parser = XmlParser()\n>>> alert = parser.parse(\"data/oasis/example2.xml\")\n>>> area = alert.infos[0].areas[0]\n>>> area.geocodes\n[Geocode(value_name=ValueName(value='SAME'), value=Value(value='006109')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006009')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006003'))]\n```\n\nThis can more ergonomically be represented using a mapping. Use the `Info.parameters_to_dict`, `Info.event_codes_to_dict` and `Area.geocode_to_dict` methods to create instances of [MultiDict](https://multidict.aio-libs.org/en/stable/) from the data.\n\n```python\n>>> geocodes_multidict = area.geocodes_to_dict()\n>>> geocodes_multidict\n<MultiDict('SAME': '006109', 'SAME': '006009', 'SAME': '006003')>\n>>> geocodes_multidict[\"SAME\"]\n'006109'\n>>> geocodes_multidict.getall(\"SAME\")\n['006109', '006009', '006003']\n```\n\nRemember that `MultiDict.__getitem__()` uses the *first* occurence of the key while a `dict` would have used the *last* because of its overwrite rules.\n\nYou can also write any mapping to the instance fields using the `Info.parameters_from_dict`, `Info.event_codes_from_dict` and `Area.geocode_from_dict` methods.\n\nUse the `MultiDict`:\n\n```python\n>>> geocodes_multidict.add(\"SAME\", \"123456\")\n>>> area.geocodes_from_dict(geocodes_multidict)\n>>> area.geocodes\n[Geocode(value_name=ValueName(value='SAME'), value=Value(value='006109')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006009')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='006003')), Geocode(value_name=ValueName(value='SAME'), value=Value(value='123456'))]\n```\n\nOr use a `dict`:\n\n```python\n>>> area.geocodes_from_dict({\"foo\": \"bar\", \"lorem\": \"ipsum\"})\n>>> area.geocodes\n[Geocode(value_name=ValueName(value='foo'), value=Value(value='bar')), Geocode(value_name=ValueName(value='lorem'), value=Value(value='ipsum'))]\n```\n\nThe MultiDicts do not retain any connection to the model. In all cases, the internal state is always represented by the list of containers of key-value-pairs.\n\n#### Splitting group listings\n\nThe attributes `addresses`, `references` and `incidents` of `Alert` store multiple values as \"group listings\", i.e. multiple values are space-delimited. You can split these attributes by using `Alert.addresses_to_list()`, `Alert.references_to_list()` and `Alert.incidents_to_list()` and write back to them with corresponding `Alert.*_from_list()` methods respectively.\n\n#### Awareness of \"en-US\" as the default language\n\nWhen no language is explicitly defined on an Info element, \"en-US\" is assumed per CAP spec. This library implements this behavior neither by using default values nor by using descriptor fields.\n\n```python\n>>> alert.infos[0].language = \"en-US\"\n>>> alert.infos[0].language\n'en-US'\n>>> alert.infos[0].language = None\n>>> alert.infos[0].language is None\nTrue\n>>> alert.infos[0].language == \"en-US\"\nFalse\n```\n\nTo still have this \"absence means en-US\" logic implemented, two convenience methods are added to Info.\n\n```python\n>>> alert.infos[0].set_language(\"de-DE\")\n>>> alert.infos[0].language\n'de-DE'\n>>> alert.infos[0].set_language(\"en-US\")\n>>> alert.infos[0].language is None\nTrue\n>>> alert.infos[0].get_language()\n'en-US'\n```\n\nUsing `Info.get_language()` returns `\"en-US\"` when `Info.language` is `None` and `Info.set_language(\"en-US\")` sets `Info.language` to `None` (implicitly \"en-US\") instead of `\"en-US\"` (explicitly).\n\n## Limitations\n\nWhile this library is fully typed to enable Python type safety, it currently does neither implement the pattern restrictions from the [CAP v1.2 XSD specification](./CAP-v1.2.xsd) (i.e. the pattern restriction for the XmlDateTime fields) nor the additional restrictions imposed by the [normative alert message structure](https://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2.html#_Toc454352650) (e.g. Alert.identifier must not include spaces, commas or the characters \"<\" and \"&\").\n\nThis does not matter much when using this library for reading CAP messages - but when you are using this library to create CAP messages, **you are responsible** for respecting those additional restrictions yourself!\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Python data bindings for the Common Alerting Protocol Version.",
"version": "3.1.1",
"project_urls": {
"Documentation": "https://github.com/bjoern-reetz/cap-tools",
"Homepage": "https://github.com/bjoern-reetz/cap-tools",
"Repository": "https://github.com/bjoern-reetz/cap-tools"
},
"split_keywords": [
"cap",
" common alerting protocol",
" read",
" write",
" tool",
" util"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1c8284f7d0fdec52ce7f0206bae76b3b05d5910629a73a17ea630b335dd6c1d8",
"md5": "fa7235b83fe08c64355439d852b60003",
"sha256": "6dc93d6b38408862ab901a7b67d038e1688cfef603ba9cc0d78db26c3b590dae"
},
"downloads": -1,
"filename": "cap_tools-3.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fa7235b83fe08c64355439d852b60003",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.10",
"size": 8149,
"upload_time": "2024-08-04T11:26:27",
"upload_time_iso_8601": "2024-08-04T11:26:27.966109Z",
"url": "https://files.pythonhosted.org/packages/1c/82/84f7d0fdec52ce7f0206bae76b3b05d5910629a73a17ea630b335dd6c1d8/cap_tools-3.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a5365bc4752ff211b9ff5cc3af9434017fc729fac295c8fb98e1a0b851b82b66",
"md5": "ec4f55e3cd8ca232f1ddb2a402cb07c2",
"sha256": "315e6edd80637f4e817783c900def7a44fd2aa4bda23e438742a64d18016d1f5"
},
"downloads": -1,
"filename": "cap_tools-3.1.1.tar.gz",
"has_sig": false,
"md5_digest": "ec4f55e3cd8ca232f1ddb2a402cb07c2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.10",
"size": 9965,
"upload_time": "2024-08-04T11:26:29",
"upload_time_iso_8601": "2024-08-04T11:26:29.456535Z",
"url": "https://files.pythonhosted.org/packages/a5/36/5bc4752ff211b9ff5cc3af9434017fc729fac295c8fb98e1a0b851b82b66/cap_tools-3.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-04 11:26:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "bjoern-reetz",
"github_project": "cap-tools",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "cap-tools"
}