From e8a4d2363feeabf676534d74c0b8d6953931dc61 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 16:19:08 +1100 Subject: [PATCH 001/118] docs: adds intro paragraph --- docs/docs/design.md | 9 +++++++++ docs/docs/index.md | 39 +++++++++++++++++++++++++++++---------- docs/mkdocs.yml | 2 +- 3 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 docs/docs/design.md diff --git a/docs/docs/design.md b/docs/docs/design.md new file mode 100644 index 00000000..8659fcfa --- /dev/null +++ b/docs/docs/design.md @@ -0,0 +1,9 @@ +# Design + +This follows how we have designed + +## Layout + +Layout of our files + +## Data Transfer Objects diff --git a/docs/docs/index.md b/docs/docs/index.md index 2556b46c..83986a68 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,17 +1,36 @@ # Welcome to Gallagher Python -This project maintains an idiomatic client for Python +This project maintains an idiomatic client for Python. The project began as a requirement for us to reliably work with the Gallagher API. While Gallagher publish their [API reference](https://gallaghersecurity.github.io/cc-rest-docs/ref/index.html) which is built from an OpenAPI spec with [Spectacle](https://github.com/sourcey/spectacle) documentation generator flavours. The OpenAPI spec is maintained by hand and [can be found on Github](https://github.com/GallagherSecurity/cc-rest-docs/tree/master/swagger). + +Our assumption is that this is for security and technical reasons. In the spirit of building good quality software we embarked on building a Python idiomatic client with full test coverage. + +The API client draws inspiration from companies like [Stripe](https://stripe.com) or projects like [pyndatic](https://pydantic.dev) who are known for stellar developer experience. Our aim is to provide a similar quality of developer experience for Gallagher projects. + +The design pattern of the API client is opinionated from our experience as software engineers. We goto great lengths to document and justify our thought process so others can see where we are coming from. + +> **Note:** This project is not affiliated with Gallagher in any way. + +## Features + +To be completed + +## Developing the client + +This library uses [httpx](https://www.python-httpx.org) as the HTTP transport and [pydantic](https://pydantic.dev) to construct and ingest payloads. We use [taskfile](https://taskfile.dev) to run tasks. Our test suite is setup using `pytest`. + +Anomaly has a demo Command Centre set up in the cloud that we run tests against. This is populate using a sample site configuration. There are no real security controllers connected to this instance. Upon a PR being lodged, Github actions is configured to run the entire test suite against our demo instance. + +To contribute to the library, please fork this repository and lodge a pull request for us to accept your changes. ## Contributing to the documentation -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs -h` - Print help message and exit. +The documentation is build using [mkdocs](https://www.mkdocs.org) and hosted on [Github pages](https://anomaly.github.io/gallagher/). The project repository is configured to build and publish the documentation on every commit to the `master` branch. + +Some handy commands to get you started: -## Project layout +- `mkdocs new [dir-name]` - Create a new project. +- `mkdocs serve -a localhost:8003` - Start the live-reloading docs server, `-a` allows you to provide a custom address. +- `mkdocs build` - Build the documentation site. +- `mkdocs -h` - Print help message and exit. - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. +To start contributing please fork this repository, make the changes you desire and submit a pull request for us to merge your changes in. Alternatively consider [starting a discussion](https://github.com/anomaly/gallagher/discussions) or [raising an issue](https://github.com/anomaly/gallagher/issues). Be kind to our maintainers and check to see if a similar discussion is already in place and join the thread. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3f03ce07..540da967 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -3,4 +3,4 @@ site_url: https://anomaly.github.io/gallagher/ repo_url: https://github.com/anomaly/gallagher/ site_author: Dev Mukherjee theme: - name: terminal + name: readthedocs From 8e3f9da630f256adecfdc2a8593483dbcf37f65a Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 16:19:24 +1100 Subject: [PATCH 002/118] feat: adds division endpoints --- TODO.md | 22 ++++++++++----------- gallagher/cc/alarms/divisions.py | 7 +++++++ gallagher/dto/division.py | 25 +++++++++++++++++------ tests/test_divisions.py | 34 ++++++++++++++++++++++++-------- tests/test_items.py | 5 ++++- 5 files changed, 66 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index 965b368a..57ad70fb 100644 --- a/TODO.md +++ b/TODO.md @@ -2,8 +2,8 @@ The following is tracking which endpoints are available along with their test cases -- [ ] Authentication -- [ ] Base API route design +- [x] Authentication +- [x] Base API route design ## Alarms, events, and non-cardholder items @@ -18,17 +18,16 @@ The following is tracking which endpoints are available along with their test ca - [ ] Event detail - [ ] Event POST body - [ ] Event groups -- [ ] Divisions -- [ ] Division +- [x] Divisions +- [x] Division - [ ] Division PATCH and POST example - [ ] Item search -- [X] Item summary -- [X] Item detail -- [X] Item types +- [x] Item summary +- [x] Item detail +- [x] Item types - [ ] Item update - [ ] Item update subscription - ## Cardholders and related items - [ ] Cardholder search @@ -94,7 +93,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Alarm Zone summary - [ ] Alarm Zone detail - [ ] Day category search -- [X] Day category +- [x] Day category - [ ] Door search - [ ] Door summary - [ ] Door detail @@ -117,12 +116,11 @@ The following is tracking which endpoints are available along with their test ca - [ ] Output summary - [ ] Output detail - [ ] Schedule search -- [X] Schedule summary +- [x] Schedule summary - [ ] Schedule detail - [ ] Schedule POST and PATCH - [ ] Override end time - ## PIV cards - [ ] PIV card GET @@ -130,4 +128,4 @@ The following is tracking which endpoints are available along with their test ca - [ ] PIV card create example - [ ] PIV card update example - [ ] PIV card data -- [ ] CHUID \ No newline at end of file +- [ ] CHUID diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 7d00b0e5..2bb635ac 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -7,6 +7,11 @@ EndpointConfig ) +from ...dto.division import ( + DivisionDetailResponse, + DivisionDetail, +) + class Division(APIBase): """ @@ -18,4 +23,6 @@ class Division(APIBase): __config__ = EndpointConfig( endpoint="divisions", + dto_list=DivisionDetailResponse, + dto_retrieve=DivisionDetail, ) diff --git a/gallagher/dto/division.py b/gallagher/dto/division.py index 7ad5f91b..ce2aab07 100644 --- a/gallagher/dto/division.py +++ b/gallagher/dto/division.py @@ -12,6 +12,7 @@ # import visitor.VisitorManagementSummary + class DivisionRef( AppBaseModel, IdentityMixin, @@ -24,17 +25,29 @@ class DivisionRef( """ pass - + class DivisionDetail( - AppBaseModel, - IdentityMixin, + DivisionRef, ): """ """ name: str - description: Optional[str] - server_display_name: Optional[str] - parent: Optional[HrefMixin] + description: Optional[str] = None + server_display_name: Optional[str] = None + parent: Optional[HrefMixin] = None + # TODO: Looks like we don't have access to visitor management + # on our test instance at the moment # visitor_management: visitor.VisitorManagementSummary + + +class DivisionDetailResponse( + AppBaseModel +): + """ + + """ + + results: list[DivisionDetail] + next: Optional[HrefMixin] = None diff --git a/tests/test_divisions.py b/tests/test_divisions.py index b01e8280..f2d38100 100644 --- a/tests/test_divisions.py +++ b/tests/test_divisions.py @@ -1,8 +1,26 @@ -# def test_division_list(): -# from gallagher.cc import Division -# from gallagher.schema import Response - -# response = Division.list() -# assert type(response) is Response -# assert type(response.results) is list -# assert len(response.results) > 0 \ No newline at end of file +def test_division_list(): + from gallagher.cc.alarms.divisions import Division + from gallagher.dto.division import DivisionDetailResponse + + response = Division.list() + assert type(response) is DivisionDetailResponse + assert type(response.results) is list + assert len(response.results) > 0 + + +def test_division_detail(): + from gallagher.cc.alarms.divisions import Division + from gallagher.dto.division import ( + DivisionDetailResponse, + DivisionDetail, + ) + + response = Division.list() + assert type(response) is DivisionDetailResponse + assert type(response.results) is list + + for division_summary in response.results: + # Get the detail of the division + division_detail_response = Division.retrieve(division_summary.id) + assert type(division_detail_response) is DivisionDetail + assert division_detail_response.id == division_summary.id diff --git a/tests/test_items.py b/tests/test_items.py index dd39f7e6..2e83b20f 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -37,8 +37,10 @@ def test_item_detail(): """ from gallagher.cc.alarms.items import Item - from gallagher.dto.items import ItemsSummaryResponse, \ + from gallagher.dto.items import ( + ItemsSummaryResponse, ItemDetail + ) response: ItemsSummaryResponse = Item.list() assert type(response) is ItemsSummaryResponse @@ -47,3 +49,4 @@ def test_item_detail(): # Get the detail of the item item_detail_response = Item.retrieve(item_summary.id) assert type(item_detail_response) is ItemDetail + assert (item_detail_response.id == item_summary.id) From ce19045bda540c12d7b1e67e9af370b43ed4e718 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 16:20:05 +1100 Subject: [PATCH 003/118] feat: adds optional cli parameter to run a single test modifies the test endpoint so if you pass a file name it just runs that test suite, if you don't then it will run all tests in the tests/ folder --- Taskfile.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index a9d3ed20..bffcaff6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -9,9 +9,12 @@ tasks: cmds: - poetry build test: - desc: runs tests inside the server container + desc: runs tests inside the virtualenv + summary: + runs all the tests inside the virtualenv, optionally + provide the name of the test as an argument to run a single test cmds: - - poetry run coverage run -m pytest -s + - poetry run coverage run -m pytest tests/{{.CLI_ARGS}} test:coverreport: desc: runs coverage inside the server container cmds: From 0c31386f45243ca638f6cd9b3292ee2ce824d02a Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 16:56:58 +1100 Subject: [PATCH 004/118] feat: adds alarm list endpoints --- TODO.md | 2 +- gallagher/cc/alarms/__init__.py | 25 ++++++++++++++++- gallagher/cc/utils.py | 4 +++ gallagher/dto/alarm.py | 49 +++++++++++++++++++++++++++++++-- gallagher/dto/event.py | 16 +++++++++++ tests/test_alarm.py | 17 ++++++++++++ 6 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 gallagher/dto/event.py create mode 100644 tests/test_alarm.py diff --git a/TODO.md b/TODO.md index 57ad70fb..17649f92 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Alarm search - [ ] Alarm updates -- [ ] Alarm summary +- [x] Alarm summary - [ ] Alarm detail - [ ] Alarm history entry - [ ] Alarm update request diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 1f4c63ae..87416197 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -1,4 +1,27 @@ """ -""" \ No newline at end of file +""" + +from ..utils import ( + APIBase, + EndpointConfig +) + +from ...dto.alarm import ( + AlarmResponse, + AlarmZoneSummary +) + + +class Alarms( + APIBase +): + """ Alarms + """ + + __config__ = EndpointConfig( + endpoint="alarms", + dto_list=AlarmResponse, + dto_retrieve=AlarmZoneSummary, + ) diff --git a/gallagher/cc/utils.py b/gallagher/cc/utils.py index 86936cbc..f7d7bc18 100644 --- a/gallagher/cc/utils.py +++ b/gallagher/cc/utils.py @@ -153,3 +153,7 @@ def search(cls): """ pass + + @classmethod + def updates(cls): + pass diff --git a/gallagher/dto/alarm.py b/gallagher/dto/alarm.py index 1ea73cb4..87ed43f6 100644 --- a/gallagher/dto/alarm.py +++ b/gallagher/dto/alarm.py @@ -2,11 +2,27 @@ """ +from typing import Optional from .utils import ( AppBaseModel, - HrefMixin + HrefMixin, + IdentityMixin, ) + +from .event import EventType + + +class AlarmSource( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ AlarmSource represents a device that has triggered an alarm + """ + name: str + + class AlarmZoneRef( AppBaseModel, HrefMixin @@ -15,8 +31,11 @@ class AlarmZoneRef( """ name: str + class AlarmZoneSummary( AppBaseModel, + HrefMixin, + IdentityMixin, ): """ #TODO: Revise this if it shows up in other places @@ -24,4 +43,30 @@ class AlarmZoneSummary( property in the access_group schema. I don't know if this is appropriate """ - alarm_zone: AlarmZoneRef \ No newline at end of file + time: str + message: str + source: AlarmSource + type: str + event_type: Optional[EventType] = None + priority: int + state: str + active: bool + division: HrefMixin + event: Optional[HrefMixin] = None + note_presets: list[str] = [] + view: HrefMixin + comment: HrefMixin + acknowledge: Optional[HrefMixin] = None + acknowledge_with_comment: Optional[HrefMixin] = None + process: Optional[HrefMixin] = None + process_with_comment: Optional[HrefMixin] = None + force_process: Optional[HrefMixin] = None + + +class AlarmResponse( + AppBaseModel, +): + """ AlarmResponse represents a single alarm + """ + alarms: list[AlarmZoneSummary] + updates: HrefMixin diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py new file mode 100644 index 00000000..d6f228ab --- /dev/null +++ b/gallagher/dto/event.py @@ -0,0 +1,16 @@ +""" + +""" + +from .utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + + +class EventType( + AppBaseModel, + IdentityMixin, +): + name: str diff --git a/tests/test_alarm.py b/tests/test_alarm.py new file mode 100644 index 00000000..628d9dd3 --- /dev/null +++ b/tests/test_alarm.py @@ -0,0 +1,17 @@ +""" + +""" + + +def test_alarms_list(): + """ Get a list of item types and iterates through it + these are a summary response + + """ + from gallagher.cc.alarms import Alarms + from gallagher.dto.alarm import AlarmResponse + + response = Alarms.list() + assert type(response) is AlarmResponse + assert type(response.alarms) is list + assert len(response.alarms) > 0 From 67934a0b99a29dc7c9a7e9db9bc2e710b77f43a6 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 17:09:25 +1100 Subject: [PATCH 005/118] refactor: package names to reflect docs --- gallagher/cc/card/__init__.py | 4 --- .../{cardholder => cardholders}/__init__.py | 0 .../cc/{card => cardholders}/card_type.py | 0 gallagher/dto/door.py | 30 +++++++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) delete mode 100644 gallagher/cc/card/__init__.py rename gallagher/cc/{cardholder => cardholders}/__init__.py (100%) rename gallagher/cc/{card => cardholders}/card_type.py (100%) create mode 100644 gallagher/dto/door.py diff --git a/gallagher/cc/card/__init__.py b/gallagher/cc/card/__init__.py deleted file mode 100644 index 1f4c63ae..00000000 --- a/gallagher/cc/card/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" - - -""" \ No newline at end of file diff --git a/gallagher/cc/cardholder/__init__.py b/gallagher/cc/cardholders/__init__.py similarity index 100% rename from gallagher/cc/cardholder/__init__.py rename to gallagher/cc/cardholders/__init__.py diff --git a/gallagher/cc/card/card_type.py b/gallagher/cc/cardholders/card_type.py similarity index 100% rename from gallagher/cc/card/card_type.py rename to gallagher/cc/cardholders/card_type.py diff --git a/gallagher/dto/door.py b/gallagher/dto/door.py new file mode 100644 index 00000000..8819b224 --- /dev/null +++ b/gallagher/dto/door.py @@ -0,0 +1,30 @@ +""" + + +""" + +from .utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class DoorSummary( + AppBaseModel, + IdentityMixin, + HrefMixin, +): + """ + + """ + name: str + + +class DoorSummaryResponse( + AppBaseModel +): + """ + + """ + results: list[DoorSummary] From 2b532d7f75ca5ee7e174fa8bcc19f2fab57cceb0 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 17:09:50 +1100 Subject: [PATCH 006/118] fix: import post package refactor --- tests/test_card_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_card_types.py b/tests/test_card_types.py index 08f6c8ee..1e459c3e 100644 --- a/tests/test_card_types.py +++ b/tests/test_card_types.py @@ -4,7 +4,7 @@ def test_get_card_types(): - from gallagher.cc.card.card_type import CardType + from gallagher.cc.cardholders.card_type import CardType from gallagher.dto.card_type import CardTypeResponse response = CardType.list() From 7dbc643272047e3d74505375b34e17135648bfa8 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 12 Nov 2023 17:51:22 +1100 Subject: [PATCH 007/118] docs: adds usage stub --- docs/docs/index.md | 5 ++++- docs/docs/usage.md | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/docs/usage.md diff --git a/docs/docs/index.md b/docs/docs/index.md index 83986a68..79a9a107 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -12,7 +12,10 @@ The design pattern of the API client is opinionated from our experience as softw ## Features -To be completed +- pydantic models for all API payloads +- HTTP transport using httpx +- Full test coverage +- A completely python based interface to interacting with Gallagher Command Centre ## Developing the client diff --git a/docs/docs/usage.md b/docs/docs/usage.md new file mode 100644 index 00000000..e2fab4ec --- /dev/null +++ b/docs/docs/usage.md @@ -0,0 +1,11 @@ +# Usage + +## Installation + +We recommend installation via `PyPI`: + +```bash +poetry add gallagher +``` + +## Understanding the interface From 0949d35f9d890260d138df0030a9172e94dcaea9 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 14 Nov 2023 15:56:20 +1100 Subject: [PATCH 008/118] feat: adds api discovery endpoint --- gallagher/cc/__init__.py | 17 ++++ gallagher/dto/discover.py | 197 ++++++++++++++++++++++++++++++++++++-- tests/test_discover.py | 16 ++++ 3 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 tests/test_discover.py diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index 47f62125..46853380 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -23,6 +23,12 @@ from typing import Optional from ..const import URL +from ..dto.discover import DiscoveryResponse + +from .utils import ( + APIBase, + EndpointConfig +) # Follow the instructions in the Gallagher documentation # to obtain an API key @@ -39,3 +45,14 @@ # should you wish to use a proxy, set this to the proxy URL proxy: Optional[str] = None + +class APIDiscovery( + APIBase +): + """ + + """ + __config__ = EndpointConfig( + endpoint="", + dto_list=DiscoveryResponse, + ) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 9f3457f2..61b55eb8 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -1,4 +1,6 @@ -""" +""" Command Centre API discovery + +The Command Centre API has a discovery endpoint that allows """ @@ -7,25 +9,208 @@ HrefMixin ) + class FeatureAccessGroups( AppBaseModel, ): access_groups: HrefMixin -class APIFeatures( + +class FeatureAccessZones( AppBaseModel, ): - access_groups: FeatureAccessGroups + access_zones: HrefMixin + + +class FeatureAlarmZones( + AppBaseModel, +): + alarm_zones: HrefMixin + + +class FeatureAlarms( + AppBaseModel, +): + alarms: HrefMixin + divisions: HrefMixin + updates: HrefMixin + + +class FeatureCardTypes( + AppBaseModel, +): + assign: HrefMixin + card_types: HrefMixin + + +class FeatureCardholders( + AppBaseModel, +): + cardholders: HrefMixin + changes: HrefMixin + update_location_access_zones: HrefMixin + + +class FeatureCompetencies( + AppBaseModel, +): + competencies: HrefMixin + + +class FeatureDayCategories( + AppBaseModel, +): + day_categories: HrefMixin + + +class FeatureDivisions( + AppBaseModel, +): + divisions: HrefMixin + + +class FeatureDoors( + AppBaseModel, +): + doors: HrefMixin + + +class FeatureElevators( + AppBaseModel, +): + elevator_groups: HrefMixin + + +class FeatureEvents( + AppBaseModel, +): + divisions: HrefMixin + event_groups: HrefMixin + events: HrefMixin + updates: HrefMixin + + +class FeatureFenceZones( + AppBaseModel, +): + fence_zones: HrefMixin + + +class FeatureInputs( + AppBaseModel, +): + inputs: HrefMixin + + +class FeatureInterlockGroups( + AppBaseModel, +): + interlock_groups: HrefMixin + + +class FeatureItems( + AppBaseModel, +): + item_types: HrefMixin + items: HrefMixin + updates: HrefMixin + + +class FeatureLockerBanks( + AppBaseModel, +): + locker_banks: HrefMixin + + +class FeatureMacros( + AppBaseModel, +): + macros: HrefMixin + + +class FeatureOperatorGroups( + AppBaseModel, +): + operator_groups: HrefMixin + + +class FeatureOutputs( + AppBaseModel, +): + outputs: HrefMixin + -class API( +class FeaturePersonalDataFields( AppBaseModel, ): + personal_data_fields: HrefMixin + + +class FeatureReceptions( + AppBaseModel, +): + receptions: HrefMixin + + +class FeatureRoles( + AppBaseModel, +): + roles: HrefMixin + + +class FeatureSchedules( + AppBaseModel, +): + schedules: HrefMixin + + +class FeatureVisits( + AppBaseModel, +): + visits: HrefMixin + + +class FeaturesDetail( + AppBaseModel, +): + """ + """ - The API obje + access_groups: FeatureAccessGroups + access_zones: FeatureAccessZones + alarm_zones: FeatureAlarmZones + alarms: FeatureAlarms + card_types: FeatureCardTypes + cardholders: FeatureCardholders + competencies: FeatureCompetencies + day_categories: FeatureDayCategories + divisions: FeatureDivisions + doors: FeatureDoors + elevators: FeatureElevators + events: FeatureEvents + fence_zones: FeatureFenceZones + inputs: FeatureInputs + interlock_groups: FeatureInterlockGroups + items: FeatureItems + locker_banks: FeatureLockerBanks + macros: FeatureMacros + operator_groups: FeatureOperatorGroups + outputs: FeatureOutputs + personal_data_fields: FeaturePersonalDataFields + receptions: FeatureReceptions + roles: FeatureRoles + schedules: FeatureSchedules + visits: FeatureVisits + + +class DiscoveryResponse( + AppBaseModel, +): + """ """ version: str - features: APIFeatures + features: FeaturesDetail @property def get_sem_ver(self): diff --git a/tests/test_discover.py b/tests/test_discover.py new file mode 100644 index 00000000..22a7e251 --- /dev/null +++ b/tests/test_discover.py @@ -0,0 +1,16 @@ +""" Discover the API. + +""" + + +def test_discover(): + + from gallagher.cc import APIDiscovery + from gallagher.dto.discover import ( + DiscoveryResponse, + FeaturesDetail + ) + + response = APIDiscovery.list() + assert type(response) is DiscoveryResponse + assert type(response.features) is FeaturesDetail From 5ebe920e38ff0318aa6373cdee9311f0448ad5eb Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 14 Nov 2023 15:56:32 +1100 Subject: [PATCH 009/118] feat: adds event endpoints --- gallagher/cc/alarms/event.py | 24 ++++++++++++++++++++++++ gallagher/dto/event.py | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 gallagher/cc/alarms/event.py diff --git a/gallagher/cc/alarms/event.py b/gallagher/cc/alarms/event.py new file mode 100644 index 00000000..34c97c50 --- /dev/null +++ b/gallagher/cc/alarms/event.py @@ -0,0 +1,24 @@ +""" Events that the Command Centre + +Command Centre has about 80 event types that occur when somebody +authenticates at a device, usually by badging a card. +""" + +from ..utils import ( + APIBase, + EndpointConfig +) + + +class Event( + APIBase +): + """ + + """ + + __config__ = EndpointConfig( + endpoint="events", + dto_list=EventResponse, + dto_retrieve=EventDetail, + ) diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index d6f228ab..f13b9723 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -1,6 +1,7 @@ """ """ +from typing import Optional from .utils import ( AppBaseModel, @@ -14,3 +15,23 @@ class EventType( IdentityMixin, ): name: str + + +class EventSummary( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ Events that the Command Centre generates + + """ + server_display_name: str + time: str + message: Optional[str] + occurrences: int + priority: int + + # Hrefs to follows other events + next: HrefMixin + previous: HrefMixin + updates: HrefMixin From 65eae80db6dad5a5813ebef8a0a693033d500a46 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 14 Nov 2023 15:56:47 +1100 Subject: [PATCH 010/118] chore: adds package placeholder --- gallagher/cc/status_overrides/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gallagher/cc/status_overrides/__init__.py diff --git a/gallagher/cc/status_overrides/__init__.py b/gallagher/cc/status_overrides/__init__.py new file mode 100644 index 00000000..e69de29b From 15ccf3ae0739fd0305ef240b99eebee7fc9ff08d Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 22 Nov 2023 09:48:58 +1100 Subject: [PATCH 011/118] refactor: renames APIBase to APIEndpoint APIEndpoint is a more fitting name going fowards as we implement discover and caching REFS #5 and #8 --- gallagher/cc/__init__.py | 23 +++++++++++++++++----- gallagher/cc/alarms/__init__.py | 4 ++-- gallagher/cc/alarms/day_category.py | 4 ++-- gallagher/cc/alarms/divisions.py | 4 ++-- gallagher/cc/alarms/event.py | 4 ++-- gallagher/cc/alarms/items.py | 6 +++--- gallagher/cc/alarms/schedule.py | 4 ++-- gallagher/cc/cardholders/card_type.py | 4 ++-- gallagher/cc/utils.py | 28 ++++++++++++++++++++++++--- 9 files changed, 58 insertions(+), 23 deletions(-) diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index 46853380..4cf0f9e4 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -26,7 +26,7 @@ from ..dto.discover import DiscoveryResponse from .utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -46,13 +46,26 @@ proxy: Optional[str] = None -class APIDiscovery( - APIBase +class APIFeatureDiscovery( + APIEndpoint ): - """ + """ The Command Centre root API endpoint + + Much of Gallagher's API documentation suggests that we don't + hard code the URL, but instead use the discovery endpoint by + calling the root endpoint. + + This should be a singleton which is instantiated upon initialisation + and then used across the other endpoints. + + For example features.events.events.href is the endpoint for the events + where as features.events.events.updates is the endpoint for getting + updates to the changes to events. + + This differs per endpoint that we work with. """ __config__ = EndpointConfig( - endpoint="", + endpoint="", # The root endpoint is the discovery endpoint dto_list=DiscoveryResponse, ) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 87416197..aa65b94d 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -4,7 +4,7 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -15,7 +15,7 @@ class Alarms( - APIBase + APIEndpoint ): """ Alarms """ diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index 3913a7e6..f3723b14 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -4,7 +4,7 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -13,7 +13,7 @@ ) -class DayCategory(APIBase): +class DayCategory(APIEndpoint): """ Day Categories """ diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 2bb635ac..66e2dc1e 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -3,7 +3,7 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -13,7 +13,7 @@ ) -class Division(APIBase): +class Division(APIEndpoint): """ Gallagher advises against hardcoding the URLs for divisions, and instead recommends using the /api endpoint to discover the URLs from diff --git a/gallagher/cc/alarms/event.py b/gallagher/cc/alarms/event.py index 34c97c50..83b9d76c 100644 --- a/gallagher/cc/alarms/event.py +++ b/gallagher/cc/alarms/event.py @@ -5,13 +5,13 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) class Event( - APIBase + APIEndpoint ): """ diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 93036a4b..697da382 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -1,7 +1,7 @@ """ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -12,7 +12,7 @@ ) -class ItemsTypes(APIBase): +class ItemsTypes(APIEndpoint): """ Gallagher """ @@ -23,7 +23,7 @@ class ItemsTypes(APIBase): ) -class Item(APIBase): +class Item(APIEndpoint): """ Gallagher advises against hardcoding the URLs for divisions, and instead recommends using the /api endpoint to discover the URLs from diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index ba1cc0ef..44a69868 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -3,7 +3,7 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -12,7 +12,7 @@ ) -class Schedule(APIBase): +class Schedule(APIEndpoint): """ Schedules """ diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 4574daab..e834f550 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -2,7 +2,7 @@ """ from ..utils import ( - APIBase, + APIEndpoint, EndpointConfig ) @@ -11,7 +11,7 @@ ) -class CardType(APIBase): +class CardType(APIEndpoint): """ Card Types provide a list of support card types for the instance. These can vary between using physical cards, mobile credentials, or diff --git a/gallagher/cc/utils.py b/gallagher/cc/utils.py index f7d7bc18..bc6e373f 100644 --- a/gallagher/cc/utils.py +++ b/gallagher/cc/utils.py @@ -6,6 +6,8 @@ import httpx +from ..dto.discover import DiscoverResponse + def check_api_key_format(api_key): """ Validates that the Gallagher Key is in the right format. @@ -68,7 +70,7 @@ class EndpointConfig: # fields: list[str] = [] # Optional list of fields -class APIBase(): +class APIEndpoint(): """ Base class for all API objects All API endpoints must inherit from this class and provide a Config class @@ -79,12 +81,32 @@ class APIBase(): """ + # Discover response object, each endpoint will reference + # one of the instance variable Href property to get the + # path to the endpoint. + # + # Gallagher recommends that the endpdoints not be hardcoded + # into the client and instead be discovered at runtime. + # + # Note that if a feature has not been licensed by a client + # then the path will be set to None, if the client attempts + # to access the endpoint then the library will throw an exception + # + # This value is memoized and should perform + paths = DiscoverResponse() + # This must be overridden by each child class that inherits # from this base class. __config__ = None @classmethod def _discover(cls): + """ Discovers the endpoints for the given API + + This is a memoized function that will only be called once + on the first operation that is accessed, subsequent calls + will return the cached result. + """ pass @classmethod @@ -148,8 +170,8 @@ def delete(cls): pass @classmethod - def search(cls): - """ + def search(cls, **kwargs): + """ Search """ pass From 752e7e17177fe5c6fe5e966feef56c2258581bfd Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 22 Nov 2023 09:50:30 +1100 Subject: [PATCH 012/118] feat: makes all features optional in discovery gallagher command centre can have features not available per instance of the command centre, this makes all the features optional hence if one is not licensed then an exception is thrown by the client REFS #8 --- gallagher/dto/discover.py | 52 ++++++++++++++++++++------------------- gallagher/exception.py | 10 ++++++++ tests/test_discover.py | 4 +-- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 61b55eb8..2272f7ca 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -4,6 +4,8 @@ """ +from typing import Optional + from .utils import ( AppBaseModel, HrefMixin @@ -176,31 +178,31 @@ class FeaturesDetail( """ """ - access_groups: FeatureAccessGroups - access_zones: FeatureAccessZones - alarm_zones: FeatureAlarmZones - alarms: FeatureAlarms - card_types: FeatureCardTypes - cardholders: FeatureCardholders - competencies: FeatureCompetencies - day_categories: FeatureDayCategories - divisions: FeatureDivisions - doors: FeatureDoors - elevators: FeatureElevators - events: FeatureEvents - fence_zones: FeatureFenceZones - inputs: FeatureInputs - interlock_groups: FeatureInterlockGroups - items: FeatureItems - locker_banks: FeatureLockerBanks - macros: FeatureMacros - operator_groups: FeatureOperatorGroups - outputs: FeatureOutputs - personal_data_fields: FeaturePersonalDataFields - receptions: FeatureReceptions - roles: FeatureRoles - schedules: FeatureSchedules - visits: FeatureVisits + access_groups: Optional[FeatureAccessGroups] = None + access_zones: Optional[FeatureAccessZones] = None + alarm_zones: Optional[FeatureAlarmZones] = None + alarms: Optional[FeatureAlarms] = None + card_types: Optional[FeatureCardTypes] = None + cardholders: Optional[FeatureCardholders] = None + competencies: Optional[FeatureCompetencies] = None + day_categories: Optional[FeatureDayCategories] = None + divisions: Optional[FeatureDivisions] = None + doors: Optional[FeatureDoors] = None + elevators: Optional[FeatureElevators] = None + events: Optional[FeatureEvents] = None + fence_zones: Optional[FeatureFenceZones] = None + inputs: Optional[FeatureInputs] = None + interlock_groups: Optional[FeatureInterlockGroups] = None + items: Optional[FeatureItems] = None + locker_banks: Optional[FeatureLockerBanks] = None + macros: Optional[FeatureMacros] = None + operator_groups: Optional[FeatureOperatorGroups] = None + outputs: Optional[FeatureOutputs] = None + personal_data_fields: Optional[FeaturePersonalDataFields] = None + receptions: Optional[FeatureReceptions] = None + roles: Optional[FeatureRoles] = None + schedules: Optional[FeatureSchedules] = None + visits: Optional[FeatureVisits] = None class DiscoveryResponse( diff --git a/gallagher/exception.py b/gallagher/exception.py index cbcbc3a7..56292993 100644 --- a/gallagher/exception.py +++ b/gallagher/exception.py @@ -6,3 +6,13 @@ Everything declared here indicates an implementation error. """ + + +class FeatureNotLicensedException(Exception): + """ Raised when a feature is not licensed + + This exception is raised when the client attempts to access + an endpoint that is not licensed by the server. + + """ + pass diff --git a/tests/test_discover.py b/tests/test_discover.py index 22a7e251..a4e40fd8 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -5,12 +5,12 @@ def test_discover(): - from gallagher.cc import APIDiscovery + from gallagher.cc import APIFeatureDiscovery from gallagher.dto.discover import ( DiscoveryResponse, FeaturesDetail ) - response = APIDiscovery.list() + response = APIFeatureDiscovery.list() assert type(response) is DiscoveryResponse assert type(response.features) is FeaturesDetail From e43f834ec03761cb2d8004415d541009f2c9b82d Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 22 Nov 2023 10:26:58 +1100 Subject: [PATCH 013/118] docs: adds intention to api discovery parser we are moving towards discovering the api endpoints dynamically, this is a major refactor in the way the api endpoints are going to work REFS #5 and #8 --- gallagher/dto/discover.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 2272f7ca..1181d689 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -175,8 +175,14 @@ class FeatureVisits( class FeaturesDetail( AppBaseModel, ): - """ + """ A detailed list of features that are available on the server. + All features are marked as Optional, which means that by default + it's assumed that they are not available on the server. Upon discovery + if a feature is enabled on the server then we receive a href which + indicates to the client that the feature is available. + + If a feature is unavailable the API client will throw an exception. """ access_groups: Optional[FeatureAccessGroups] = None access_zones: Optional[FeatureAccessZones] = None @@ -208,7 +214,18 @@ class FeaturesDetail( class DiscoveryResponse( AppBaseModel, ): - """ + """ A response that outlines the capability of the server + + Gallagher requires customers to license individual features, if they are + the server will return a 403 HTTP code. The purpose of this model is to + discover what features are available on the server. + + The response should be memoized as it is unlikely to change during individual + sessions, they can however change over a period of time. + + This API client is updated to work with various versions of the server, the + server responds with a version string that can be used to determine if + the API client can work with the server. """ version: str From d48390ad5d91d9683f5daa9e5d72fa4040bc8ecc Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 25 Nov 2023 10:54:45 +1100 Subject: [PATCH 014/118] refactor: discover api endpoints dynamically gallagher command centre makes available a set of hrefs that should be dynamically references as opposed to statically referencing api endpoints this is the start of the change to do so. as part of this it's recommended to - drop the root api endpoint query to be a privat endpoint - the utils package to be renamed as core - move the discovery contant outside of the APIEndpoint class REFS #5 --- gallagher/cc/alarms/items.py | 2 +- gallagher/cc/utils.py | 28 +++++++++++++++++++++++++--- gallagher/dto/discover.py | 10 +++++++--- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 697da382..2b7913d9 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -18,7 +18,7 @@ class ItemsTypes(APIEndpoint): """ __config__ = EndpointConfig( - endpoint="items/types", + endpoint=cls.paths.features.alarms.alarms.href, dto_list=ItemTypesResponse, ) diff --git a/gallagher/cc/utils.py b/gallagher/cc/utils.py index bc6e373f..39a77fdb 100644 --- a/gallagher/cc/utils.py +++ b/gallagher/cc/utils.py @@ -1,12 +1,25 @@ """ Utilities for the Gallagher Command Centre API +This package provides a set of utilities that are used that each endpoint +uses to communicate with the Gallagher Command Centre API. + +Every endpoint inherits from the APIEndpoint class and must define +a configuration that is assigned to the variable __config__. + +The endpoint variable in the EndpointConfig should be assigned to a reference +to the endpoint in the DiscoveryResponse object. When initialised the +endpoint will be assigned to None but will self heal as part of +the bootstrapping process. """ from typing import Optional from dataclasses import dataclass import httpx -from ..dto.discover import DiscoverResponse +from ..dto.discover import ( + DiscoveryResponse, + FeaturesDetail, +) def check_api_key_format(api_key): @@ -93,7 +106,10 @@ class APIEndpoint(): # to access the endpoint then the library will throw an exception # # This value is memoized and should perform - paths = DiscoverResponse() + paths = DiscoveryResponse( + version="0.0.0", # Indicates that it's not been discovered + features=FeaturesDetail() + ) # This must be overridden by each child class that inherits # from this base class. @@ -107,7 +123,13 @@ def _discover(cls): on the first operation that is accessed, subsequent calls will return the cached result. """ - pass + # Auto-discovery of the API endpoints, this will + # be called as part of the bootstrapping process + from ..cc import ( + APIFeatureDiscovery + ) + + cls.paths = APIFeatureDiscovery.list() @classmethod def list(cls, skip=0): diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 1181d689..73762e0e 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -4,7 +4,10 @@ """ -from typing import Optional +from typing import ( + Annotated, + Optional, +) from .utils import ( AppBaseModel, @@ -228,8 +231,9 @@ class DiscoveryResponse( the API client can work with the server. """ - version: str - features: FeaturesDetail + version: Annotated[str, "The version of the server"] + features: Annotated[FeaturesDetail, + "A list of features available on the server"] @property def get_sem_ver(self): From 2655b8c8f24da7f4a3fc9bac8707554295ab17bf Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 25 Nov 2023 15:42:25 +1100 Subject: [PATCH 015/118] refactor: renames utils to core REFS #5 --- gallagher/cc/__init__.py | 2 +- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/event.py | 2 +- gallagher/cc/alarms/items.py | 2 +- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/card_type.py | 2 +- gallagher/cc/{utils.py => core.py} | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename gallagher/cc/{utils.py => core.py} (99%) diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index 4cf0f9e4..4a9a0543 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -25,7 +25,7 @@ from ..const import URL from ..dto.discover import DiscoveryResponse -from .utils import ( +from .core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index aa65b94d..b8e42963 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -3,7 +3,7 @@ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index f3723b14..d5e79acf 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -3,7 +3,7 @@ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 66e2dc1e..6e911e9f 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -2,7 +2,7 @@ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/event.py b/gallagher/cc/alarms/event.py index 83b9d76c..8710bba3 100644 --- a/gallagher/cc/alarms/event.py +++ b/gallagher/cc/alarms/event.py @@ -4,7 +4,7 @@ authenticates at a device, usually by badging a card. """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 2b7913d9..6b0cc830 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -1,6 +1,6 @@ """ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 44a69868..71dc6f87 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -2,7 +2,7 @@ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index e834f550..f5973c33 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -1,7 +1,7 @@ """ """ -from ..utils import ( +from ..core import ( APIEndpoint, EndpointConfig ) diff --git a/gallagher/cc/utils.py b/gallagher/cc/core.py similarity index 99% rename from gallagher/cc/utils.py rename to gallagher/cc/core.py index 39a77fdb..93a5de64 100644 --- a/gallagher/cc/utils.py +++ b/gallagher/cc/core.py @@ -125,7 +125,7 @@ def _discover(cls): """ # Auto-discovery of the API endpoints, this will # be called as part of the bootstrapping process - from ..cc import ( + from . import ( APIFeatureDiscovery ) From 7871c75eaf5e4acc6575f87a40a19aa17a51f29b Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:08:58 +1100 Subject: [PATCH 016/118] feat: adds actions task --- .github/workflows/run-tests.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000..a98ee15c --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,21 @@ +name: Testing +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version-file: pyproject.toml + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} From b46c8307d5e38b5fc48028acdda477bdf58491bf Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:09:13 +1100 Subject: [PATCH 017/118] refactor: allows the test task to take an optional input --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index bffcaff6..dc0d6229 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -14,7 +14,7 @@ tasks: runs all the tests inside the virtualenv, optionally provide the name of the test as an argument to run a single test cmds: - - poetry run coverage run -m pytest tests/{{.CLI_ARGS}} + - poetry run coverage run -m pytest -s tests/{{.CLI_ARGS}} test:coverreport: desc: runs coverage inside the server container cmds: From 22752f9cc66cdfbc36ae7b343ef826e2a752694c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:11:47 +1100 Subject: [PATCH 018/118] refactor: first attempt a dynamic discovery this is my first refactor towards getting dynamic discovery working there are still issues with accessing the singlton to share the discovered version around along side caching the discovery results REFS #5 --- .github/workflows/run-tests.yml | 7 ++++ gallagher/cc/__init__.py | 47 +++++++++------------- gallagher/cc/alarms/items.py | 7 +++- gallagher/cc/core.py | 69 +++++++++++++++++++++------------ gallagher/const.py | 22 ++++++++++- gallagher/dto/utils.py | 11 ++++++ gallagher/exception.py | 2 +- 7 files changed, 107 insertions(+), 58 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index a98ee15c..ef332705 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,3 +19,10 @@ jobs: with: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install dependencies + run: | + pip install poetry + poetry install + - name: Run tests + run: | + task test diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index 4a9a0543..7be38100 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -23,11 +23,10 @@ from typing import Optional from ..const import URL -from ..dto.discover import DiscoveryResponse -from .core import ( - APIEndpoint, - EndpointConfig +from ..dto.discover import ( + DiscoveryResponse, + FeaturesDetail, ) # Follow the instructions in the Gallagher documentation @@ -45,27 +44,19 @@ # should you wish to use a proxy, set this to the proxy URL proxy: Optional[str] = None - -class APIFeatureDiscovery( - APIEndpoint -): - """ The Command Centre root API endpoint - - Much of Gallagher's API documentation suggests that we don't - hard code the URL, but instead use the discovery endpoint by - calling the root endpoint. - - This should be a singleton which is instantiated upon initialisation - and then used across the other endpoints. - - For example features.events.events.href is the endpoint for the events - where as features.events.events.updates is the endpoint for getting - updates to the changes to events. - - This differs per endpoint that we work with. - - """ - __config__ = EndpointConfig( - endpoint="", # The root endpoint is the discovery endpoint - dto_list=DiscoveryResponse, - ) +# Discover response object, each endpoint will reference +# one of the instance variable Href property to get the +# path to the endpoint. +# +# Gallagher recommends that the endpoints not be hardcoded +# into the client and instead be discovered at runtime. +# +# Note that if a feature has not been licensed by a client +# then the path will be set to None, if the client attempts +# to access the endpoint then the library will throw an exception +# +# This value is memoized and should perform +CAPABILITIES = DiscoveryResponse( + version="0.0.0", # Indicates that it's not been discovered + features=FeaturesDetail() +) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 6b0cc830..fc1655c4 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -1,8 +1,11 @@ """ """ + +from gallagher.cc import CAPABILITIES + from ..core import ( APIEndpoint, - EndpointConfig + EndpointConfig, ) from ...dto.items import ( @@ -18,7 +21,7 @@ class ItemsTypes(APIEndpoint): """ __config__ = EndpointConfig( - endpoint=cls.paths.features.alarms.alarms.href, + endpoint="items/types", dto_list=ItemTypesResponse, ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 93a5de64..afc2a8cc 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -16,9 +16,12 @@ import httpx +from gallagher.exception import ( + UnlicensedFeatureException +) + from ..dto.discover import ( DiscoveryResponse, - FeaturesDetail, ) @@ -82,6 +85,18 @@ class EndpointConfig: sort: Optional[str] = "id" # Can be set to id or -id # fields: list[str] = [] # Optional list of fields + @classmethod + def validate_endpoint(cls): + """ Check to see if the feature is licensed and available + + Gallagher REST API is licensed per feature, if a feature is not + the endpoint is set to none and we should throw an exception + """ + if not cls.endpoint: + raise UnlicensedFeatureException( + "Endpoint not defined" + ) + class APIEndpoint(): """ Base class for all API objects @@ -94,42 +109,42 @@ class APIEndpoint(): """ - # Discover response object, each endpoint will reference - # one of the instance variable Href property to get the - # path to the endpoint. - # - # Gallagher recommends that the endpdoints not be hardcoded - # into the client and instead be discovered at runtime. - # - # Note that if a feature has not been licensed by a client - # then the path will be set to None, if the client attempts - # to access the endpoint then the library will throw an exception - # - # This value is memoized and should perform - paths = DiscoveryResponse( - version="0.0.0", # Indicates that it's not been discovered - features=FeaturesDetail() - ) - # This must be overridden by each child class that inherits # from this base class. __config__ = None @classmethod def _discover(cls): - """ Discovers the endpoints for the given API + """ The Command Centre root API endpoint + + Much of Gallagher's API documentation suggests that we don't + hard code the URL, but instead use the discovery endpoint by + calling the root endpoint. + + This should be a singleton which is instantiated upon initialisation + and then used across the other endpoints. + + For example features.events.events.href is the endpoint for the events + where as features.events.events.updates is the endpoint for getting + updates to the changes to events. - This is a memoized function that will only be called once - on the first operation that is accessed, subsequent calls - will return the cached result. + This differs per endpoint that we work with. """ + # Auto-discovery of the API endpoints, this will # be called as part of the bootstrapping process - from . import ( - APIFeatureDiscovery + from . import api_base + response = httpx.get( + api_base, + headers=get_authorization_headers(), ) - cls.paths = APIFeatureDiscovery.list() + parsed_obj = DiscoveryResponse.model_validate( + response.json() + ) + + from . import CAPABILITIES + CAPABILITIES = parsed_obj @classmethod def list(cls, skip=0): @@ -138,6 +153,8 @@ def list(cls, skip=0): Most resources can be searched which is exposed by this method. Resources also allow pagination which can be controlled by the skip """ + cls._discover() + from . import api_base response = httpx.get( f'{api_base}{cls.__config__.endpoint}', @@ -158,6 +175,8 @@ def retrieve(cls, id): Each resource also provides a href and pagination for children. """ + cls._discover() + from . import api_base response = httpx.get( f'{api_base}{cls.__config__.endpoint}/{id}', diff --git a/gallagher/const.py b/gallagher/const.py index 550b66b0..f45764dd 100644 --- a/gallagher/const.py +++ b/gallagher/const.py @@ -1,11 +1,29 @@ """ Constants for the Gallagher Cloud ecosystem + +Gallagher publishes a set of networking constants for their cloud gateways +these should be updated as the documentation is updated. """ + + class URL: + """ DNS names for the cloud gateways + + These are published by Gallagher on Github + https://gallaghersecurity.github.io/commandcentre-cloud-api-gateway.html + """ + + CLOUD_GATEWAY_AU: str = \ + "https://commandcentre-api-au.security.gallagher.cloud/api/" + CLOUD_GATEWAY_US: str = \ + "https://commandcentre-api-us.security.gallagher.cloud/api/" - CLOUD_GATEWAY_AU: str = "https://commandcentre-api-au.security.gallagher.cloud/api/" - CLOUD_GATEWAY_US: str = "https://commandcentre-api-us.security.gallagher.cloud/api/" class IP_ADDR: + """ IP addresses for the cloud gateways + + These are published by Gallagher on Github + https://gallaghersecurity.github.io/commandcentre-cloud-api-gateway.html + """ CLOUD_GATEWAY_AU = [ "3.106.1.6", diff --git a/gallagher/dto/utils.py b/gallagher/dto/utils.py index 811e47d0..be162a15 100644 --- a/gallagher/dto/utils.py +++ b/gallagher/dto/utils.py @@ -3,6 +3,10 @@ """ +from typing import ( + Optional +) +from datetime import datetime from pydantic import ( BaseModel, @@ -37,6 +41,13 @@ class AppBaseModel(BaseModel): alias_generator=to_lower_camel, ) + # Set to the last time each response was retrieved + # If it's set to None then the response was either created + # by the API client or it wasn't retrieved from the server + # + # This is generally used for caching + good_known_since: Optional[datetime] = None + class IdentityMixin(BaseModel): """ Identifier diff --git a/gallagher/exception.py b/gallagher/exception.py index 56292993..84efab4d 100644 --- a/gallagher/exception.py +++ b/gallagher/exception.py @@ -8,7 +8,7 @@ """ -class FeatureNotLicensedException(Exception): +class UnlicensedFeatureException(Exception): """ Raised when a feature is not licensed This exception is raised when the client attempts to access From 9ce564b070922f8fccac6636ca94c2a6c5a2188c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:13:21 +1100 Subject: [PATCH 019/118] fix: adds missing checkout action --- .github/workflows/run-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ef332705..1155dcb9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,6 +10,8 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: From 641a31ebc86b50c559362188e3632b274fe23f70 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:17:52 +1100 Subject: [PATCH 020/118] fix: adds environment variable from secret --- .github/workflows/run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1155dcb9..39ee0790 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -27,4 +27,5 @@ jobs: poetry install - name: Run tests run: | + export GACC_API_KEY=${{ secrets.GACC_API_KEY }} task test From 5de09ee050f2246bcc0ac520aeaf5edda94dd6fb Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:38:01 +1100 Subject: [PATCH 021/118] refactor: discover test disable temporarily --- .github/workflows/run-tests.yml | 6 ++++-- tests/test_discover.py | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 39ee0790..602ae405 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,9 +1,11 @@ name: Testing on: push: - branches: [master] + branches: + - master pull_request: - branches: [master] + branches: + - master jobs: test: diff --git a/tests/test_discover.py b/tests/test_discover.py index a4e40fd8..c33fbe91 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -4,13 +4,14 @@ def test_discover(): + """ + + """ + from gallagher.cc.core import APIEndpoint - from gallagher.cc import APIFeatureDiscovery from gallagher.dto.discover import ( DiscoveryResponse, FeaturesDetail ) - response = APIFeatureDiscovery.list() - assert type(response) is DiscoveryResponse - assert type(response.features) is FeaturesDetail + assert 1 == 1 From 68185e0cedf3373cdba05a55c7b956fc2e830769 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 26 Nov 2023 12:42:19 +1100 Subject: [PATCH 022/118] chore: update dependencies --- poetry.lock | 290 ++++++++++++++++++++++++------------------------- pyproject.toml | 13 ++- 2 files changed, 153 insertions(+), 150 deletions(-) diff --git a/poetry.lock b/poetry.lock index 212c560e..d582fa5e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.0.0" +version = "4.1.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, - {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, + {file = "anyio-4.1.0-py3-none-any.whl", hash = "sha256:56a415fbc462291813a94528a779597226619c8e78af7de0507333f700011e5f"}, + {file = "anyio-4.1.0.tar.gz", hash = "sha256:5a0bec7085176715be77df87fc66d6c9d70626bd752fcc85f57cdbee5b3760da"}, ] [package.dependencies] @@ -27,19 +27,19 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "certifi" -version = "2023.7.22" +version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[package]] @@ -130,39 +130,40 @@ files = [ [[package]] name = "httpcore" -version = "0.16.3" +version = "1.0.2" description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, - {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, + {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, + {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, ] [package.dependencies] -anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = "==1.*" [package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.23.0)"] [[package]] name = "httpx" -version = "0.24.1" +version = "0.25.2" description = "The next generation HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, - {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, + {file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"}, + {file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"}, ] [package.dependencies] +anyio = "*" certifi = "*" -httpcore = ">=0.15.0,<0.18.0" +httpcore = "==1.*" idna = "*" sniffio = "*" @@ -174,13 +175,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] @@ -196,24 +197,24 @@ files = [ [[package]] name = "packaging" -version = "23.0" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] @@ -222,18 +223,18 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.4.2" +version = "2.5.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"}, - {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"}, + {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, + {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.10.1" +pydantic-core = "2.14.5" typing-extensions = ">=4.6.1" [package.extras] @@ -241,117 +242,116 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.10.1" +version = "2.14.5" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"}, - {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"}, - {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"}, - {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"}, - {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"}, - {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"}, - {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"}, - {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"}, - {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"}, - {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"}, - {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"}, - {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"}, - {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"}, - {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"}, - {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"}, - {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"}, - {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"}, - {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"}, - {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"}, - {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"}, - {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"}, - {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"}, - {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"}, - {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"}, - {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"}, - {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"}, - {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"}, - {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"}, - {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"}, - {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"}, - {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"}, - {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"}, - {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, + {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, + {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, + {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, + {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, + {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, + {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, + {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, + {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, + {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, + {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, + {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, + {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, + {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, + {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, + {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, ] [package.dependencies] @@ -397,13 +397,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-order" -version = "1.1.0" +version = "1.2.0" description = "pytest plugin to run your tests in a specific order" optional = false python-versions = ">=3.6" files = [ - {file = "pytest-order-1.1.0.tar.gz", hash = "sha256:139d25b30826b78eebb42722f747eab14c44b88059d7a71d4f79d14a057269a5"}, - {file = "pytest_order-1.1.0-py3-none-any.whl", hash = "sha256:3b3730969c97900fa5cd31ecff80847680ed56b2490954565c14949ba60d9371"}, + {file = "pytest-order-1.2.0.tar.gz", hash = "sha256:944f86b6d441aa7b1da80f801c6ab65b84bbeba472d0a7a12eb43ba26650101a"}, + {file = "pytest_order-1.2.0-py3-none-any.whl", hash = "sha256:9d65c3b6dc6d6ee984d6ae2c6c4aa4f1331e5b915116219075c888c8bcbb93b8"}, ] [package.dependencies] @@ -434,4 +434,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "c39835f0564595eaa7cde3e93817d88e2632343de3bcd3876ad832fc5ff2d5f3" +content-hash = "679bf50da52088350442b14a6073a6aea9854ffcfc37875a1c1ff057fae4acd1" diff --git a/pyproject.toml b/pyproject.toml index c04cabc3..3d419d11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,15 +7,18 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" -httpx = "^0.24.1" -pydantic = "^2.4.2" +httpx = "^0.25.2" +pydantic = "^2.5.2" typing-extensions = "^4.8.0" coverage = "^7.3.2" pytest = "^7.4.3" -pytest-order = "^1.1.0" -anyio = "^4.0.0" +pytest-order = "^1.2.0" +anyio = "^4.1.0" annotated-types = "^0.6.0" -certifi = "^2023.7.22" +certifi = "^2023.11.17" +idna = "^3.6" +packaging = "^23.2" +pluggy = "^1.3.0" [tool.poetry.group.dev.dependencies] From 44aa5d9db63313e5a80b66fde7235bfa931b6134 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 29 Nov 2023 12:43:38 +1100 Subject: [PATCH 023/118] chore: update meta data in preperation for pypi the project is now registered on pypi, initially published through my personal development computer, we want to eventually move this to be part of our ci/cd pipeline and publish the package through a github action REFS #16 --- gallagher/__init__.py | 3 +-- pyproject.toml | 13 ++++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gallagher/__init__.py b/gallagher/__init__.py index f6f23fb1..391ec27b 100644 --- a/gallagher/__init__.py +++ b/gallagher/__init__.py @@ -7,5 +7,4 @@ Distributed under the terms of the MIT License. """ -__version__ = "0.1.0" - +__version__ = "0.1.0-alpha.1" diff --git a/pyproject.toml b/pyproject.toml index 3d419d11..c6ad2e94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,20 @@ [tool.poetry] name = "gallagher" -version = "0.1.0" +version = "0.1.0-alpha.1" description = "Python idiomatic client for Gallagher Command Centre API" authors = ["Dev Mukherjee "] readme = "README.md" +license = "MIT" +repository = "https://github.com/anomaly/gallagher" +documentation = "https://anomaly.github.io/gallagher/" +keywords = ["gallagher", "rest", "api"] +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/anomaly/gallagher/issues" [tool.poetry.dependencies] python = "^3.11" From 4ff5ef49a5887804aae4807f76c432265e918ee4 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 13:18:17 +1100 Subject: [PATCH 024/118] refactor: experinmental commit for dynamic discovery this a commit in a separate branch for me to experinment implementing dynamic discovery of the api endpoints, at the moment because of import cycles the singleton seems to be overrwritten REFS #5 --- gallagher/cc/__init__.py | 17 ---- gallagher/cc/alarms/__init__.py | 14 +-- gallagher/cc/alarms/items.py | 4 +- gallagher/cc/core.py | 24 ++++- gallagher/dto/discover.py | 155 +++++++++++++++++++------------- tests/test_alarm.py | 3 +- 6 files changed, 128 insertions(+), 89 deletions(-) diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index 7be38100..ca030378 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -43,20 +43,3 @@ # By default connections are sent straight to the server # should you wish to use a proxy, set this to the proxy URL proxy: Optional[str] = None - -# Discover response object, each endpoint will reference -# one of the instance variable Href property to get the -# path to the endpoint. -# -# Gallagher recommends that the endpoints not be hardcoded -# into the client and instead be discovered at runtime. -# -# Note that if a feature has not been licensed by a client -# then the path will be set to None, if the client attempts -# to access the endpoint then the library will throw an exception -# -# This value is memoized and should perform -CAPABILITIES = DiscoveryResponse( - version="0.0.0", # Indicates that it's not been discovered - features=FeaturesDetail() -) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index b8e42963..e5729fef 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -1,4 +1,4 @@ -""" +""" Alarms """ @@ -20,8 +20,10 @@ class Alarms( """ Alarms """ - __config__ = EndpointConfig( - endpoint="alarms", - dto_list=AlarmResponse, - dto_retrieve=AlarmZoneSummary, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._capabilities.alarms.alarms.href, + dto_list=AlarmResponse, + dto_retrieve=AlarmZoneSummary, + ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index fc1655c4..a6de9299 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -1,4 +1,6 @@ -""" +""" Items + + """ from gallagher.cc import CAPABILITIES diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index afc2a8cc..a51dc19d 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -22,6 +22,7 @@ from ..dto.discover import ( DiscoveryResponse, + FeaturesDetail, ) @@ -109,6 +110,23 @@ class APIEndpoint(): """ + # Discover response object, each endpoint will reference + # one of the instance variable Href property to get the + # path to the endpoint. + # + # Gallagher recommends that the endpoints not be hardcoded + # into the client and instead be discovered at runtime. + # + # Note that if a feature has not been licensed by a client + # then the path will be set to None, if the client attempts + # to access the endpoint then the library will throw an exception + # + # This value is memoized and should perform + _capabilities = DiscoveryResponse( + version="0.0.0", # Indicates that it's not been discovered + features=FeaturesDetail() + ) + # This must be overridden by each child class that inherits # from this base class. __config__ = None @@ -143,8 +161,10 @@ def _discover(cls): response.json() ) - from . import CAPABILITIES - CAPABILITIES = parsed_obj + cls._capabilities = parsed_obj + + cls.__config__ = cls.get_config() + print(cls.__config__) @classmethod def list(cls, skip=0): diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 73762e0e..4f6392b2 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -9,6 +9,10 @@ Optional, ) +from pydantic.dataclasses import ( + dataclass, +) + from .utils import ( AppBaseModel, HrefMixin @@ -18,163 +22,164 @@ class FeatureAccessGroups( AppBaseModel, ): - access_groups: HrefMixin + access_groups: Optional[HrefMixin] = None class FeatureAccessZones( AppBaseModel, ): - access_zones: HrefMixin + access_zones: Optional[HrefMixin] = None class FeatureAlarmZones( AppBaseModel, ): - alarm_zones: HrefMixin + alarm_zones: Optional[HrefMixin] = None class FeatureAlarms( AppBaseModel, ): - alarms: HrefMixin - divisions: HrefMixin - updates: HrefMixin + alarms: Optional[HrefMixin] = None + divisions: Optional[HrefMixin] = None + updates: Optional[HrefMixin] = None class FeatureCardTypes( AppBaseModel, ): - assign: HrefMixin - card_types: HrefMixin + assign: Optional[HrefMixin] = None + card_types: Optional[HrefMixin] = None class FeatureCardholders( AppBaseModel, ): - cardholders: HrefMixin - changes: HrefMixin - update_location_access_zones: HrefMixin + cardholders: Optional[HrefMixin] = None + changes: Optional[HrefMixin] = None + update_location_access_zones: Optional[HrefMixin] = None class FeatureCompetencies( AppBaseModel, ): - competencies: HrefMixin + competencies: Optional[HrefMixin] = None class FeatureDayCategories( AppBaseModel, ): - day_categories: HrefMixin + day_categories: Optional[HrefMixin] = None class FeatureDivisions( AppBaseModel, ): - divisions: HrefMixin + divisions: Optional[HrefMixin] = None class FeatureDoors( AppBaseModel, ): - doors: HrefMixin + doors: Optional[HrefMixin] = None class FeatureElevators( AppBaseModel, ): - elevator_groups: HrefMixin + elevator_groups: Optional[HrefMixin] = None class FeatureEvents( AppBaseModel, ): - divisions: HrefMixin - event_groups: HrefMixin - events: HrefMixin - updates: HrefMixin + divisions: Optional[HrefMixin] = None + event_groups: Optional[HrefMixin] = None + events: Optional[HrefMixin] = None + updates: Optional[HrefMixin] = None class FeatureFenceZones( AppBaseModel, ): - fence_zones: HrefMixin + fence_zones: Optional[HrefMixin] = None class FeatureInputs( AppBaseModel, ): - inputs: HrefMixin + inputs: Optional[HrefMixin] = None class FeatureInterlockGroups( AppBaseModel, ): - interlock_groups: HrefMixin + interlock_groups: Optional[HrefMixin] = None class FeatureItems( AppBaseModel, ): - item_types: HrefMixin - items: HrefMixin - updates: HrefMixin + item_types: Optional[HrefMixin] = None + items: Optional[HrefMixin] = None + updates: Optional[HrefMixin] = None class FeatureLockerBanks( AppBaseModel, ): - locker_banks: HrefMixin + locker_banks: Optional[HrefMixin] = None class FeatureMacros( AppBaseModel, ): - macros: HrefMixin + macros: Optional[HrefMixin] = None class FeatureOperatorGroups( AppBaseModel, ): - operator_groups: HrefMixin + operator_groups: Optional[HrefMixin] = None class FeatureOutputs( AppBaseModel, ): - outputs: HrefMixin + outputs: Optional[HrefMixin] = None class FeaturePersonalDataFields( AppBaseModel, ): - personal_data_fields: HrefMixin + personal_data_fields: Optional[HrefMixin] = None class FeatureReceptions( AppBaseModel, ): - receptions: HrefMixin + receptions: Optional[HrefMixin] = None class FeatureRoles( AppBaseModel, ): - roles: HrefMixin + roles: Optional[HrefMixin] = None class FeatureSchedules( AppBaseModel, ): - schedules: HrefMixin + schedules: Optional[HrefMixin] = None class FeatureVisits( AppBaseModel, ): - visits: HrefMixin + visits: Optional[HrefMixin] = None +@dataclass class FeaturesDetail( AppBaseModel, ): @@ -187,33 +192,59 @@ class FeaturesDetail( If a feature is unavailable the API client will throw an exception. """ - access_groups: Optional[FeatureAccessGroups] = None - access_zones: Optional[FeatureAccessZones] = None - alarm_zones: Optional[FeatureAlarmZones] = None - alarms: Optional[FeatureAlarms] = None - card_types: Optional[FeatureCardTypes] = None - cardholders: Optional[FeatureCardholders] = None - competencies: Optional[FeatureCompetencies] = None - day_categories: Optional[FeatureDayCategories] = None - divisions: Optional[FeatureDivisions] = None - doors: Optional[FeatureDoors] = None - elevators: Optional[FeatureElevators] = None - events: Optional[FeatureEvents] = None - fence_zones: Optional[FeatureFenceZones] = None - inputs: Optional[FeatureInputs] = None - interlock_groups: Optional[FeatureInterlockGroups] = None - items: Optional[FeatureItems] = None - locker_banks: Optional[FeatureLockerBanks] = None - macros: Optional[FeatureMacros] = None - operator_groups: Optional[FeatureOperatorGroups] = None - outputs: Optional[FeatureOutputs] = None - personal_data_fields: Optional[FeaturePersonalDataFields] = None - receptions: Optional[FeatureReceptions] = None - roles: Optional[FeatureRoles] = None - schedules: Optional[FeatureSchedules] = None - visits: Optional[FeatureVisits] = None - - + # access_groups: Optional[FeatureAccessGroups]\ + # = FeatureAccessGroups() + # access_zones: Optional[FeatureAccessZones]\ + # = FeatureAccessZones() + # alarm_zones: Optional[FeatureAlarmZones]\ + # = FeatureAlarmZones() + alarms: Optional[FeatureAlarms]\ + = FeatureAlarms() + # card_types: Optional[FeatureCardTypes]\ + # = FeatureCardTypes() + # cardholders: Optional[FeatureCardholders]\ + # = FeatureCardholders() + # competencies: Optional[FeatureCompetencies]\ + # = FeatureCompetencies() + # day_categories: Optional[FeatureDayCategories]\ + # = FeatureDayCategories() + # divisions: Optional[FeatureDivisions]\ + # = FeatureDivisions() + # doors: Optional[FeatureDoors]\ + # = FeatureDoors() + # elevators: Optional[FeatureElevators]\ + # = FeatureElevators() + # events: Optional[FeatureEvents]\ + # = FeatureEvents() + # fence_zones: Optional[FeatureFenceZones]\ + # = FeatureFenceZones() + # inputs: Optional[FeatureInputs]\ + # = FeatureInputs() + # interlock_groups: Optional[FeatureInterlockGroups]\ + # = FeatureInterlockGroups() + # items: Optional[FeatureItems]\ + # = FeatureItems() + # locker_banks: Optional[FeatureLockerBanks]\ + # = FeatureLockerBanks() + # macros: Optional[FeatureMacros]\ + # = FeatureMacros() + # operator_groups: Optional[FeatureOperatorGroups]\ + # = FeatureOperatorGroups() + # outputs: Optional[FeatureOutputs]\ + # = FeatureOutputs() + # personal_data_fields: Optional[FeaturePersonalDataFields]\ + # = FeaturePersonalDataFields() + # receptions: Optional[FeatureReceptions]\ + # = FeatureReceptions() + # roles: Optional[FeatureRoles]\ + # = FeatureRoles() + # schedules: Optional[FeatureSchedules]\ + # = FeatureSchedules() + # visits: Optional[FeatureVisits]\ + # = FeatureVisits() + + +@dataclass class DiscoveryResponse( AppBaseModel, ): diff --git a/tests/test_alarm.py b/tests/test_alarm.py index 628d9dd3..bf5ba187 100644 --- a/tests/test_alarm.py +++ b/tests/test_alarm.py @@ -1,4 +1,5 @@ -""" +""" Alarms are raised by the command centre, we want +to make sure that we are getting valid responses. """ From 85e40ad707a29e5c09a3740b19ed1dd8264bb01c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 13:19:46 +1100 Subject: [PATCH 025/118] fix: version test post pubilshing alpha --- tests/test_version.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_version.py b/tests/test_version.py index 8b0c9991..d1adffdc 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,7 +1,10 @@ -""" +""" Tests to see that are running the tests against the right version + + """ from gallagher import __version__ + def test_version(): - assert __version__ == '0.1.0' + assert __version__ == '0.1.0-alpha.1' From 8602083ca318732bf8648862404b772be5ce1b5c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 13:46:53 +1100 Subject: [PATCH 026/118] refactor: dynamic configuration primarily working we have the discovery object being initialised when the base class initialises and then the discovery fetches the url, note that the href that the discover will return are fully qualified addresses this drops using the api_base prefix post this we need to formalies the configuration and syntax so this scales to all endpoints we are also yet to test for unlicensed features REFS #5 --- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/core.py | 5 +- gallagher/dto/discover.py | 108 +++++++++++++++----------------- 3 files changed, 54 insertions(+), 61 deletions(-) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index e5729fef..64f1e20e 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -23,7 +23,7 @@ class Alarms( @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.alarms.alarms.href, + endpoint=cls._capabilities.features.alarms.alarms.href, dto_list=AlarmResponse, dto_retrieve=AlarmZoneSummary, ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index a51dc19d..a55070ab 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -23,6 +23,7 @@ from ..dto.discover import ( DiscoveryResponse, FeaturesDetail, + FeatureAlarms, ) @@ -164,7 +165,6 @@ def _discover(cls): cls._capabilities = parsed_obj cls.__config__ = cls.get_config() - print(cls.__config__) @classmethod def list(cls, skip=0): @@ -175,9 +175,8 @@ def list(cls, skip=0): """ cls._discover() - from . import api_base response = httpx.get( - f'{api_base}{cls.__config__.endpoint}', + f'{cls.__config__.endpoint}', headers=get_authorization_headers(), ) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 4f6392b2..c1b0ebb2 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -9,10 +9,6 @@ Optional, ) -from pydantic.dataclasses import ( - dataclass, -) - from .utils import ( AppBaseModel, HrefMixin @@ -179,7 +175,6 @@ class FeatureVisits( visits: Optional[HrefMixin] = None -@dataclass class FeaturesDetail( AppBaseModel, ): @@ -192,59 +187,58 @@ class FeaturesDetail( If a feature is unavailable the API client will throw an exception. """ - # access_groups: Optional[FeatureAccessGroups]\ - # = FeatureAccessGroups() - # access_zones: Optional[FeatureAccessZones]\ - # = FeatureAccessZones() - # alarm_zones: Optional[FeatureAlarmZones]\ - # = FeatureAlarmZones() + access_groups: Optional[FeatureAccessGroups]\ + = FeatureAccessGroups() + access_zones: Optional[FeatureAccessZones]\ + = FeatureAccessZones() + alarm_zones: Optional[FeatureAlarmZones]\ + = FeatureAlarmZones() alarms: Optional[FeatureAlarms]\ = FeatureAlarms() - # card_types: Optional[FeatureCardTypes]\ - # = FeatureCardTypes() - # cardholders: Optional[FeatureCardholders]\ - # = FeatureCardholders() - # competencies: Optional[FeatureCompetencies]\ - # = FeatureCompetencies() - # day_categories: Optional[FeatureDayCategories]\ - # = FeatureDayCategories() - # divisions: Optional[FeatureDivisions]\ - # = FeatureDivisions() - # doors: Optional[FeatureDoors]\ - # = FeatureDoors() - # elevators: Optional[FeatureElevators]\ - # = FeatureElevators() - # events: Optional[FeatureEvents]\ - # = FeatureEvents() - # fence_zones: Optional[FeatureFenceZones]\ - # = FeatureFenceZones() - # inputs: Optional[FeatureInputs]\ - # = FeatureInputs() - # interlock_groups: Optional[FeatureInterlockGroups]\ - # = FeatureInterlockGroups() - # items: Optional[FeatureItems]\ - # = FeatureItems() - # locker_banks: Optional[FeatureLockerBanks]\ - # = FeatureLockerBanks() - # macros: Optional[FeatureMacros]\ - # = FeatureMacros() - # operator_groups: Optional[FeatureOperatorGroups]\ - # = FeatureOperatorGroups() - # outputs: Optional[FeatureOutputs]\ - # = FeatureOutputs() - # personal_data_fields: Optional[FeaturePersonalDataFields]\ - # = FeaturePersonalDataFields() - # receptions: Optional[FeatureReceptions]\ - # = FeatureReceptions() - # roles: Optional[FeatureRoles]\ - # = FeatureRoles() - # schedules: Optional[FeatureSchedules]\ - # = FeatureSchedules() - # visits: Optional[FeatureVisits]\ - # = FeatureVisits() - - -@dataclass + card_types: Optional[FeatureCardTypes]\ + = FeatureCardTypes() + cardholders: Optional[FeatureCardholders]\ + = FeatureCardholders() + competencies: Optional[FeatureCompetencies]\ + = FeatureCompetencies() + day_categories: Optional[FeatureDayCategories]\ + = FeatureDayCategories() + divisions: Optional[FeatureDivisions]\ + = FeatureDivisions() + doors: Optional[FeatureDoors]\ + = FeatureDoors() + elevators: Optional[FeatureElevators]\ + = FeatureElevators() + events: Optional[FeatureEvents]\ + = FeatureEvents() + fence_zones: Optional[FeatureFenceZones]\ + = FeatureFenceZones() + inputs: Optional[FeatureInputs]\ + = FeatureInputs() + interlock_groups: Optional[FeatureInterlockGroups]\ + = FeatureInterlockGroups() + items: Optional[FeatureItems]\ + = FeatureItems() + locker_banks: Optional[FeatureLockerBanks]\ + = FeatureLockerBanks() + macros: Optional[FeatureMacros]\ + = FeatureMacros() + operator_groups: Optional[FeatureOperatorGroups]\ + = FeatureOperatorGroups() + outputs: Optional[FeatureOutputs]\ + = FeatureOutputs() + personal_data_fields: Optional[FeaturePersonalDataFields]\ + = FeaturePersonalDataFields() + receptions: Optional[FeatureReceptions]\ + = FeatureReceptions() + roles: Optional[FeatureRoles]\ + = FeatureRoles() + schedules: Optional[FeatureSchedules]\ + = FeatureSchedules() + visits: Optional[FeatureVisits]\ + = FeatureVisits() + + class DiscoveryResponse( AppBaseModel, ): @@ -262,7 +256,7 @@ class DiscoveryResponse( the API client can work with the server. """ - version: Annotated[str, "The version of the server"] + version: Annotated[str, "The version of the server"] = "0.0.0" features: Annotated[FeaturesDetail, "A list of features available on the server"] From d588eaf71c74a8f94b5f02c0806004490bf78635 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:28:24 +1100 Subject: [PATCH 027/118] fix: collect only endpoint for tasks --- Taskfile.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Taskfile.yml b/Taskfile.yml index dc0d6229..919cfb10 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -15,6 +15,12 @@ tasks: provide the name of the test as an argument to run a single test cmds: - poetry run coverage run -m pytest -s tests/{{.CLI_ARGS}} + test:list: + desc: lists the available tests + summary: + runs collect only on pytest to list the tests available + cmds: + - poetry run pytest --co test:coverreport: desc: runs coverage inside the server container cmds: From 7b3ff2882122138621b9b305b673fc26c40fb4e0 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:28:40 +1100 Subject: [PATCH 028/118] refactor: initialisation of the discovery endpoint --- gallagher/dto/discover.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index c1b0ebb2..b3f71bbb 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -258,7 +258,8 @@ class DiscoveryResponse( version: Annotated[str, "The version of the server"] = "0.0.0" features: Annotated[FeaturesDetail, - "A list of features available on the server"] + "A list of features available on the server" + ] = FeaturesDetail() @property def get_sem_ver(self): From be14035842d82f20000a9c7e2a9472ad95b91a90 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:32:10 +1100 Subject: [PATCH 029/118] refactor: formalises configuration pattern for dynamic configuration adds context around the design of the classes and lifecycle for dynamic discovery of api endpoints REFS #5 --- gallagher/cc/core.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index a55070ab..252d2f69 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -128,10 +128,19 @@ class APIEndpoint(): features=FeaturesDetail() ) - # This must be overridden by each child class that inherits - # from this base class. + # Do not set this variable in your class, this is set by the + # lifecycle methods and use to cache the configuration object __config__ = None + @classmethod + def get_config(cls): + """ Returns the configuration for the endpoint + + This method can be overridden by the child class to + provide additional configuration options. + """ + return None + @classmethod def _discover(cls): """ The Command Centre root API endpoint @@ -162,8 +171,12 @@ def _discover(cls): response.json() ) + # Assign the capabilities to the class, this should + # result in the endpoint cls._capabilities = parsed_obj + # Set this so the configuration is only discovered + # once per endpoint cls.__config__ = cls.get_config() @classmethod @@ -198,7 +211,7 @@ def retrieve(cls, id): from . import api_base response = httpx.get( - f'{api_base}{cls.__config__.endpoint}/{id}', + f'{cls.__config__.endpoint}/{id}', headers=get_authorization_headers(), ) From 977ce9ccfe9c0f4a377fe6ac7f3bfc280c40d834 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:36:54 +1100 Subject: [PATCH 030/118] refactor: configuration of endpoints to the new design --- gallagher/cc/alarms/day_category.py | 13 ++++++++----- gallagher/cc/alarms/divisions.py | 12 +++++++----- gallagher/cc/alarms/event.py | 12 +++++++----- gallagher/cc/alarms/items.py | 23 ++++++++++++++--------- gallagher/cc/alarms/schedule.py | 10 ++++++---- gallagher/cc/cardholders/card_type.py | 12 ++++++++---- 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index d5e79acf..65b32075 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -17,8 +17,11 @@ class DayCategory(APIEndpoint): """ Day Categories """ - __config__ = EndpointConfig( - endpoint="day_categories", - dto_list=DayCategoryResponse, - dto_retrieve=DayCategoryResponse, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.day_categories + .day_categories.href, + dto_list=DayCategoryResponse, + dto_retrieve=DayCategoryResponse, + ) diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 6e911e9f..a20a5f4d 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -21,8 +21,10 @@ class Division(APIEndpoint): """ - __config__ = EndpointConfig( - endpoint="divisions", - dto_list=DivisionDetailResponse, - dto_retrieve=DivisionDetail, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.divisions.divisions.href, + dto_list=DivisionDetailResponse, + dto_retrieve=DivisionDetail, + ) diff --git a/gallagher/cc/alarms/event.py b/gallagher/cc/alarms/event.py index 8710bba3..c3f1cf2c 100644 --- a/gallagher/cc/alarms/event.py +++ b/gallagher/cc/alarms/event.py @@ -17,8 +17,10 @@ class Event( """ - __config__ = EndpointConfig( - endpoint="events", - dto_list=EventResponse, - dto_retrieve=EventDetail, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint="events", + dto_list=EventResponse, + dto_retrieve=EventDetail, + ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index a6de9299..c9770a6c 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -22,10 +22,13 @@ class ItemsTypes(APIEndpoint): Gallagher """ - __config__ = EndpointConfig( - endpoint="items/types", - dto_list=ItemTypesResponse, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.items.item_types.href, + dto_list=ItemTypesResponse, + dto_retrieve=ItemTypesResponse, + ) class Item(APIEndpoint): @@ -36,8 +39,10 @@ class Item(APIEndpoint): """ - __config__ = EndpointConfig( - endpoint="items", - dto_list=ItemsSummaryResponse, - dto_retrieve=ItemDetail, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.items.items.href, + dto_list=ItemsSummaryResponse, + dto_retrieve=ItemDetail, + ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 71dc6f87..40d35c89 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -16,7 +16,9 @@ class Schedule(APIEndpoint): """ Schedules """ - __config__ = EndpointConfig( - endpoint="schedules", - dto_list=ScheduleSummaryResponse, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.schedules.schedules.href, + dto_list=ScheduleSummaryResponse, + ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index f5973c33..9669b08b 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -19,7 +19,11 @@ class CardType(APIEndpoint): of credentials available on this particular instance. """ - __config__ = EndpointConfig( - endpoint="card_types", - dto_list=CardTypeResponse, - ) + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._discover.features.card_types + .card_types.href, + dto_list=CardTypeResponse, + dto_retrieve=CardTypeResponse, + ) From 83ec6ee285fcafa6e37c4347a555aa41721261e7 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:42:28 +1100 Subject: [PATCH 031/118] fix: incorrect reference to capabilities --- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/items.py | 4 ++-- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/card_type.py | 3 +-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index 65b32075..e9483e89 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -20,7 +20,7 @@ class DayCategory(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.day_categories + endpoint=cls._capabilities.features.day_categories .day_categories.href, dto_list=DayCategoryResponse, dto_retrieve=DayCategoryResponse, diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index a20a5f4d..cf2e9b66 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -24,7 +24,7 @@ class Division(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.divisions.divisions.href, + endpoint=cls._capabilities.features.divisions.divisions.href, dto_list=DivisionDetailResponse, dto_retrieve=DivisionDetail, ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index c9770a6c..96ca234f 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -25,7 +25,7 @@ class ItemsTypes(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.items.item_types.href, + endpoint=cls._capabilities.features.items.item_types.href, dto_list=ItemTypesResponse, dto_retrieve=ItemTypesResponse, ) @@ -42,7 +42,7 @@ class Item(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.items.items.href, + endpoint=cls._capabilities.features.items.items.href, dto_list=ItemsSummaryResponse, dto_retrieve=ItemDetail, ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 40d35c89..1a430079 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -19,6 +19,6 @@ class Schedule(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.schedules.schedules.href, + endpoint=cls._capabilities.features.schedules.schedules.href, dto_list=ScheduleSummaryResponse, ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 9669b08b..f69e02b6 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -22,8 +22,7 @@ class CardType(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._discover.features.card_types - .card_types.href, + endpoint=cls._capabilities.features.card_types.card_types.href, dto_list=CardTypeResponse, dto_retrieve=CardTypeResponse, ) From 50854154804a57a2cbc2186385b0b6b0cf90cb0c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 14:47:49 +1100 Subject: [PATCH 032/118] fix: endpoint configuration --- gallagher/cc/alarms/items.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 96ca234f..cf785514 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -3,8 +3,6 @@ """ -from gallagher.cc import CAPABILITIES - from ..core import ( APIEndpoint, EndpointConfig, From e1fdc1bc480640bca4f155f246870c8c1a0a3969 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 3 Dec 2023 16:00:17 +1100 Subject: [PATCH 033/118] feat: adds cardholder list and detail endpoints --- TODO.md | 4 +-- gallagher/cc/cardholders/cardholders.py | 29 +++++++++++++++++ gallagher/cc/core.py | 10 ++++-- gallagher/dto/__init__.py | 13 ++++++-- gallagher/dto/cardholder.py | 43 ++++++++++++++++++++----- tests/test_cardholder.py | 31 +++++++++++++++++- 6 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 gallagher/cc/cardholders/cardholders.py diff --git a/TODO.md b/TODO.md index 17649f92..d778b819 100644 --- a/TODO.md +++ b/TODO.md @@ -31,8 +31,8 @@ The following is tracking which endpoints are available along with their test ca ## Cardholders and related items - [ ] Cardholder search -- [ ] Cardholder summary -- [ ] Cardholder detail +- [x] Cardholder summary +- [x] Cardholder detail - [ ] Cardholder POST example - [ ] Cardholder Update Location POST example - [ ] Cardholder PATCH example diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py new file mode 100644 index 00000000..120d3ff5 --- /dev/null +++ b/gallagher/cc/cardholders/cardholders.py @@ -0,0 +1,29 @@ +""" + +""" +from ..core import ( + APIEndpoint, + EndpointConfig +) + +from ...dto.cardholder import ( + CardholderSummaryResponse, + CardholderDetail, +) + + +class Cardholder(APIEndpoint): + """ Cardholder endpoints allow you to search for and retrieve cardholder details. + + Cardholders are the users of the system and are the entities that are + granted access to doors. Cardholders can be people, vehicles, or other + entities that require access to the site. + """ + + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=cls._capabilities.features.cardholders.cardholders.href, + dto_list=CardholderSummaryResponse, + dto_retrieve=CardholderDetail, + ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 252d2f69..3e565fe2 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -12,6 +12,7 @@ the bootstrapping process. """ from typing import Optional +from datetime import datetime from dataclasses import dataclass import httpx @@ -23,7 +24,6 @@ from ..dto.discover import ( DiscoveryResponse, FeaturesDetail, - FeatureAlarms, ) @@ -159,6 +159,13 @@ def _discover(cls): This differs per endpoint that we work with. """ + if cls._capabilities.version != "0.0.0" and\ + type(cls._capabilities.good_known_since) is datetime: + # We've already discovered the endpoint hence + # we can stop execution to improve performance + # and avoid network round trips. + return + # Auto-discovery of the API endpoints, this will # be called as part of the bootstrapping process from . import api_base @@ -209,7 +216,6 @@ def retrieve(cls, id): """ cls._discover() - from . import api_base response = httpx.get( f'{cls.__config__.endpoint}/{id}', headers=get_authorization_headers(), diff --git a/gallagher/dto/__init__.py b/gallagher/dto/__init__.py index 1a680b80..7e2a207e 100644 --- a/gallagher/dto/__init__.py +++ b/gallagher/dto/__init__.py @@ -1,7 +1,14 @@ -""" +""" Data Transfer Object + +This package defines the data transfer objects (DTOs) to parse +the data from the Gallagher API into Python objects. + +We use pyndatic to define the DTOs. pydantic is able to validate +the data types and values of the DTOs. - Resources: +Our ultimate aim is define rules as outlined by their documentation. + +Resources: - https://bit.ly/3UkhQhS """ - diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index 4f72c2c4..b15763e5 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -1,12 +1,17 @@ """ """ - +from typing import Optional from datetime import datetime from .utils import ( AppBaseModel, IdentityMixin, + HrefMixin, +) + +from .division import ( + DivisionRef, ) @@ -18,21 +23,43 @@ class CardholderSummary( The cardholder search at /api/cardholders returns an array of these. It is a subset of what you get from a cardholder's detail page at /api/cardholders/{id} - + (linked as the href in this object), to be more suitable for large result sets. """ - + first_name: str last_name: str - short_name: str - description: str - authorised: str + short_name: Optional[str] = None + description: Optional[str] = None + authorised: bool class CardholderDetail( CardholderSummary ): """ - + + """ + disable_cipher_pad: bool = False + division: DivisionRef + edit: HrefMixin + + operator_login_enabled: bool = False + operator_password_expired: bool = False + + update_location: HrefMixin + updates: HrefMixin + + user_extended_access_time: bool = False + windows_login_enabled: bool = False + + +class CardholderSummaryResponse( + AppBaseModel +): + """ Summary response for cardholder list and search + + /api/cardholders is generally the endpoint that + """ - last_successful_access_time: datetime + results: list[CardholderSummary] diff --git a/tests/test_cardholder.py b/tests/test_cardholder.py index 2eb2788d..29f112d9 100644 --- a/tests/test_cardholder.py +++ b/tests/test_cardholder.py @@ -1,3 +1,32 @@ """ -""" \ No newline at end of file +""" + + +def test_cardholder_list(): + + from gallagher.cc.cardholders.cardholders import Cardholder + from gallagher.dto.cardholder import CardholderSummaryResponse + + response = Cardholder.list() + assert type(response) is CardholderSummaryResponse + assert type(response.results) is list + assert len(response.results) > 0 + + +def test_cardholder_detail(): + + from gallagher.cc.cardholders.cardholders import Cardholder + from gallagher.dto.cardholder import ( + CardholderSummaryResponse, + CardholderDetail, + ) + + response = Cardholder.list() + assert type(response) is CardholderSummaryResponse + + for cardholder_summary in response.results: + # Get the detail of the cardholder for comparison + cardholder_detail_response = Cardholder.retrieve(cardholder_summary.id) + assert type(cardholder_detail_response) is CardholderDetail + assert (cardholder_detail_response.id == cardholder_summary.id) From 66e99c4a5b7baa81ab372d47da3d8b921c8cfad8 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 4 Dec 2023 08:43:47 +1100 Subject: [PATCH 034/118] refactor: singleton pattern on the baseclass --- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/items.py | 4 ++-- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/card_type.py | 2 +- gallagher/cc/cardholders/cardholders.py | 2 +- gallagher/cc/core.py | 9 ++++++--- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 64f1e20e..0c72695b 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -23,7 +23,7 @@ class Alarms( @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.alarms.alarms.href, + endpoint=APIEndpoint._capabilities.features.alarms.alarms.href, dto_list=AlarmResponse, dto_retrieve=AlarmZoneSummary, ) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index e9483e89..069bc373 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -20,7 +20,7 @@ class DayCategory(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.day_categories + endpoint=APIEndpoint._capabilities.features.day_categories .day_categories.href, dto_list=DayCategoryResponse, dto_retrieve=DayCategoryResponse, diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index cf2e9b66..05ccc1eb 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -24,7 +24,7 @@ class Division(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.divisions.divisions.href, + endpoint=APIEndpoint._capabilities.features.divisions.divisions.href, dto_list=DivisionDetailResponse, dto_retrieve=DivisionDetail, ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index cf785514..f2b84173 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -23,7 +23,7 @@ class ItemsTypes(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.items.item_types.href, + endpoint=APIEndpoint._capabilities.features.items.item_types.href, dto_list=ItemTypesResponse, dto_retrieve=ItemTypesResponse, ) @@ -40,7 +40,7 @@ class Item(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.items.items.href, + endpoint=APIEndpoint._capabilities.features.items.items.href, dto_list=ItemsSummaryResponse, dto_retrieve=ItemDetail, ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 1a430079..3eb733ca 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -19,6 +19,6 @@ class Schedule(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.schedules.schedules.href, + endpoint=APIEndpoint._capabilities.features.schedules.schedules.href, dto_list=ScheduleSummaryResponse, ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index f69e02b6..6ab424cf 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -22,7 +22,7 @@ class CardType(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.card_types.card_types.href, + endpoint=APIEndpoint._capabilities.features.card_types.card_types.href, dto_list=CardTypeResponse, dto_retrieve=CardTypeResponse, ) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 120d3ff5..d44d2ce9 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -23,7 +23,7 @@ class Cardholder(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=cls._capabilities.features.cardholders.cardholders.href, + endpoint=APIEndpoint._capabilities.features.cardholders.cardholders.href, dto_list=CardholderSummaryResponse, dto_retrieve=CardholderDetail, ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 3e565fe2..133323a3 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -157,10 +157,13 @@ def _discover(cls): updates to the changes to events. This differs per endpoint that we work with. + + Note that references to APIEndpoint._capabilities as a singleton, while + cls.method when executing a class method. """ - if cls._capabilities.version != "0.0.0" and\ - type(cls._capabilities.good_known_since) is datetime: + if APIEndpoint._capabilities.version != "0.0.0" and\ + type(APIEndpoint._capabilities.good_known_since) is datetime: # We've already discovered the endpoint hence # we can stop execution to improve performance # and avoid network round trips. @@ -180,7 +183,7 @@ def _discover(cls): # Assign the capabilities to the class, this should # result in the endpoint - cls._capabilities = parsed_obj + APIEndpoint._capabilities = parsed_obj # Set this so the configuration is only discovered # once per endpoint From 6dd7d72b028bdfe8d57fa79e1e7350779d064ecd Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 6 Dec 2023 10:19:46 +1100 Subject: [PATCH 035/118] refactor: moves capabilities singleton out of base class i wasn't quite happy with the idea that the singleton was part of the base class that all the endpoints inherited from, this is now moved into a class called Capabilties which has a class level variable called CURRENT to represent the current capabilities of the server we are connected to, this reads a lot better than before REFS #5 we should also explore if it's possible to move back to using the __config__ constants being assigned by the endpoint classes as opposed to the method returning the configuration there may be lifecycle issues here --- gallagher/cc/alarms/__init__.py | 5 ++-- gallagher/cc/alarms/day_category.py | 3 ++- gallagher/cc/alarms/divisions.py | 3 ++- gallagher/cc/alarms/items.py | 5 ++-- gallagher/cc/alarms/schedule.py | 3 ++- gallagher/cc/cardholders/card_type.py | 3 ++- gallagher/cc/cardholders/cardholders.py | 3 ++- gallagher/cc/core.py | 33 ++++++++++++++----------- 8 files changed, 34 insertions(+), 24 deletions(-) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 0c72695b..cbb931d3 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -5,7 +5,8 @@ from ..core import ( APIEndpoint, - EndpointConfig + EndpointConfig, + Capabilities ) from ...dto.alarm import ( @@ -23,7 +24,7 @@ class Alarms( @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.alarms.alarms.href, + endpoint=Capabilities.CURRENT.features.alarms.alarms.href, dto_list=AlarmResponse, dto_retrieve=AlarmZoneSummary, ) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index 069bc373..83d9131f 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -4,6 +4,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) @@ -20,7 +21,7 @@ class DayCategory(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.day_categories + endpoint=Capabilities.CURRENT.features.day_categories .day_categories.href, dto_list=DayCategoryResponse, dto_retrieve=DayCategoryResponse, diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 05ccc1eb..264f7a82 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -3,6 +3,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) @@ -24,7 +25,7 @@ class Division(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.divisions.divisions.href, + endpoint=Capabilities.CURRENT.features.divisions.divisions.href, dto_list=DivisionDetailResponse, dto_retrieve=DivisionDetail, ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index f2b84173..7324c4cc 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -4,6 +4,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig, ) @@ -23,7 +24,7 @@ class ItemsTypes(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.items.item_types.href, + endpoint=Capabilities.CURRENT.features.items.item_types.href, dto_list=ItemTypesResponse, dto_retrieve=ItemTypesResponse, ) @@ -40,7 +41,7 @@ class Item(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.items.items.href, + endpoint=Capabilities.CURRENT.features.items.items.href, dto_list=ItemsSummaryResponse, dto_retrieve=ItemDetail, ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 3eb733ca..e8c4b5df 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -3,6 +3,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) @@ -19,6 +20,6 @@ class Schedule(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.schedules.schedules.href, + endpoint=Capabilities.CURRENT.features.schedules.schedules.href, dto_list=ScheduleSummaryResponse, ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 6ab424cf..18029d3c 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -2,6 +2,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) @@ -22,7 +23,7 @@ class CardType(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.card_types.card_types.href, + endpoint=Capabilities.CURRENT.features.card_types.card_types.href, dto_list=CardTypeResponse, dto_retrieve=CardTypeResponse, ) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index d44d2ce9..f06fb599 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -2,6 +2,7 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) @@ -23,7 +24,7 @@ class Cardholder(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=APIEndpoint._capabilities.features.cardholders.cardholders.href, + endpoint=Capabilities.CURRENT.features.cardholders.cardholders.href, dto_list=CardholderSummaryResponse, dto_retrieve=CardholderDetail, ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 133323a3..9bafc593 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -100,16 +100,7 @@ def validate_endpoint(cls): ) -class APIEndpoint(): - """ Base class for all API objects - - All API endpoints must inherit from this class and provide a Config class - that automates the implementation of many of the API methods. - - If the endpoints provide additional methods then they are to implement them - based on the same standards as this base class. - - """ +class Capabilities(): # Discover response object, each endpoint will reference # one of the instance variable Href property to get the @@ -123,11 +114,23 @@ class APIEndpoint(): # to access the endpoint then the library will throw an exception # # This value is memoized and should perform - _capabilities = DiscoveryResponse( + CURRENT = DiscoveryResponse( version="0.0.0", # Indicates that it's not been discovered features=FeaturesDetail() ) + +class APIEndpoint(): + """ Base class for all API objects + + All API endpoints must inherit from this class and provide a Config class + that automates the implementation of many of the API methods. + + If the endpoints provide additional methods then they are to implement them + based on the same standards as this base class. + + """ + # Do not set this variable in your class, this is set by the # lifecycle methods and use to cache the configuration object __config__ = None @@ -158,12 +161,12 @@ def _discover(cls): This differs per endpoint that we work with. - Note that references to APIEndpoint._capabilities as a singleton, while + Note that references to Capabilities.CURRENT as a singleton, while cls.method when executing a class method. """ - if APIEndpoint._capabilities.version != "0.0.0" and\ - type(APIEndpoint._capabilities.good_known_since) is datetime: + if Capabilities.CURRENT.version != "0.0.0" and\ + type(Capabilities.CURRENT.good_known_since) is datetime: # We've already discovered the endpoint hence # we can stop execution to improve performance # and avoid network round trips. @@ -183,7 +186,7 @@ def _discover(cls): # Assign the capabilities to the class, this should # result in the endpoint - APIEndpoint._capabilities = parsed_obj + Capabilities.CURRENT = parsed_obj # Set this so the configuration is only discovered # once per endpoint From b5105085e9d945645e48031f0f23f4e49545f9d4 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 6 Dec 2023 12:20:51 +1100 Subject: [PATCH 036/118] refactor: configuration syntax of discover and endpoints the discover endpoints drop the .href portion of the configuration this is infered by the common runtime code, making it uncessary for each endpoint to explicitly reference the href propeorty this also leads us to handing #8 properly as the core runtime can check for None type of the href property an OptionalHref type replaces the use of the Mixin for the discover endpoints additionally these are initialised to None and are overridden if the feature is avaialble on the command centre also see commentary in APIEndpoint._discover as to why we are sticking to initialising the configuration as the result of a function not an assignment to a class level variable REFS #5 --- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/items.py | 4 +- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/cardholders.py | 2 +- gallagher/cc/core.py | 19 +++++-- gallagher/dto/discover.py | 72 ++++++++++++------------- gallagher/dto/utils.py | 22 ++++++++ 9 files changed, 80 insertions(+), 47 deletions(-) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index cbb931d3..64af6fd6 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -24,7 +24,7 @@ class Alarms( @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.alarms.alarms.href, + endpoint=Capabilities.CURRENT.features.alarms.alarms, dto_list=AlarmResponse, dto_retrieve=AlarmZoneSummary, ) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index 83d9131f..f908665e 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -22,7 +22,7 @@ class DayCategory(APIEndpoint): def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.day_categories - .day_categories.href, + .day_categories, dto_list=DayCategoryResponse, dto_retrieve=DayCategoryResponse, ) diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 264f7a82..bb9990ae 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -25,7 +25,7 @@ class Division(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.divisions.divisions.href, + endpoint=Capabilities.CURRENT.features.divisions.divisions, dto_list=DivisionDetailResponse, dto_retrieve=DivisionDetail, ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 7324c4cc..8d06d1fb 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -24,7 +24,7 @@ class ItemsTypes(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.items.item_types.href, + endpoint=Capabilities.CURRENT.features.items.item_types, dto_list=ItemTypesResponse, dto_retrieve=ItemTypesResponse, ) @@ -41,7 +41,7 @@ class Item(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.items.items.href, + endpoint=Capabilities.CURRENT.features.items.items, dto_list=ItemsSummaryResponse, dto_retrieve=ItemDetail, ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index e8c4b5df..38095ef7 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -20,6 +20,6 @@ class Schedule(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.schedules.schedules.href, + endpoint=Capabilities.CURRENT.features.schedules.schedules, dto_list=ScheduleSummaryResponse, ) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index f06fb599..4a686ab0 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -24,7 +24,7 @@ class Cardholder(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.cardholders.cardholders.href, + endpoint=Capabilities.CURRENT.features.cardholders.cardholders, dto_list=CardholderSummaryResponse, dto_retrieve=CardholderDetail, ) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 9bafc593..f8b83b0b 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -100,7 +100,7 @@ def validate_endpoint(cls): ) -class Capabilities(): +class Capabilities: # Discover response object, each endpoint will reference # one of the instance variable Href property to get the @@ -120,7 +120,7 @@ class Capabilities(): ) -class APIEndpoint(): +class APIEndpoint: """ Base class for all API objects All API endpoints must inherit from this class and provide a Config class @@ -186,10 +186,21 @@ def _discover(cls): # Assign the capabilities to the class, this should # result in the endpoint + # + # With the refactored initialisation of the pydantic + # models, the values for the unavailable endpoints + # should be set to None Capabilities.CURRENT = parsed_obj # Set this so the configuration is only discovered # once per endpoint + # + # If we assign the __config__ variable in the class + # that inherits from this class, the instance of EndpointConfig + # will copy the None values from the Capabilities.CURRENT + # object, this primarily because Capabilities.CURRENT is + # an instance of a pyndatic object and all values are thus + # copied not referenced. cls.__config__ = cls.get_config() @classmethod @@ -202,7 +213,7 @@ def list(cls, skip=0): cls._discover() response = httpx.get( - f'{cls.__config__.endpoint}', + f'{cls.__config__.endpoint.href}', headers=get_authorization_headers(), ) @@ -223,7 +234,7 @@ def retrieve(cls, id): cls._discover() response = httpx.get( - f'{cls.__config__.endpoint}/{id}', + f'{cls.__config__.endpoint.href}/{id}', headers=get_authorization_headers(), ) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index b3f71bbb..654f4224 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -11,168 +11,168 @@ from .utils import ( AppBaseModel, - HrefMixin + OptionalHref, ) class FeatureAccessGroups( AppBaseModel, ): - access_groups: Optional[HrefMixin] = None + access_groups: Optional[OptionalHref] = OptionalHref() class FeatureAccessZones( AppBaseModel, ): - access_zones: Optional[HrefMixin] = None + access_zones: Optional[OptionalHref] = OptionalHref() class FeatureAlarmZones( AppBaseModel, ): - alarm_zones: Optional[HrefMixin] = None + alarm_zones: Optional[OptionalHref] = OptionalHref() class FeatureAlarms( AppBaseModel, ): - alarms: Optional[HrefMixin] = None - divisions: Optional[HrefMixin] = None - updates: Optional[HrefMixin] = None + alarms: Optional[OptionalHref] = OptionalHref() + divisions: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() class FeatureCardTypes( AppBaseModel, ): - assign: Optional[HrefMixin] = None - card_types: Optional[HrefMixin] = None + assign: Optional[OptionalHref] = OptionalHref() + card_types: Optional[OptionalHref] = OptionalHref() class FeatureCardholders( AppBaseModel, ): - cardholders: Optional[HrefMixin] = None - changes: Optional[HrefMixin] = None - update_location_access_zones: Optional[HrefMixin] = None + cardholders: Optional[OptionalHref] = OptionalHref() + changes: Optional[OptionalHref] = OptionalHref() + update_location_access_zones: Optional[OptionalHref] = OptionalHref() class FeatureCompetencies( AppBaseModel, ): - competencies: Optional[HrefMixin] = None + competencies: Optional[OptionalHref] = OptionalHref() class FeatureDayCategories( AppBaseModel, ): - day_categories: Optional[HrefMixin] = None + day_categories: Optional[OptionalHref] = OptionalHref() class FeatureDivisions( AppBaseModel, ): - divisions: Optional[HrefMixin] = None + divisions: Optional[OptionalHref] = OptionalHref() class FeatureDoors( AppBaseModel, ): - doors: Optional[HrefMixin] = None + doors: Optional[OptionalHref] = OptionalHref() class FeatureElevators( AppBaseModel, ): - elevator_groups: Optional[HrefMixin] = None + elevator_groups: Optional[OptionalHref] = OptionalHref() class FeatureEvents( AppBaseModel, ): - divisions: Optional[HrefMixin] = None - event_groups: Optional[HrefMixin] = None - events: Optional[HrefMixin] = None - updates: Optional[HrefMixin] = None + divisions: Optional[OptionalHref] = OptionalHref() + event_groups: Optional[OptionalHref] = OptionalHref() + events: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() class FeatureFenceZones( AppBaseModel, ): - fence_zones: Optional[HrefMixin] = None + fence_zones: Optional[OptionalHref] = OptionalHref() class FeatureInputs( AppBaseModel, ): - inputs: Optional[HrefMixin] = None + inputs: Optional[OptionalHref] = OptionalHref() class FeatureInterlockGroups( AppBaseModel, ): - interlock_groups: Optional[HrefMixin] = None + interlock_groups: Optional[OptionalHref] = OptionalHref() class FeatureItems( AppBaseModel, ): - item_types: Optional[HrefMixin] = None - items: Optional[HrefMixin] = None - updates: Optional[HrefMixin] = None + item_types: Optional[OptionalHref] = OptionalHref() + items: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() class FeatureLockerBanks( AppBaseModel, ): - locker_banks: Optional[HrefMixin] = None + locker_banks: Optional[OptionalHref] = OptionalHref() class FeatureMacros( AppBaseModel, ): - macros: Optional[HrefMixin] = None + macros: Optional[OptionalHref] = OptionalHref() class FeatureOperatorGroups( AppBaseModel, ): - operator_groups: Optional[HrefMixin] = None + operator_groups: Optional[OptionalHref] = OptionalHref() class FeatureOutputs( AppBaseModel, ): - outputs: Optional[HrefMixin] = None + outputs: Optional[OptionalHref] = OptionalHref() class FeaturePersonalDataFields( AppBaseModel, ): - personal_data_fields: Optional[HrefMixin] = None + personal_data_fields: Optional[OptionalHref] = OptionalHref() class FeatureReceptions( AppBaseModel, ): - receptions: Optional[HrefMixin] = None + receptions: Optional[OptionalHref] = OptionalHref() class FeatureRoles( AppBaseModel, ): - roles: Optional[HrefMixin] = None + roles: Optional[OptionalHref] = OptionalHref() class FeatureSchedules( AppBaseModel, ): - schedules: Optional[HrefMixin] = None + schedules: Optional[OptionalHref] = OptionalHref() class FeatureVisits( AppBaseModel, ): - visits: Optional[HrefMixin] = None + visits: Optional[OptionalHref] = OptionalHref() class FeaturesDetail( diff --git a/gallagher/dto/utils.py b/gallagher/dto/utils.py index be162a15..f0806010 100644 --- a/gallagher/dto/utils.py +++ b/gallagher/dto/utils.py @@ -65,3 +65,25 @@ class HrefMixin(BaseModel): responses from the Gallagher API. """ href: str + + +class OptionalHref(BaseModel): + """ Optionally available Href + + This mixin is used to define the href field for all + responses from the Gallagher API. + + Primarily used by the discovery endpoint, where the href + may be absent if the feature is not available. + + Reason for this so the API Endpoint configuration can + reference the href property (pre discovery), otherwise + the Feature* classes have a None object for the object + + # Use with caution + + Only use these with responses that don't optionally + require a href. See Gallagher's documentation for + confirmation. + """ + href: Optional[str] = None From 263ba0b31fd872e5d4723eb3a11328720effb683 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 6 Dec 2023 12:24:11 +1100 Subject: [PATCH 037/118] chore: port issues template for github --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..dd84ea78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..bbcbbe7d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From b6568c91939dbe4636d87674395f2269c5c316e2 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 6 Dec 2023 13:02:57 +1100 Subject: [PATCH 038/118] feat: configure typer and cli endpoint --- gallagher/cli/__init__.py | 9 +++++++++ poetry.lock | 39 +++++++++++++++++++++++++++++++++++++-- pyproject.toml | 5 ++++- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 gallagher/cli/__init__.py diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py new file mode 100644 index 00000000..7196294e --- /dev/null +++ b/gallagher/cli/__init__.py @@ -0,0 +1,9 @@ +import typer + + +def main(): + typer.echo("Hello World") + + +if __name__ == "__main__": + main() diff --git a/poetry.lock b/poetry.lock index d582fa5e..791487d5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -42,6 +42,20 @@ files = [ {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -420,6 +434,27 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + [[package]] name = "typing-extensions" version = "4.8.0" @@ -434,4 +469,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "679bf50da52088350442b14a6073a6aea9854ffcfc37875a1c1ff057fae4acd1" +content-hash = "77be7f7a46597cc2503dc221538a03c11bb75fe39690a7cfcba6f5dc9114e0c5" diff --git a/pyproject.toml b/pyproject.toml index c6ad2e94..eb57eb4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ certifi = "^2023.11.17" idna = "^3.6" packaging = "^23.2" pluggy = "^1.3.0" - +typer = "^0.9.0" [tool.poetry.group.dev.dependencies] pytest = "^7.2.2" @@ -38,6 +38,9 @@ coverage = "^7.2.1" pytest-order = "^1.0.1" pytest-cov = "^4.1.0" +[tool.poetry.scripts] +gallagher = "gallagher.cli" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From a3dde5f39c65e387b529632cc2f2a2e76a828778 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 6 Dec 2023 13:10:30 +1100 Subject: [PATCH 039/118] fix: href reference post design change --- gallagher/cc/cardholders/card_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 18029d3c..722dded0 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -23,7 +23,7 @@ class CardType(APIEndpoint): @classmethod def get_config(cls): return EndpointConfig( - endpoint=Capabilities.CURRENT.features.card_types.card_types.href, + endpoint=Capabilities.CURRENT.features.card_types.card_types, dto_list=CardTypeResponse, dto_retrieve=CardTypeResponse, ) From 573e5c49719fc3f25c2f65cf14acda6997bab191 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 7 Dec 2023 09:21:30 +1100 Subject: [PATCH 040/118] feat: basic layout of the cli basic layout of the cli application using typer and subcommands REFS #13 --- gallagher/cli/__init__.py | 27 +++++++++++++++++++++++++-- gallagher/cli/alarms.py | 8 ++++++++ gallagher/cli/cardholders.py | 6 ++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 gallagher/cli/alarms.py create mode 100644 gallagher/cli/cardholders.py diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py index 7196294e..269bb551 100644 --- a/gallagher/cli/__init__.py +++ b/gallagher/cli/__init__.py @@ -1,9 +1,32 @@ +""" + + +""" import typer +from gallagher import __version__ + +from .alarms import app as alarms_app +from .cardholders import app as cardholders_app + +# Main Typer app use to create the CLI +app = typer.Typer() +app.add_typer(alarms_app, name="alarms") +app.add_typer(cardholders_app, name="ch") -def main(): + +@app.command() +def echo(name: str): + """ + + """ typer.echo("Hello World") if __name__ == "__main__": - main() + """ In case you are invoking this via Python directly + + This is probably never actually used but it is here for completeness. + You'd execute this by running `python -m gallagher.cli` + """ + app() diff --git a/gallagher/cli/alarms.py b/gallagher/cli/alarms.py new file mode 100644 index 00000000..8535e73b --- /dev/null +++ b/gallagher/cli/alarms.py @@ -0,0 +1,8 @@ +""" Alarms + + +""" + +import typer + +app = typer.Typer() diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py new file mode 100644 index 00000000..828d6073 --- /dev/null +++ b/gallagher/cli/cardholders.py @@ -0,0 +1,6 @@ +""" Cardholder cli commands mounted at ch + +""" +import typer + +app = typer.Typer() From fa3290752e885982f5bc8b887eabc88baaf62455 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 7 Dec 2023 09:21:41 +1100 Subject: [PATCH 041/118] chore: adds rich to format cli --- poetry.lock | 70 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 3 ++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 791487d5..4d7010c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -209,6 +209,41 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "packaging" version = "23.2" @@ -371,6 +406,21 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pytest" version = "7.4.3" @@ -423,6 +473,24 @@ files = [ [package.dependencies] pytest = {version = ">=6.2.4", markers = "python_version >= \"3.10\""} +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "sniffio" version = "1.3.0" @@ -469,4 +537,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "77be7f7a46597cc2503dc221538a03c11bb75fe39690a7cfcba6f5dc9114e0c5" +content-hash = "4f9db9da40a4675fc883c9aef34358c7cb377ec808d473607c409edb261694b3" diff --git a/pyproject.toml b/pyproject.toml index eb57eb4a..8f89c364 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ idna = "^3.6" packaging = "^23.2" pluggy = "^1.3.0" typer = "^0.9.0" +rich = "^13.7.0" [tool.poetry.group.dev.dependencies] pytest = "^7.2.2" @@ -39,7 +40,7 @@ pytest-order = "^1.0.1" pytest-cov = "^4.1.0" [tool.poetry.scripts] -gallagher = "gallagher.cli" +gal = "gallagher.cli:app" [build-system] requires = ["poetry-core"] From a9e82b996e04eae4420a4b0ecef0ab68f48872e4 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 7 Dec 2023 09:57:25 +1100 Subject: [PATCH 042/118] feat: basic prototype of cli working subcommands have been setup in accordance to the documentation with typer routing the cardholder commands and then fetching the data via the client rich is rendering tables to the terminal REFS #13 --- gallagher/cli/__init__.py | 8 -------- gallagher/cli/cardholders.py | 36 +++++++++++++++++++++++++++++++++++- gallagher/dto/cardholder.py | 25 +++++++++++++++++++++++++ tests/__init__.py | 3 ++- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py index 269bb551..ddcd0b2e 100644 --- a/gallagher/cli/__init__.py +++ b/gallagher/cli/__init__.py @@ -15,14 +15,6 @@ app.add_typer(cardholders_app, name="ch") -@app.command() -def echo(name: str): - """ - - """ - typer.echo("Hello World") - - if __name__ == "__main__": """ In case you are invoking this via Python directly diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index 828d6073..d12e28e2 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -2,5 +2,39 @@ """ import typer +from rich.console import Console +from rich.table import Table -app = typer.Typer() +from gallagher.cc.cardholders.cardholders import Cardholder + +app = typer.Typer(help="query or manage cardholders") + + +@app.command("summary") +def summary(): + """ list all cardholders + """ + import os + api_key = os.environ.get("GACC_API_KEY") + + from gallagher import cc + cc.api_key = api_key + + cardholders = Cardholder.list() + + table = Table(title="Cardholders") + for header in cardholders.cli_header: + table.add_column(header) + + for row in cardholders.cli_repr: + table.add_row(*row) + + console = Console() + console.print(table) + + +@app.command("get") +def get(id: int): + """ get a cardholder by id + """ + typer.echo(f"Getting cardholder {id}") diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index b15763e5..2b99a645 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -33,6 +33,16 @@ class CardholderSummary( description: Optional[str] = None authorised: bool + def cli_repr(self): + return [ + self.first_name, + self.last_name, + "yes" if self.authorised else "no" + ] + + def __str__(self): + return f"{self.id} {self.first_name} {self.last_name}" + class CardholderDetail( CardholderSummary @@ -63,3 +73,18 @@ class CardholderSummaryResponse( """ results: list[CardholderSummary] + + @property + def cli_header(self): + return [ + "First name", + "Last name", + "Authorised" + ] + + @property + def cli_repr(self): + return [x.cli_repr() for x in self.results] + + def __str__(self): + return f"{len(self.results)} cardholders" diff --git a/tests/__init__.py b/tests/__init__.py index 62bb40b8..dc5d6ae6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,6 +5,7 @@ """ + def setup_module(module): """ The Gallagher API client requires a test key, this is set in the environment variable GACC_API_KEY. @@ -14,7 +15,7 @@ def setup_module(module): """ import os api_key = os.environ.get("GACC_API_KEY") - + from gallagher import cc cc.api_key = api_key From 30ae2a5bef4c7836d3da13b3d31ef697c71c1204 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 8 Dec 2023 09:52:06 +1100 Subject: [PATCH 043/118] refactor: moves api init out of cli handler moves assigning the environment variable to the top of the package this should still check if the environment variable is available before cli endpoints are executed REFS #13 --- gallagher/cli/alarms.py | 6 ++++++ gallagher/cli/cardholders.py | 10 +++------- gallagher/dto/cardholder.py | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/gallagher/cli/alarms.py b/gallagher/cli/alarms.py index 8535e73b..74d2edce 100644 --- a/gallagher/cli/alarms.py +++ b/gallagher/cli/alarms.py @@ -3,6 +3,12 @@ """ +from gallagher import cc +import os import typer +api_key = os.environ.get("GACC_API_KEY") + +cc.api_key = api_key + app = typer.Typer() diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index d12e28e2..ac28baaf 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -2,6 +2,7 @@ """ import typer +from rich import print as rprint from rich.console import Console from rich.table import Table @@ -14,12 +15,6 @@ def summary(): """ list all cardholders """ - import os - api_key = os.environ.get("GACC_API_KEY") - - from gallagher import cc - cc.api_key = api_key - cardholders = Cardholder.list() table = Table(title="Cardholders") @@ -37,4 +32,5 @@ def summary(): def get(id: int): """ get a cardholder by id """ - typer.echo(f"Getting cardholder {id}") + cardholder = Cardholder.retrieve(id) + rprint(cardholder.__dict__) diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index 2b99a645..e94624b7 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -35,6 +35,7 @@ class CardholderSummary( def cli_repr(self): return [ + self.id, self.first_name, self.last_name, "yes" if self.authorised else "no" @@ -77,6 +78,7 @@ class CardholderSummaryResponse( @property def cli_header(self): return [ + "Id", "First name", "Last name", "Authorised" From ea76d5484536ba47d0b7e708c9484909e12b813a Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 11 Dec 2023 10:02:55 +1100 Subject: [PATCH 044/118] chore: update packages --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4d7010c7..e466bd21 100644 --- a/poetry.lock +++ b/poetry.lock @@ -525,16 +525,16 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "4f9db9da40a4675fc883c9aef34358c7cb377ec808d473607c409edb261694b3" +content-hash = "b306cf2dff8f61532978a697b696f78f80317e46f5cbd385c1913afed607b691" diff --git a/pyproject.toml b/pyproject.toml index 8f89c364..7baa44ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ python = "^3.11" httpx = "^0.25.2" pydantic = "^2.5.2" -typing-extensions = "^4.8.0" +typing-extensions = "^4.9.0" coverage = "^7.3.2" pytest = "^7.4.3" pytest-order = "^1.2.0" From 60ca052d914b79a6c471b874e208f67e39ba7522 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 11 Dec 2023 10:43:02 +1100 Subject: [PATCH 045/118] feat: sample detail feature for cardholders a proposed detail feature of the cli, this establishes a pattern that we can use to build out the other parts of the cli REFS #13 suggest that we merge this back into the dto-implementation branch post this commit to continue development as a whole --- gallagher/cli/cardholders.py | 26 +++++++++++++++----------- gallagher/dto/cardholder.py | 22 ++++++++++++++++++---- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index ac28baaf..1f1d4632 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -11,21 +11,25 @@ app = typer.Typer(help="query or manage cardholders") -@app.command("summary") -def summary(): +@app.command("list") +def list(): """ list all cardholders """ - cardholders = Cardholder.list() + console = Console() + with console.status( + "[bold green]Fetching cardholders...", + spinner="clock" + ): + cardholders = Cardholder.list() - table = Table(title="Cardholders") - for header in cardholders.cli_header: - table.add_column(header) + table = Table(title="Cardholders") + for header in cardholders.cli_header: + table.add_column(header) - for row in cardholders.cli_repr: - table.add_row(*row) + for row in cardholders.__rich_repr__(): + table.add_row(*row) - console = Console() - console.print(table) + console.print(table) @app.command("get") @@ -33,4 +37,4 @@ def get(id: int): """ get a cardholder by id """ cardholder = Cardholder.retrieve(id) - rprint(cardholder.__dict__) + [rprint(r) for r in cardholder.__rich_repr__()] diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index e94624b7..0f09a3d6 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -33,7 +33,7 @@ class CardholderSummary( description: Optional[str] = None authorised: bool - def cli_repr(self): + def __rich_repr__(self): return [ self.id, self.first_name, @@ -64,6 +64,21 @@ class CardholderDetail( user_extended_access_time: bool = False windows_login_enabled: bool = False + def __rich_repr__(self): + return [ + f"[blue bold] person", + f"{'id':>20} {self.id}", + f"{'first_name':>20} {self.first_name}", + f"{'last_name':>20} {self.last_name}", + f"{'short_name':>20} {self.short_name}", + f"{'description':>20} {self.description}", + f"{'authorised':>20} {'yes' if self.authorised else 'no'}", + f"", + f"{'disable_cipher_pad':>20} {'yes' if self.disable_cipher_pad else 'no'}", + f"{'division':>20} {self.division.id}", + f"", + ] + class CardholderSummaryResponse( AppBaseModel @@ -84,9 +99,8 @@ def cli_header(self): "Authorised" ] - @property - def cli_repr(self): - return [x.cli_repr() for x in self.results] + def __rich_repr__(self): + return [r.__rich_repr__() for r in self.results] def __str__(self): return f"{len(self.results)} cardholders" From f3cb6abea5622be92bfc181e3b63ba4ca21b4d51 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:30:52 +1100 Subject: [PATCH 046/118] refactor: events endpoint with even type endpoint configuration --- gallagher/cc/alarms/{event.py => events.py} | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) rename gallagher/cc/alarms/{event.py => events.py} (58%) diff --git a/gallagher/cc/alarms/event.py b/gallagher/cc/alarms/events.py similarity index 58% rename from gallagher/cc/alarms/event.py rename to gallagher/cc/alarms/events.py index c3f1cf2c..969e3ae0 100644 --- a/gallagher/cc/alarms/event.py +++ b/gallagher/cc/alarms/events.py @@ -5,10 +5,15 @@ """ from ..core import ( + Capabilities, APIEndpoint, EndpointConfig ) +from ...dto.event import ( + EventTypeResponse +) + class Event( APIEndpoint @@ -24,3 +29,18 @@ def get_config(cls): dto_list=EventResponse, dto_retrieve=EventDetail, ) + + +class EventType( + APIEndpoint +): + """ + + """ + + @classmethod + def get_config(cls): + return EndpointConfig( + endpoint=Capabilities.CURRENT.features.events.event_groups, + dto_list=EventTypeResponse, + ) From 5eb89df05fe528b8bb66500e5c274267eeeefded Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:31:01 +1100 Subject: [PATCH 047/118] feat: starts on search feature --- gallagher/cc/cardholders/cardholders.py | 4 +++ gallagher/cc/core.py | 47 ++++++++++++++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 4a686ab0..0f97fedb 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -28,3 +28,7 @@ def get_config(cls): dto_list=CardholderSummaryResponse, dto_retrieve=CardholderDetail, ) + + @classmethod + def search(cls, name: str, sort: str = "id", top: int = 100): + pass diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index f8b83b0b..f2ea3ce7 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -256,22 +256,53 @@ def create(cls, **params): """ """ - pass + cls._discover() @classmethod def delete(cls): """ """ - pass + cls._discover() @classmethod - def search(cls, **kwargs): - """ Search + def search(cls, + top: int = 100, + sort: str = 'id', + fields: str = 'defaults', + **kwargs + ): + """ Search wrapper for most objects to dynamically search content + + Each object has a set of fields that you can query for, most searches + also allow you to search for a partial string. + + :param int top: Number of results to return + :param str sort: Sort order, can be set to id or -id + :param str fields: List of fields to return + :param kwargs: Fields to search for """ - pass + cls._discover() - @classmethod - def updates(cls): - pass + params = { + 'top': top, + 'sort': sort, + 'fields': fields, + } + + # Adds arbitrary fields to the search, these will be different + # for each type of object that calls the base function + params.update(kwargs) + + response = httpx.get( + f'{cls.__config__.endpoint.href}', + params=params, + headers=get_authorization_headers(), + ) + + parsed_obj = cls.__config__.dto_list.model_validate( + response.json() + ) + + return parsed_obj From 52ed349779e27529de8702cae59f68efc2c1ef2f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:31:16 +1100 Subject: [PATCH 048/118] docs: adds cli output --- gallagher/dto/cardholder.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index 0f09a3d6..53c667f2 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -48,7 +48,11 @@ def __str__(self): class CardholderDetail( CardholderSummary ): - """ + """ Displays a table of cardholders + + Gallagher command centre offers a summary of all the cardholders + provisioned on the system. This command presents a summary table + with the aim of using the identifier to get detailed information. """ disable_cipher_pad: bool = False @@ -76,7 +80,8 @@ def __rich_repr__(self): f"", f"{'disable_cipher_pad':>20} {'yes' if self.disable_cipher_pad else 'no'}", f"{'division':>20} {self.division.id}", - f"", + f"[blue bold] hrefs", + f"{'edit':>20} [link={self.edit.href}]edit[/link]", ] From 7a8db13bcc7a2094a713c21476cb0526b6378a6e Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:31:27 +1100 Subject: [PATCH 049/118] feat: adds dto for event type/group feature --- gallagher/dto/event.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index f13b9723..9f5b547e 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -10,10 +10,12 @@ ) -class EventType( +class EventTypeSummary( AppBaseModel, IdentityMixin, ): + """ An event type has identifiers and names + """ name: str @@ -35,3 +37,28 @@ class EventSummary( next: HrefMixin previous: HrefMixin updates: HrefMixin + + +class EventGroupSummary( + AppBaseModel, + IdentityMixin, +): + """ Event Groups are a collection of event types + + Each group has names and event types. This is usually used + in an Event Type Response. + """ + name: str + event_types: list[EventTypeSummary] + + +class EventTypeResponse( + AppBaseModel, +): + """ Event Type Response + + Event Type Responses return a set of eventGroups which in turn + has identifiers, names and event types. + + """ + event_groups: list[EventGroupSummary] From 6738a44808eb0a7e539d3d8d35b4d85ae2fb367c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:31:44 +1100 Subject: [PATCH 050/118] test: adds test for event types --- tests/test_events.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/test_events.py diff --git a/tests/test_events.py b/tests/test_events.py new file mode 100644 index 00000000..7fdea099 --- /dev/null +++ b/tests/test_events.py @@ -0,0 +1,10 @@ + + +def test_event_types(): + from gallagher.cc.alarms.events import EventType + from gallagher.dto.event import ( + EventTypeResponse, + ) + + response = EventType.list() + assert type(response) is EventTypeResponse From 8efdb591f0220566230c9caf20ae4a3fcea2614f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:31:55 +1100 Subject: [PATCH 051/118] feat: exploration of enums for search ordering --- gallagher/enum.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 gallagher/enum.py diff --git a/gallagher/enum.py b/gallagher/enum.py new file mode 100644 index 00000000..0ebf54c3 --- /dev/null +++ b/gallagher/enum.py @@ -0,0 +1,20 @@ +""" Contains Enumerations for the Gallagher objects + +Our naming convention is ObjectIntent + +CustomerSearch + +""" +from enum import Enum + + +class CustomerSort(Enum): + """ Sort descriptors for the Customer object + + """ + + ID: str = "id" + ID_DESC: str = "-id" + + NAME: str = "name" + NAME_DESC: str = "-name" From c15893c37bb0dd349fee899669387d72b78e7ced Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 15 Dec 2023 16:32:09 +1100 Subject: [PATCH 052/118] fix: adhere to the data definition rules --- gallagher/dto/alarm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gallagher/dto/alarm.py b/gallagher/dto/alarm.py index 87ed43f6..24689c11 100644 --- a/gallagher/dto/alarm.py +++ b/gallagher/dto/alarm.py @@ -10,7 +10,7 @@ IdentityMixin, ) -from .event import EventType +from .event import EventTypeSummary class AlarmSource( @@ -47,7 +47,7 @@ class AlarmZoneSummary( message: str source: AlarmSource type: str - event_type: Optional[EventType] = None + event_type: Optional[EventTypeSummary] = None priority: int state: str active: bool From 5d5131b913b0a50c92af889b025a06f24866cca1 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 17 Dec 2023 16:50:51 +1100 Subject: [PATCH 053/118] refactor: wire up events endpoints --- gallagher/cc/alarms/events.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gallagher/cc/alarms/events.py b/gallagher/cc/alarms/events.py index 969e3ae0..7a5bab56 100644 --- a/gallagher/cc/alarms/events.py +++ b/gallagher/cc/alarms/events.py @@ -11,21 +11,21 @@ ) from ...dto.event import ( - EventTypeResponse + EventTypeResponse, ) class Event( APIEndpoint ): - """ + """ Event """ @classmethod def get_config(cls): return EndpointConfig( - endpoint="events", + endpoint=Capabilities.CURRENT.features.events.events, dto_list=EventResponse, dto_retrieve=EventDetail, ) @@ -34,7 +34,7 @@ def get_config(cls): class EventType( APIEndpoint ): - """ + """ EventType """ From f733ac63042b4542038a4cc5c0fe2a26281a94d0 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 17 Dec 2023 16:51:50 +1100 Subject: [PATCH 054/118] feat: wip wiring up events parser --- gallagher/dto/alarm.py | 9 +++++++++ gallagher/dto/cardholder.py | 9 +++++++++ gallagher/dto/division.py | 2 +- gallagher/dto/door.py | 10 ++++++++++ gallagher/dto/event.py | 38 +++++++++++++++++++++++++++++++++++++ gallagher/dto/items.py | 18 +++++++++--------- 6 files changed, 76 insertions(+), 10 deletions(-) diff --git a/gallagher/dto/alarm.py b/gallagher/dto/alarm.py index 24689c11..1c769881 100644 --- a/gallagher/dto/alarm.py +++ b/gallagher/dto/alarm.py @@ -13,6 +13,15 @@ from .event import EventTypeSummary +class AlarmRef( + AppBaseModel, + HrefMixin +): + """ AlarmRef represents a single alarm + """ + state: str + + class AlarmSource( AppBaseModel, HrefMixin, diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index 53c667f2..62faf164 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -15,6 +15,15 @@ ) +class CardholderRef( + AppBaseModel, + HrefMixin, +): + """ Reference to a Cardholder + """ + name: str + + class CardholderSummary( AppBaseModel, IdentityMixin diff --git a/gallagher/dto/division.py b/gallagher/dto/division.py index ce2aab07..abd703d9 100644 --- a/gallagher/dto/division.py +++ b/gallagher/dto/division.py @@ -45,7 +45,7 @@ class DivisionDetail( class DivisionDetailResponse( AppBaseModel ): - """ + """ Division """ diff --git a/gallagher/dto/door.py b/gallagher/dto/door.py index 8819b224..91edab1c 100644 --- a/gallagher/dto/door.py +++ b/gallagher/dto/door.py @@ -10,6 +10,16 @@ ) +class DoorRef( + AppBaseModel, + HrefMixin +): + """ Door + + """ + name: str + + class DoorSummary( AppBaseModel, IdentityMixin, diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index 9f5b547e..0c5016e2 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -9,6 +9,11 @@ IdentityMixin, ) +from ..dto.alarm import AlarmRef +from ..dto.cardholder import CardholderRef +from ..dto.access_group import AccessGroupRef +from ..dto.door import DoorRef + class EventTypeSummary( AppBaseModel, @@ -52,6 +57,39 @@ class EventGroupSummary( event_types: list[EventTypeSummary] +class EventSummaryResponse( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ + """ + server_display_name: str + time: str + message: Optional[str] + occurrences: int + priority: int + alarm: AlarmRef + + operator: CardholderRef + source: str + group: str + type: str + event_type: EventTypeSummary + division: str + cardholder: str + entry_access_zone: str + exit_access_zone: str + door: DoorRef + access_group: HrefMixin + card: str + modified_item: str + + next: HrefMixin + previous: HrefMixin + updates: HrefMixin + + class EventTypeResponse( AppBaseModel, ): diff --git a/gallagher/dto/items.py b/gallagher/dto/items.py index 8e3b7eb2..48716f84 100644 --- a/gallagher/dto/items.py +++ b/gallagher/dto/items.py @@ -14,6 +14,15 @@ from .division import DivisionRef +class ItemRef( + AppBaseModel, + HrefMixin +): + """ Reference to an ItemType + """ + name: str + + class ItemTypeDetail( AppBaseModel, ): @@ -27,15 +36,6 @@ class ItemTypeDetail( canonical_type_name: str -class ItemRef( - AppBaseModel, - HrefMixin -): - """ Reference to an ItemType - """ - name: str - - class ItemSummary( ItemRef, IdentityMixin, From a5213b29cfee22c57c8e2e34c8c34da0a9ed1baf Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 17 Dec 2023 16:51:55 +1100 Subject: [PATCH 055/118] docs: update todo --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index d778b819..54267bdf 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Event summary - [ ] Event detail - [ ] Event POST body -- [ ] Event groups +- [x] Event groups - [x] Divisions - [x] Division - [ ] Division PATCH and POST example @@ -63,7 +63,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Competency summary - [ ] Competency detail - [ ] Card type search -- [ ] Card type +- [x] Card type - [ ] Operator group search - [ ] Operator group summary - [ ] Operator group detail From 8b9384b807d8d4a4daf23da25634dbedb0734818 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 18 Dec 2023 15:12:58 +1100 Subject: [PATCH 056/118] chore: update packages --- poetry.lock | 132 ++++++++++++++++++++++++++++--------------------- pyproject.toml | 5 +- 2 files changed, 78 insertions(+), 59 deletions(-) diff --git a/poetry.lock b/poetry.lock index e466bd21..0cfda622 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.1.0" +version = "4.2.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.1.0-py3-none-any.whl", hash = "sha256:56a415fbc462291813a94528a779597226619c8e78af7de0507333f700011e5f"}, - {file = "anyio-4.1.0.tar.gz", hash = "sha256:5a0bec7085176715be77df87fc66d6c9d70626bd752fcc85f57cdbee5b3760da"}, + {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, + {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, ] [package.dependencies] @@ -69,63 +69,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.2" +version = "7.3.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, + {file = "coverage-7.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d874434e0cb7b90f7af2b6e3309b0733cde8ec1476eb47db148ed7deeb2a9494"}, + {file = "coverage-7.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee6621dccce8af666b8c4651f9f43467bfbf409607c604b840b78f4ff3619aeb"}, + {file = "coverage-7.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1367aa411afb4431ab58fd7ee102adb2665894d047c490649e86219327183134"}, + {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f0f8f0c497eb9c9f18f21de0750c8d8b4b9c7000b43996a094290b59d0e7523"}, + {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0338c4b0951d93d547e0ff8d8ea340fecf5885f5b00b23be5aa99549e14cfd"}, + {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d31650d313bd90d027f4be7663dfa2241079edd780b56ac416b56eebe0a21aab"}, + {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9437a4074b43c177c92c96d051957592afd85ba00d3e92002c8ef45ee75df438"}, + {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17d9cb06c13b4f2ef570355fa45797d10f19ca71395910b249e3f77942a837"}, + {file = "coverage-7.3.3-cp310-cp310-win32.whl", hash = "sha256:eee5e741b43ea1b49d98ab6e40f7e299e97715af2488d1c77a90de4a663a86e2"}, + {file = "coverage-7.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:593efa42160c15c59ee9b66c5f27a453ed3968718e6e58431cdfb2d50d5ad284"}, + {file = "coverage-7.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c944cf1775235c0857829c275c777a2c3e33032e544bcef614036f337ac37bb"}, + {file = "coverage-7.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eda7f6e92358ac9e1717ce1f0377ed2b9320cea070906ece4e5c11d172a45a39"}, + {file = "coverage-7.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c854c1d2c7d3e47f7120b560d1a30c1ca221e207439608d27bc4d08fd4aeae8"}, + {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:222b038f08a7ebed1e4e78ccf3c09a1ca4ac3da16de983e66520973443b546bc"}, + {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff4800783d85bff132f2cc7d007426ec698cdce08c3062c8d501ad3f4ea3d16c"}, + {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fc200cec654311ca2c3f5ab3ce2220521b3d4732f68e1b1e79bef8fcfc1f2b97"}, + {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:307aecb65bb77cbfebf2eb6e12009e9034d050c6c69d8a5f3f737b329f4f15fb"}, + {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ffb0eacbadb705c0a6969b0adf468f126b064f3362411df95f6d4f31c40d31c1"}, + {file = "coverage-7.3.3-cp311-cp311-win32.whl", hash = "sha256:79c32f875fd7c0ed8d642b221cf81feba98183d2ff14d1f37a1bbce6b0347d9f"}, + {file = "coverage-7.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:243576944f7c1a1205e5cd658533a50eba662c74f9be4c050d51c69bd4532936"}, + {file = "coverage-7.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a2ac4245f18057dfec3b0074c4eb366953bca6787f1ec397c004c78176a23d56"}, + {file = "coverage-7.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9191be7af41f0b54324ded600e8ddbcabea23e1e8ba419d9a53b241dece821d"}, + {file = "coverage-7.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c0b1b8b5a4aebf8fcd227237fc4263aa7fa0ddcd4d288d42f50eff18b0bac4"}, + {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee453085279df1bac0996bc97004771a4a052b1f1e23f6101213e3796ff3cb85"}, + {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1191270b06ecd68b1d00897b2daddb98e1719f63750969614ceb3438228c088e"}, + {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:007a7e49831cfe387473e92e9ff07377f6121120669ddc39674e7244350a6a29"}, + {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:af75cf83c2d57717a8493ed2246d34b1f3398cb8a92b10fd7a1858cad8e78f59"}, + {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:811ca7373da32f1ccee2927dc27dc523462fd30674a80102f86c6753d6681bc6"}, + {file = "coverage-7.3.3-cp312-cp312-win32.whl", hash = "sha256:733537a182b5d62184f2a72796eb6901299898231a8e4f84c858c68684b25a70"}, + {file = "coverage-7.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:e995efb191f04b01ced307dbd7407ebf6e6dc209b528d75583277b10fd1800ee"}, + {file = "coverage-7.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbd8a5fe6c893de21a3c6835071ec116d79334fbdf641743332e442a3466f7ea"}, + {file = "coverage-7.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:50c472c1916540f8b2deef10cdc736cd2b3d1464d3945e4da0333862270dcb15"}, + {file = "coverage-7.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e9223a18f51d00d3ce239c39fc41410489ec7a248a84fab443fbb39c943616c"}, + {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f501e36ac428c1b334c41e196ff6bd550c0353c7314716e80055b1f0a32ba394"}, + {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475de8213ed95a6b6283056d180b2442eee38d5948d735cd3d3b52b86dd65b92"}, + {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afdcc10c01d0db217fc0a64f58c7edd635b8f27787fea0a3054b856a6dff8717"}, + {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fff0b2f249ac642fd735f009b8363c2b46cf406d3caec00e4deeb79b5ff39b40"}, + {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a1f76cfc122c9e0f62dbe0460ec9cc7696fc9a0293931a33b8870f78cf83a327"}, + {file = "coverage-7.3.3-cp38-cp38-win32.whl", hash = "sha256:757453848c18d7ab5d5b5f1827293d580f156f1c2c8cef45bfc21f37d8681069"}, + {file = "coverage-7.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad2453b852a1316c8a103c9c970db8fbc262f4f6b930aa6c606df9b2766eee06"}, + {file = "coverage-7.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b15e03b8ee6a908db48eccf4e4e42397f146ab1e91c6324da44197a45cb9132"}, + {file = "coverage-7.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89400aa1752e09f666cc48708eaa171eef0ebe3d5f74044b614729231763ae69"}, + {file = "coverage-7.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c59a3e59fb95e6d72e71dc915e6d7fa568863fad0a80b33bc7b82d6e9f844973"}, + {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ede881c7618f9cf93e2df0421ee127afdfd267d1b5d0c59bcea771cf160ea4a"}, + {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3bfd2c2f0e5384276e12b14882bf2c7621f97c35320c3e7132c156ce18436a1"}, + {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f3bad1a9313401ff2964e411ab7d57fb700a2d5478b727e13f156c8f89774a0"}, + {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:65d716b736f16e250435473c5ca01285d73c29f20097decdbb12571d5dfb2c94"}, + {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a702e66483b1fe602717020a0e90506e759c84a71dbc1616dd55d29d86a9b91f"}, + {file = "coverage-7.3.3-cp39-cp39-win32.whl", hash = "sha256:7fbf3f5756e7955174a31fb579307d69ffca91ad163467ed123858ce0f3fd4aa"}, + {file = "coverage-7.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cad9afc1644b979211989ec3ff7d82110b2ed52995c2f7263e7841c846a75348"}, + {file = "coverage-7.3.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:d299d379b676812e142fb57662a8d0d810b859421412b4d7af996154c00c31bb"}, + {file = "coverage-7.3.3.tar.gz", hash = "sha256:df04c64e58df96b4427db8d0559e95e2df3138c9916c96f9f6a4dd220db2fdb7"}, ] [package.extras] @@ -441,6 +441,24 @@ pluggy = ">=0.12,<2.0" [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-asyncio" +version = "0.23.2" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"}, + {file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "pytest-cov" version = "4.1.0" @@ -537,4 +555,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "b306cf2dff8f61532978a697b696f78f80317e46f5cbd385c1913afed607b691" +content-hash = "ea1f66b0ea5936b9d5d1a0e6e6389a1382a502720f756d7ed34b6f7350b3afa0" diff --git a/pyproject.toml b/pyproject.toml index 7baa44ab..17292343 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,10 +21,10 @@ python = "^3.11" httpx = "^0.25.2" pydantic = "^2.5.2" typing-extensions = "^4.9.0" -coverage = "^7.3.2" +coverage = "^7.3.3" pytest = "^7.4.3" pytest-order = "^1.2.0" -anyio = "^4.1.0" +anyio = "^4.2.0" annotated-types = "^0.6.0" certifi = "^2023.11.17" idna = "^3.6" @@ -32,6 +32,7 @@ packaging = "^23.2" pluggy = "^1.3.0" typer = "^0.9.0" rich = "^13.7.0" +pytest-asyncio = "^0.23.2" [tool.poetry.group.dev.dependencies] pytest = "^7.2.2" From cc73e4e0590728836735334aac3ba1abde4ccd01 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 18 Dec 2023 15:57:35 +1100 Subject: [PATCH 057/118] chore: move to concurrency this is our attempt to move to concurrency before the library goes out of hand there are a few exception at the moment around typer not being able to support async/await we can work around that when they support it https://github.com/anomaly/gallagher/issues/18 --- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/events.py | 4 +- gallagher/cc/alarms/items.py | 4 +- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/card_type.py | 2 +- gallagher/cc/cardholders/cardholders.py | 4 +- gallagher/cc/core.py | 156 +++++++++++++----------- gallagher/cli/cardholders.py | 4 +- gallagher/dto/cardholder.py | 2 +- gallagher/dto/discover.py | 2 +- gallagher/dto/event.py | 4 +- pytest.ini | 2 + tests/__init__.py | 6 + 15 files changed, 110 insertions(+), 88 deletions(-) create mode 100644 pytest.ini diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 64af6fd6..943de9b2 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -22,7 +22,7 @@ class Alarms( """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.alarms.alarms, dto_list=AlarmResponse, diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index f908665e..ca9112f8 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -19,7 +19,7 @@ class DayCategory(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.day_categories .day_categories, diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index bb9990ae..383e4838 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -23,7 +23,7 @@ class Division(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.divisions.divisions, dto_list=DivisionDetailResponse, diff --git a/gallagher/cc/alarms/events.py b/gallagher/cc/alarms/events.py index 7a5bab56..4b843d21 100644 --- a/gallagher/cc/alarms/events.py +++ b/gallagher/cc/alarms/events.py @@ -23,7 +23,7 @@ class Event( """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.events.events, dto_list=EventResponse, @@ -39,7 +39,7 @@ class EventType( """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.events.event_groups, dto_list=EventTypeResponse, diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 8d06d1fb..b5aa226d 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -22,7 +22,7 @@ class ItemsTypes(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.items.item_types, dto_list=ItemTypesResponse, @@ -39,7 +39,7 @@ class Item(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.items.items, dto_list=ItemsSummaryResponse, diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 38095ef7..77c8f3bb 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -18,7 +18,7 @@ class Schedule(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.schedules.schedules, dto_list=ScheduleSummaryResponse, diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 722dded0..d523b778 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -21,7 +21,7 @@ class CardType(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.card_types.card_types, dto_list=CardTypeResponse, diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 0f97fedb..9341d1dc 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -22,7 +22,7 @@ class Cardholder(APIEndpoint): """ @classmethod - def get_config(cls): + async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.cardholders.cardholders, dto_list=CardholderSummaryResponse, @@ -30,5 +30,5 @@ def get_config(cls): ) @classmethod - def search(cls, name: str, sort: str = "id", top: int = 100): + async def search(cls, name: str, sort: str = "id", top: int = 100): pass diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index f2ea3ce7..c3387da9 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -27,7 +27,7 @@ ) -def check_api_key_format(api_key): +async def check_api_key_format(api_key): """ Validates that the Gallagher Key is in the right format. It's not possible for the API client to validate the key against @@ -38,7 +38,7 @@ def check_api_key_format(api_key): return (api_tokens.count() == 8) -def get_authorization_headers(): +async def get_authorization_headers(): """ Creates an authorization header for Gallagher API calls The server expects an Authorization header with GGL-API-KEY @@ -88,7 +88,7 @@ class EndpointConfig: # fields: list[str] = [] # Optional list of fields @classmethod - def validate_endpoint(cls): + async def validate_endpoint(cls): """ Check to see if the feature is licensed and available Gallagher REST API is licensed per feature, if a feature is not @@ -120,6 +120,13 @@ class Capabilities: ) +# Async client for making requests to the Gallagher API +# see documentation where httpx suggests we use the same +# object across requests +# https://www.python-httpx.org/async/#opening-and-closing-clients +_httpx_async = httpx.AsyncClient() + + class APIEndpoint: """ Base class for all API objects @@ -136,7 +143,7 @@ class APIEndpoint: __config__ = None @classmethod - def get_config(cls): + async def get_config(cls): """ Returns the configuration for the endpoint This method can be overridden by the child class to @@ -145,7 +152,7 @@ def get_config(cls): return None @classmethod - def _discover(cls): + async def _discover(cls): """ The Command Centre root API endpoint Much of Gallagher's API documentation suggests that we don't @@ -175,103 +182,108 @@ def _discover(cls): # Auto-discovery of the API endpoints, this will # be called as part of the bootstrapping process from . import api_base - response = httpx.get( - api_base, - headers=get_authorization_headers(), - ) - - parsed_obj = DiscoveryResponse.model_validate( - response.json() - ) - - # Assign the capabilities to the class, this should - # result in the endpoint - # - # With the refactored initialisation of the pydantic - # models, the values for the unavailable endpoints - # should be set to None - Capabilities.CURRENT = parsed_obj - - # Set this so the configuration is only discovered - # once per endpoint - # - # If we assign the __config__ variable in the class - # that inherits from this class, the instance of EndpointConfig - # will copy the None values from the Capabilities.CURRENT - # object, this primarily because Capabilities.CURRENT is - # an instance of a pyndatic object and all values are thus - # copied not referenced. - cls.__config__ = cls.get_config() + async with _httpx_async: + response = await _httpx_async.get( + api_base, + headers=get_authorization_headers(), + ) + + parsed_obj = DiscoveryResponse.model_validate( + response.json() + ) + + # Assign the capabilities to the class, this should + # result in the endpoint + # + # With the refactored initialisation of the pydantic + # models, the values for the unavailable endpoints + # should be set to None + Capabilities.CURRENT = parsed_obj + + # Set this so the configuration is only discovered + # once per endpoint + # + # If we assign the __config__ variable in the class + # that inherits from this class, the instance of EndpointConfig + # will copy the None values from the Capabilities.CURRENT + # object, this primarily because Capabilities.CURRENT is + # an instance of a pyndatic object and all values are thus + # copied not referenced. + cls.__config__ = await cls.get_config() @classmethod - def list(cls, skip=0): + async def list(cls, skip=0): """ For a list of objects for the given resource Most resources can be searched which is exposed by this method. Resources also allow pagination which can be controlled by the skip """ - cls._discover() + await cls._discover() - response = httpx.get( - f'{cls.__config__.endpoint.href}', - headers=get_authorization_headers(), - ) + async with _httpx_async: + + response = await _httpx_async.get( + f'{cls.__config__.endpoint.href}', + headers=get_authorization_headers(), + ) - parsed_obj = cls.__config__.dto_list.model_validate( - response.json() - ) + parsed_obj = cls.__config__.dto_list.model_validate( + response.json() + ) - return parsed_obj + return parsed_obj @classmethod - def retrieve(cls, id): + async def retrieve(cls, id): """ Retrieve a single object for the given resource Most objects have an ID which is numeral or UUID. Each resource also provides a href and pagination for children. """ - cls._discover() + await cls._discover() - response = httpx.get( - f'{cls.__config__.endpoint.href}/{id}', - headers=get_authorization_headers(), - ) + async with _httpx_async: - parsed_obj = cls.__config__.dto_retrieve.model_validate( - response.json() - ) + response = await _httpx_async.get( + f'{cls.__config__.endpoint.href}/{id}', + headers=get_authorization_headers(), + ) + + parsed_obj = cls.__config__.dto_retrieve.model_validate( + response.json() + ) - return parsed_obj + return parsed_obj @classmethod - def modify(cls): + async def modify(cls): """ """ pass @classmethod - def create(cls, **params): + async def create(cls, **params): """ """ cls._discover() @classmethod - def delete(cls): + async def delete(cls): """ """ cls._discover() @classmethod - def search(cls, - top: int = 100, - sort: str = 'id', - fields: str = 'defaults', - **kwargs - ): + async def search(cls, + top: int = 100, + sort: str = 'id', + fields: str = 'defaults', + **kwargs + ): """ Search wrapper for most objects to dynamically search content Each object has a set of fields that you can query for, most searches @@ -283,7 +295,7 @@ def search(cls, :param kwargs: Fields to search for """ - cls._discover() + await cls._discover() params = { 'top': top, @@ -295,14 +307,16 @@ def search(cls, # for each type of object that calls the base function params.update(kwargs) - response = httpx.get( - f'{cls.__config__.endpoint.href}', - params=params, - headers=get_authorization_headers(), - ) + async with _httpx_async: + + response = await _httpx_async.get( + f'{cls.__config__.endpoint.href}', + params=params, + headers=get_authorization_headers(), + ) - parsed_obj = cls.__config__.dto_list.model_validate( - response.json() - ) + parsed_obj = cls.__config__.dto_list.model_validate( + response.json() + ) - return parsed_obj + return parsed_obj diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index 1f1d4632..71a7a7bd 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -12,7 +12,7 @@ @app.command("list") -def list(): +async def list(): """ list all cardholders """ console = Console() @@ -33,7 +33,7 @@ def list(): @app.command("get") -def get(id: int): +async def get(id: int): """ get a cardholder by id """ cardholder = Cardholder.retrieve(id) diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index 62faf164..c61a5414 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -50,7 +50,7 @@ def __rich_repr__(self): "yes" if self.authorised else "no" ] - def __str__(self): + async def __str__(self): return f"{self.id} {self.first_name} {self.last_name}" diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index 654f4224..e3d184dc 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -262,7 +262,7 @@ class DiscoveryResponse( ] = FeaturesDetail() @property - def get_sem_ver(self): + async def get_sem_ver(self): """ Get a SemVer tuple from the version string """ return self.version.split(".") diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index 0c5016e2..081c78fe 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -9,7 +9,7 @@ IdentityMixin, ) -from ..dto.alarm import AlarmRef +# from ..dto.alarm import AlarmRef from ..dto.cardholder import CardholderRef from ..dto.access_group import AccessGroupRef from ..dto.door import DoorRef @@ -69,7 +69,7 @@ class EventSummaryResponse( message: Optional[str] occurrences: int priority: int - alarm: AlarmRef + # alarm: AlarmRef operator: CardholderRef source: str diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..d280de04 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py index dc5d6ae6..2aa93935 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,6 +3,12 @@ Test suite for the Gallagher Python idiomatic client library, using pytest run via Taskfile. + The pytest.mark.asyncio marker can be omitted entirely in auto mode + where the asyncio marker is added automatically to async test functions. + + Refer to pytest.ini for configuration + https://pytest-asyncio.readthedocs.io/en/latest/reference/markers/index.html + """ From cdaa0b2a0d05f4a4027c47c8f4dac676b8a8978b Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 18 Dec 2023 16:01:50 +1100 Subject: [PATCH 058/118] fix: circular dependency issues the Ref classes cause a circular depdency issue, i have disabled them in this commit but it will be inevitable as there are cross references between alarms and events, the eventual solution will have to be moving Refs into their own package so each dto can reference it --- gallagher/dto/event.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index 0c5016e2..7fb9f112 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -9,10 +9,10 @@ IdentityMixin, ) -from ..dto.alarm import AlarmRef -from ..dto.cardholder import CardholderRef -from ..dto.access_group import AccessGroupRef -from ..dto.door import DoorRef +# from ..dto.alarm import AlarmRef +# from ..dto.cardholder import CardholderRef +# from ..dto.access_group import AccessGroupRef +# from ..dto.door import DoorRef class EventTypeSummary( @@ -69,21 +69,21 @@ class EventSummaryResponse( message: Optional[str] occurrences: int priority: int - alarm: AlarmRef - - operator: CardholderRef - source: str - group: str - type: str - event_type: EventTypeSummary - division: str - cardholder: str - entry_access_zone: str - exit_access_zone: str - door: DoorRef - access_group: HrefMixin - card: str - modified_item: str + # alarm: AlarmRef + + # operator: CardholderRef + # source: str + # group: str + # type: str + # event_type: EventTypeSummary + # division: str + # cardholder: str + # entry_access_zone: str + # exit_access_zone: str + # door: DoorRef + # access_group: HrefMixin + # card: str + # modified_item: str next: HrefMixin previous: HrefMixin From 86918f875c87b3334f1eed23428c565f40314920 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 18 Dec 2023 16:25:28 +1100 Subject: [PATCH 059/118] fix: basic lifecycle of asyncio working moved to explicitly closing the client upon each requestREFS #18 --- gallagher/cc/core.py | 27 ++++++++++++++------------- tests/test_alarm.py | 4 ++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index c3387da9..0e199a20 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -27,7 +27,7 @@ ) -async def check_api_key_format(api_key): +def check_api_key_format(api_key): """ Validates that the Gallagher Key is in the right format. It's not possible for the API client to validate the key against @@ -38,7 +38,7 @@ async def check_api_key_format(api_key): return (api_tokens.count() == 8) -async def get_authorization_headers(): +def get_authorization_headers(): """ Creates an authorization header for Gallagher API calls The server expects an Authorization header with GGL-API-KEY @@ -120,13 +120,6 @@ class Capabilities: ) -# Async client for making requests to the Gallagher API -# see documentation where httpx suggests we use the same -# object across requests -# https://www.python-httpx.org/async/#opening-and-closing-clients -_httpx_async = httpx.AsyncClient() - - class APIEndpoint: """ Base class for all API objects @@ -182,12 +175,14 @@ async def _discover(cls): # Auto-discovery of the API endpoints, this will # be called as part of the bootstrapping process from . import api_base - async with _httpx_async: + async with httpx.AsyncClient() as _httpx_async: response = await _httpx_async.get( api_base, headers=get_authorization_headers(), ) + await _httpx_async.aclose() + parsed_obj = DiscoveryResponse.model_validate( response.json() ) @@ -220,13 +215,15 @@ async def list(cls, skip=0): """ await cls._discover() - async with _httpx_async: + async with httpx.AsyncClient() as _httpx_async: response = await _httpx_async.get( f'{cls.__config__.endpoint.href}', headers=get_authorization_headers(), ) + await _httpx_async.aclose() + parsed_obj = cls.__config__.dto_list.model_validate( response.json() ) @@ -243,13 +240,15 @@ async def retrieve(cls, id): """ await cls._discover() - async with _httpx_async: + async with httpx.AsyncClient() as _httpx_async: response = await _httpx_async.get( f'{cls.__config__.endpoint.href}/{id}', headers=get_authorization_headers(), ) + await _httpx_async.aclose() + parsed_obj = cls.__config__.dto_retrieve.model_validate( response.json() ) @@ -307,7 +306,7 @@ async def search(cls, # for each type of object that calls the base function params.update(kwargs) - async with _httpx_async: + async with httpx.AsyncClient() as _httpx_async: response = await _httpx_async.get( f'{cls.__config__.endpoint.href}', @@ -315,6 +314,8 @@ async def search(cls, headers=get_authorization_headers(), ) + await _httpx_async.aclose() + parsed_obj = cls.__config__.dto_list.model_validate( response.json() ) diff --git a/tests/test_alarm.py b/tests/test_alarm.py index bf5ba187..ae84e377 100644 --- a/tests/test_alarm.py +++ b/tests/test_alarm.py @@ -4,7 +4,7 @@ """ -def test_alarms_list(): +async def test_alarms_list(): """ Get a list of item types and iterates through it these are a summary response @@ -12,7 +12,7 @@ def test_alarms_list(): from gallagher.cc.alarms import Alarms from gallagher.dto.alarm import AlarmResponse - response = Alarms.list() + response = await Alarms.list() assert type(response) is AlarmResponse assert type(response.alarms) is list assert len(response.alarms) > 0 From baaf841ef4f3950a83981c4e69989432d334c811 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 18 Dec 2023 16:30:14 +1100 Subject: [PATCH 060/118] refactor: all tests are now async pending running a full test this changes all the test calls to be async, we recomend merging this branch into the dto-implementation branch and let the tests run against the server to see if the tests pass REFS #18 --- tests/__init__.py | 2 ++ tests/test_card_types.py | 4 ++-- tests/test_cardholder.py | 8 ++++---- tests/test_day_category.py | 4 ++-- tests/test_discover.py | 2 +- tests/test_divisions.py | 10 +++++----- tests/test_events.py | 4 ++-- tests/test_items.py | 14 +++++++------- tests/test_schedule.py | 4 ++-- tests/test_version.py | 2 +- 10 files changed, 28 insertions(+), 26 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 2aa93935..243b2176 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -9,6 +9,8 @@ Refer to pytest.ini for configuration https://pytest-asyncio.readthedocs.io/en/latest/reference/markers/index.html + TODO: check if setup and teardown can be turned into async + """ diff --git a/tests/test_card_types.py b/tests/test_card_types.py index 1e459c3e..18bdb122 100644 --- a/tests/test_card_types.py +++ b/tests/test_card_types.py @@ -3,11 +3,11 @@ """ -def test_get_card_types(): +async def test_get_card_types(): from gallagher.cc.cardholders.card_type import CardType from gallagher.dto.card_type import CardTypeResponse - response = CardType.list() + response = await CardType.list() assert type(response) is CardTypeResponse assert type(response.results) is list assert len(response.results) > 0 diff --git a/tests/test_cardholder.py b/tests/test_cardholder.py index 29f112d9..61e6d1d1 100644 --- a/tests/test_cardholder.py +++ b/tests/test_cardholder.py @@ -3,18 +3,18 @@ """ -def test_cardholder_list(): +async def test_cardholder_list(): from gallagher.cc.cardholders.cardholders import Cardholder from gallagher.dto.cardholder import CardholderSummaryResponse - response = Cardholder.list() + response = await Cardholder.list() assert type(response) is CardholderSummaryResponse assert type(response.results) is list assert len(response.results) > 0 -def test_cardholder_detail(): +async def test_cardholder_detail(): from gallagher.cc.cardholders.cardholders import Cardholder from gallagher.dto.cardholder import ( @@ -22,7 +22,7 @@ def test_cardholder_detail(): CardholderDetail, ) - response = Cardholder.list() + response = await Cardholder.list() assert type(response) is CardholderSummaryResponse for cardholder_summary in response.results: diff --git a/tests/test_day_category.py b/tests/test_day_category.py index b36947fc..c0f784a2 100644 --- a/tests/test_day_category.py +++ b/tests/test_day_category.py @@ -3,12 +3,12 @@ """ -def test_day_category(): +async def test_day_category(): from gallagher.cc.alarms.day_category import DayCategory from gallagher.dto.day_category import DayCategoryResponse - response = DayCategory.list() + response = await DayCategory.list() assert type(response) is DayCategoryResponse assert type(response.results) is list assert len(response.results) > 0 diff --git a/tests/test_discover.py b/tests/test_discover.py index c33fbe91..f9caa99d 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -3,7 +3,7 @@ """ -def test_discover(): +async def test_discover(): """ """ diff --git a/tests/test_divisions.py b/tests/test_divisions.py index f2d38100..3d1af3d4 100644 --- a/tests/test_divisions.py +++ b/tests/test_divisions.py @@ -1,26 +1,26 @@ -def test_division_list(): +async def test_division_list(): from gallagher.cc.alarms.divisions import Division from gallagher.dto.division import DivisionDetailResponse - response = Division.list() + response = await Division.list() assert type(response) is DivisionDetailResponse assert type(response.results) is list assert len(response.results) > 0 -def test_division_detail(): +async def test_division_detail(): from gallagher.cc.alarms.divisions import Division from gallagher.dto.division import ( DivisionDetailResponse, DivisionDetail, ) - response = Division.list() + response = await Division.list() assert type(response) is DivisionDetailResponse assert type(response.results) is list for division_summary in response.results: # Get the detail of the division - division_detail_response = Division.retrieve(division_summary.id) + division_detail_response = await Division.retrieve(division_summary.id) assert type(division_detail_response) is DivisionDetail assert division_detail_response.id == division_summary.id diff --git a/tests/test_events.py b/tests/test_events.py index 7fdea099..97b5e974 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -1,10 +1,10 @@ -def test_event_types(): +async def test_event_types(): from gallagher.cc.alarms.events import EventType from gallagher.dto.event import ( EventTypeResponse, ) - response = EventType.list() + response = await EventType.list() assert type(response) is EventTypeResponse diff --git a/tests/test_items.py b/tests/test_items.py index 2e83b20f..49a9f93a 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -4,7 +4,7 @@ """ -def test_items_types_list(): +async def test_items_types_list(): """ Get a list of item types and iterates through it these are a summary response @@ -12,13 +12,13 @@ def test_items_types_list(): from gallagher.cc.alarms.items import ItemsTypes from gallagher.dto.items import ItemTypesResponse - response = ItemsTypes.list() + response = await ItemsTypes.list() assert type(response) is ItemTypesResponse assert type(response.item_types) is list assert len(response.item_types) > 0 -def test_items_list(): +async def test_items_list(): """ Get a list of items and this should feed into fetching each one of these on it's own. @@ -26,13 +26,13 @@ def test_items_list(): from gallagher.cc.alarms.items import Item from gallagher.dto.items import ItemsSummaryResponse - response = Item.list() + response = await Item.list() assert type(response) is ItemsSummaryResponse assert type(response.results) is list assert len(response.results) > 0 -def test_item_detail(): +async def test_item_detail(): """ Get each item in the list and make sure it's a valid item """ @@ -42,11 +42,11 @@ def test_item_detail(): ItemDetail ) - response: ItemsSummaryResponse = Item.list() + response: ItemsSummaryResponse = await Item.list() assert type(response) is ItemsSummaryResponse for item_summary in response.results: # Get the detail of the item - item_detail_response = Item.retrieve(item_summary.id) + item_detail_response = await Item.retrieve(item_summary.id) assert type(item_detail_response) is ItemDetail assert (item_detail_response.id == item_summary.id) diff --git a/tests/test_schedule.py b/tests/test_schedule.py index 7549a546..94f3fb64 100644 --- a/tests/test_schedule.py +++ b/tests/test_schedule.py @@ -4,12 +4,12 @@ """ -def test_schedules_list(): +async def test_schedules_list(): from gallagher.cc.alarms.schedule import Schedule from gallagher.dto.schedule import ScheduleSummaryResponse - response = Schedule.list() + response = await Schedule.list() assert type(response) is ScheduleSummaryResponse assert type(response.results) is list assert len(response.results) > 0 diff --git a/tests/test_version.py b/tests/test_version.py index d1adffdc..0944a78f 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -6,5 +6,5 @@ from gallagher import __version__ -def test_version(): +async def test_version(): assert __version__ == '0.1.0-alpha.1' From cc2be329da5eaf96839554cc9974829cbf20d575 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 19 Dec 2023 08:36:05 +1100 Subject: [PATCH 061/118] chore: adds typer with all option --- poetry.lock | 16 +++++++++++++++- pyproject.toml | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0cfda622..5429ab5e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -509,6 +509,17 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "sniffio" version = "1.3.0" @@ -533,6 +544,9 @@ files = [ [package.dependencies] click = ">=7.1.1,<9.0.0" +colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"all\""} +rich = {version = ">=10.11.0,<14.0.0", optional = true, markers = "extra == \"all\""} +shellingham = {version = ">=1.3.0,<2.0.0", optional = true, markers = "extra == \"all\""} typing-extensions = ">=3.7.4.3" [package.extras] @@ -555,4 +569,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "ea1f66b0ea5936b9d5d1a0e6e6389a1382a502720f756d7ed34b6f7350b3afa0" +content-hash = "cb533402e7870ecaffdee556206d07cc1a4c92c5b8f842997e65562d8b729cb5" diff --git a/pyproject.toml b/pyproject.toml index 17292343..04a91ff3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ certifi = "^2023.11.17" idna = "^3.6" packaging = "^23.2" pluggy = "^1.3.0" -typer = "^0.9.0" +typer = {extras = ["all"], version = "^0.9.0"} rich = "^13.7.0" pytest-asyncio = "^0.23.2" From 1dc72c1831bd67b600250df6b13c897ea21e240d Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 19 Dec 2023 08:36:13 +1100 Subject: [PATCH 062/118] fix: async await issue in test --- tests/test_cardholder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_cardholder.py b/tests/test_cardholder.py index 61e6d1d1..ed57c341 100644 --- a/tests/test_cardholder.py +++ b/tests/test_cardholder.py @@ -27,6 +27,8 @@ async def test_cardholder_detail(): for cardholder_summary in response.results: # Get the detail of the cardholder for comparison - cardholder_detail_response = Cardholder.retrieve(cardholder_summary.id) + cardholder_detail_response = await Cardholder.retrieve( + cardholder_summary.id + ) assert type(cardholder_detail_response) is CardholderDetail assert (cardholder_detail_response.id == cardholder_summary.id) From 620b652464c2f6cbddb53d519b912384476cd639 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 19 Dec 2023 09:25:34 +1100 Subject: [PATCH 063/118] refactor: async support for cli typer officially does not support asyncio, there's a thread about it https://github.com/tiangolo/typer/issues/88 essentially we are using a decorator based workaround which has been adapted from a solution posted by @gilcu2 on his comment https://github.com/tiangolo/typer/issues/88\#issuecomment-1732469681 note that this requires the use of asyncer which uses a previous version of anyio, i am willing to live with this for now until an official solution is published by typer --- gallagher/cli/__init__.py | 8 +++--- gallagher/cli/alarms.py | 7 +++--- gallagher/cli/cardholders.py | 13 ++++++---- gallagher/cli/utils.py | 49 ++++++++++++++++++++++++++++++++++++ poetry.lock | 30 ++++++++++++++++------ pyproject.toml | 2 +- 6 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 gallagher/cli/utils.py diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py index ddcd0b2e..8d2245a3 100644 --- a/gallagher/cli/__init__.py +++ b/gallagher/cli/__init__.py @@ -1,16 +1,16 @@ -""" +""" CLI entry point """ -import typer - from gallagher import __version__ +from .utils import AsyncTyper + from .alarms import app as alarms_app from .cardholders import app as cardholders_app # Main Typer app use to create the CLI -app = typer.Typer() +app = AsyncTyper() app.add_typer(alarms_app, name="alarms") app.add_typer(cardholders_app, name="ch") diff --git a/gallagher/cli/alarms.py b/gallagher/cli/alarms.py index 74d2edce..729f1c24 100644 --- a/gallagher/cli/alarms.py +++ b/gallagher/cli/alarms.py @@ -3,12 +3,13 @@ """ -from gallagher import cc import os -import typer + +from gallagher import cc +from .utils import AsyncTyper api_key = os.environ.get("GACC_API_KEY") cc.api_key = api_key -app = typer.Typer() +app = AsyncTyper() diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index 71a7a7bd..218e0579 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -1,14 +1,17 @@ """ Cardholder cli commands mounted at ch """ -import typer from rich import print as rprint from rich.console import Console from rich.table import Table -from gallagher.cc.cardholders.cardholders import Cardholder +from .utils import AsyncTyper -app = typer.Typer(help="query or manage cardholders") +from gallagher.cc.cardholders.cardholders import ( + Cardholder +) + +app = AsyncTyper(help="query or manage cardholders") @app.command("list") @@ -20,7 +23,7 @@ async def list(): "[bold green]Fetching cardholders...", spinner="clock" ): - cardholders = Cardholder.list() + cardholders = await Cardholder.list() table = Table(title="Cardholders") for header in cardholders.cli_header: @@ -36,5 +39,5 @@ async def list(): async def get(id: int): """ get a cardholder by id """ - cardholder = Cardholder.retrieve(id) + cardholder = await Cardholder.retrieve(id) [rprint(r) for r in cardholder.__rich_repr__()] diff --git a/gallagher/cli/utils.py b/gallagher/cli/utils.py new file mode 100644 index 00000000..d2733375 --- /dev/null +++ b/gallagher/cli/utils.py @@ -0,0 +1,49 @@ +""" Asyncio patches for Typer + +There's a thread open on the typer repo about this: + https://github.com/tiangolo/typer/issues/88 + +Essentially, Typer doesn't support async functions. This is an issue +post migration to async, @csheppard points out that there's a work +around using a decorator on the click repository: +https://github.com/tiangolo/typer/issues/88#issuecomment-612687289 + +@gilcu2 posted a similar solution on the typer repo: +https://github.com/tiangolo/typer/issues/88#issuecomment-1732469681 + +this particular one uses asyncer to run the async function in a thread +we're going in with this with the hope that the official solution is +closer to this than a decorator per command. +""" +import inspect + +from functools import ( + partial, + wraps, +) + +import asyncer +from typer import Typer + + +class AsyncTyper(Typer): + @staticmethod + def maybe_run_async(decorator, f): + if inspect.iscoroutinefunction(f): + + @wraps(f) + def runner(*args, **kwargs): + return asyncer.runnify(f)(*args, **kwargs) + + decorator(runner) + else: + decorator(f) + return f + + def callback(self, *args, **kwargs): + decorator = super().callback(*args, **kwargs) + return partial(self.maybe_run_async, decorator) + + def command(self, *args, **kwargs): + decorator = super().command(*args, **kwargs) + return partial(self.maybe_run_async, decorator) diff --git a/poetry.lock b/poetry.lock index 5429ab5e..251e3065 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.2.0" +version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, - {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] [package.dependencies] @@ -27,9 +27,23 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "asyncer" +version = "0.0.2" +description = "Asyncer, async and await, focused on developer experience." +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "asyncer-0.0.2-py3-none-any.whl", hash = "sha256:46e0e1423ce21588350ad425875e81795280b9e1f517e8a389de940b86c348bd"}, + {file = "asyncer-0.0.2.tar.gz", hash = "sha256:d546c85f3626ebbaf06bb4395db49761c902a61a6ac802b1a74133cab4f7f433"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<4.0.0" [[package]] name = "certifi" @@ -569,4 +583,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "cb533402e7870ecaffdee556206d07cc1a4c92c5b8f842997e65562d8b729cb5" +content-hash = "e89eca878d5a623d4c943065a133978fafba45e1928b87024480f11a1b16357b" diff --git a/pyproject.toml b/pyproject.toml index 04a91ff3..c17c0b51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ typing-extensions = "^4.9.0" coverage = "^7.3.3" pytest = "^7.4.3" pytest-order = "^1.2.0" -anyio = "^4.2.0" annotated-types = "^0.6.0" certifi = "^2023.11.17" idna = "^3.6" @@ -33,6 +32,7 @@ pluggy = "^1.3.0" typer = {extras = ["all"], version = "^0.9.0"} rich = "^13.7.0" pytest-asyncio = "^0.23.2" +asyncer = "^0.0.2" [tool.poetry.group.dev.dependencies] pytest = "^7.2.2" From 0c0c9597bca10621923ca97a666e16ff50f5b026 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 19 Dec 2023 09:57:40 +1100 Subject: [PATCH 064/118] refactor: move configuration to package root moves the environment variable configuration to the root of the package clean up various imports post asyncio workaround, see previous commit for details on how typer asyncio works REFS #18 #13 --- gallagher/cli/__init__.py | 18 +++++++++++++++++- gallagher/cli/alarms.py | 5 ----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py index 8d2245a3..97f75a11 100644 --- a/gallagher/cli/__init__.py +++ b/gallagher/cli/__init__.py @@ -1,16 +1,32 @@ """ CLI entry point +gal is the gallagher command line, which is constructed using Typer. +At the moment typer does not support async functions, so we have to +use a wrapper to make it work. See utils.py for the AsyncTyper class. +The cli should be pretty self documenting, however see docs/ for +official documentation. """ -from gallagher import __version__ +import os + +from gallagher import ( + cc, + __version__ +) from .utils import AsyncTyper from .alarms import app as alarms_app from .cardholders import app as cardholders_app +# Load the API key for the package so all entry points +# can use it to query the service +api_key = os.environ.get("GACC_API_KEY") +cc.api_key = api_key + # Main Typer app use to create the CLI app = AsyncTyper() +# Load up all sub commands app.add_typer(alarms_app, name="alarms") app.add_typer(cardholders_app, name="ch") diff --git a/gallagher/cli/alarms.py b/gallagher/cli/alarms.py index 729f1c24..bc1dc811 100644 --- a/gallagher/cli/alarms.py +++ b/gallagher/cli/alarms.py @@ -3,13 +3,8 @@ """ -import os -from gallagher import cc from .utils import AsyncTyper -api_key = os.environ.get("GACC_API_KEY") - -cc.api_key = api_key app = AsyncTyper() From d556a56eb3abcc7db1c6abc23929070da8b7e367 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 13:00:51 +1100 Subject: [PATCH 065/118] refactor: start of splitting up dto into sub packages decided to go with grouping dtos into refs, summary, detail and response this will be one of many commits before the code base is stable again REFS #21 --- gallagher/dto/access_group.py | 70 ----------------- gallagher/dto/alarm.py | 68 ---------------- gallagher/dto/card_type.py | 31 -------- gallagher/dto/cardholder.py | 105 ------------------------- gallagher/dto/day_category.py | 43 ---------- gallagher/dto/detail/__init__.py | 9 +++ gallagher/dto/detail/access_group.py | 70 +++++++++++++++++ gallagher/dto/detail/alarm.py | 0 gallagher/dto/detail/card_type.py | 30 +++++++ gallagher/dto/detail/cardholder.py | 59 ++++++++++++++ gallagher/dto/detail/day_category.py | 0 gallagher/dto/detail/discover.py | 0 gallagher/dto/detail/division.py | 25 ++++++ gallagher/dto/detail/door.py | 0 gallagher/dto/detail/event.py | 0 gallagher/dto/detail/features.py | 0 gallagher/dto/detail/items.py | 0 gallagher/dto/detail/locker.py | 0 gallagher/dto/detail/operator.py | 0 gallagher/dto/detail/pdf.py | 0 gallagher/dto/detail/reception.py | 0 gallagher/dto/detail/role.py | 0 gallagher/dto/detail/salto.py | 0 gallagher/dto/detail/schedule.py | 0 gallagher/dto/detail/utils.py | 0 gallagher/dto/detail/visit.py | 0 gallagher/dto/detail/visitor.py | 0 gallagher/dto/detail/zone.py | 0 gallagher/dto/division.py | 40 ---------- gallagher/dto/ref/__init__.py | 17 ++++ gallagher/dto/ref/access_group.py | 13 +++ gallagher/dto/ref/alarm.py | 22 ++++++ gallagher/dto/ref/card_type.py | 0 gallagher/dto/ref/cardholder.py | 13 +++ gallagher/dto/ref/day_category.py | 22 ++++++ gallagher/dto/ref/discover.py | 0 gallagher/dto/ref/division.py | 18 +++++ gallagher/dto/ref/door.py | 0 gallagher/dto/ref/event.py | 0 gallagher/dto/ref/features.py | 0 gallagher/dto/ref/items.py | 0 gallagher/dto/ref/locker.py | 0 gallagher/dto/ref/operator.py | 0 gallagher/dto/ref/pdf.py | 0 gallagher/dto/ref/reception.py | 0 gallagher/dto/ref/role.py | 0 gallagher/dto/ref/salto.py | 0 gallagher/dto/ref/schedule.py | 0 gallagher/dto/ref/utils.py | 0 gallagher/dto/ref/visit.py | 0 gallagher/dto/ref/visitor.py | 0 gallagher/dto/ref/zone.py | 0 gallagher/dto/response/__init__.py | 4 + gallagher/dto/response/access_group.py | 0 gallagher/dto/response/alarm.py | 17 ++++ gallagher/dto/response/card_type.py | 19 +++++ gallagher/dto/response/cardholder.py | 33 ++++++++ gallagher/dto/response/day_category.py | 20 +++++ gallagher/dto/response/discover.py | 0 gallagher/dto/response/division.py | 21 +++++ gallagher/dto/response/door.py | 0 gallagher/dto/response/event.py | 0 gallagher/dto/response/features.py | 0 gallagher/dto/response/items.py | 0 gallagher/dto/response/locker.py | 0 gallagher/dto/response/operator.py | 0 gallagher/dto/response/pdf.py | 0 gallagher/dto/response/reception.py | 0 gallagher/dto/response/role.py | 0 gallagher/dto/response/salto.py | 0 gallagher/dto/response/schedule.py | 0 gallagher/dto/response/utils.py | 0 gallagher/dto/response/visit.py | 0 gallagher/dto/response/visitor.py | 0 gallagher/dto/response/zone.py | 0 gallagher/dto/summary/__init__.py | 4 + gallagher/dto/summary/access_group.py | 38 +++++++++ gallagher/dto/summary/alarm.py | 52 ++++++++++++ gallagher/dto/summary/card_type.py | 13 +++ gallagher/dto/summary/cardholder.py | 36 +++++++++ gallagher/dto/summary/day_category.py | 23 ++++++ gallagher/dto/summary/discover.py | 0 gallagher/dto/summary/division.py | 0 gallagher/dto/summary/door.py | 0 gallagher/dto/summary/event.py | 0 gallagher/dto/summary/features.py | 0 gallagher/dto/summary/items.py | 0 gallagher/dto/summary/locker.py | 0 gallagher/dto/summary/operator.py | 0 gallagher/dto/summary/pdf.py | 0 gallagher/dto/summary/reception.py | 0 gallagher/dto/summary/role.py | 0 gallagher/dto/summary/salto.py | 0 gallagher/dto/summary/schedule.py | 0 gallagher/dto/summary/utils.py | 0 gallagher/dto/summary/visit.py | 0 gallagher/dto/summary/visitor.py | 0 gallagher/dto/summary/zone.py | 0 98 files changed, 578 insertions(+), 357 deletions(-) create mode 100644 gallagher/dto/detail/__init__.py create mode 100644 gallagher/dto/detail/access_group.py create mode 100644 gallagher/dto/detail/alarm.py create mode 100644 gallagher/dto/detail/card_type.py create mode 100644 gallagher/dto/detail/cardholder.py create mode 100644 gallagher/dto/detail/day_category.py create mode 100644 gallagher/dto/detail/discover.py create mode 100644 gallagher/dto/detail/division.py create mode 100644 gallagher/dto/detail/door.py create mode 100644 gallagher/dto/detail/event.py create mode 100644 gallagher/dto/detail/features.py create mode 100644 gallagher/dto/detail/items.py create mode 100644 gallagher/dto/detail/locker.py create mode 100644 gallagher/dto/detail/operator.py create mode 100644 gallagher/dto/detail/pdf.py create mode 100644 gallagher/dto/detail/reception.py create mode 100644 gallagher/dto/detail/role.py create mode 100644 gallagher/dto/detail/salto.py create mode 100644 gallagher/dto/detail/schedule.py create mode 100644 gallagher/dto/detail/utils.py create mode 100644 gallagher/dto/detail/visit.py create mode 100644 gallagher/dto/detail/visitor.py create mode 100644 gallagher/dto/detail/zone.py create mode 100644 gallagher/dto/ref/__init__.py create mode 100644 gallagher/dto/ref/access_group.py create mode 100644 gallagher/dto/ref/alarm.py create mode 100644 gallagher/dto/ref/card_type.py create mode 100644 gallagher/dto/ref/cardholder.py create mode 100644 gallagher/dto/ref/day_category.py create mode 100644 gallagher/dto/ref/discover.py create mode 100644 gallagher/dto/ref/division.py create mode 100644 gallagher/dto/ref/door.py create mode 100644 gallagher/dto/ref/event.py create mode 100644 gallagher/dto/ref/features.py create mode 100644 gallagher/dto/ref/items.py create mode 100644 gallagher/dto/ref/locker.py create mode 100644 gallagher/dto/ref/operator.py create mode 100644 gallagher/dto/ref/pdf.py create mode 100644 gallagher/dto/ref/reception.py create mode 100644 gallagher/dto/ref/role.py create mode 100644 gallagher/dto/ref/salto.py create mode 100644 gallagher/dto/ref/schedule.py create mode 100644 gallagher/dto/ref/utils.py create mode 100644 gallagher/dto/ref/visit.py create mode 100644 gallagher/dto/ref/visitor.py create mode 100644 gallagher/dto/ref/zone.py create mode 100644 gallagher/dto/response/__init__.py create mode 100644 gallagher/dto/response/access_group.py create mode 100644 gallagher/dto/response/alarm.py create mode 100644 gallagher/dto/response/card_type.py create mode 100644 gallagher/dto/response/cardholder.py create mode 100644 gallagher/dto/response/day_category.py create mode 100644 gallagher/dto/response/discover.py create mode 100644 gallagher/dto/response/division.py create mode 100644 gallagher/dto/response/door.py create mode 100644 gallagher/dto/response/event.py create mode 100644 gallagher/dto/response/features.py create mode 100644 gallagher/dto/response/items.py create mode 100644 gallagher/dto/response/locker.py create mode 100644 gallagher/dto/response/operator.py create mode 100644 gallagher/dto/response/pdf.py create mode 100644 gallagher/dto/response/reception.py create mode 100644 gallagher/dto/response/role.py create mode 100644 gallagher/dto/response/salto.py create mode 100644 gallagher/dto/response/schedule.py create mode 100644 gallagher/dto/response/utils.py create mode 100644 gallagher/dto/response/visit.py create mode 100644 gallagher/dto/response/visitor.py create mode 100644 gallagher/dto/response/zone.py create mode 100644 gallagher/dto/summary/__init__.py create mode 100644 gallagher/dto/summary/access_group.py create mode 100644 gallagher/dto/summary/alarm.py create mode 100644 gallagher/dto/summary/card_type.py create mode 100644 gallagher/dto/summary/cardholder.py create mode 100644 gallagher/dto/summary/day_category.py create mode 100644 gallagher/dto/summary/discover.py create mode 100644 gallagher/dto/summary/division.py create mode 100644 gallagher/dto/summary/door.py create mode 100644 gallagher/dto/summary/event.py create mode 100644 gallagher/dto/summary/features.py create mode 100644 gallagher/dto/summary/items.py create mode 100644 gallagher/dto/summary/locker.py create mode 100644 gallagher/dto/summary/operator.py create mode 100644 gallagher/dto/summary/pdf.py create mode 100644 gallagher/dto/summary/reception.py create mode 100644 gallagher/dto/summary/role.py create mode 100644 gallagher/dto/summary/salto.py create mode 100644 gallagher/dto/summary/schedule.py create mode 100644 gallagher/dto/summary/utils.py create mode 100644 gallagher/dto/summary/visit.py create mode 100644 gallagher/dto/summary/visitor.py create mode 100644 gallagher/dto/summary/zone.py diff --git a/gallagher/dto/access_group.py b/gallagher/dto/access_group.py index 26017b06..1f2e4dbe 100644 --- a/gallagher/dto/access_group.py +++ b/gallagher/dto/access_group.py @@ -13,73 +13,3 @@ from .zone import AccessZoneRef from .salto import SaltoAccessItemSummary from .alarm import AlarmZoneSummary - -class AccessSummary( - AppBaseModel -): - """ Access is zone paired with a schedule - """ - access_zone: AccessZoneRef - schedule: ScheduleRef - - -class AccessGroupRef( - AppBaseModel, - HrefMixin -): - """ Access Groups is what a user is assigned to to provide access to doors - """ - name: str - -class AccessGroupSummary( - AccessGroupRef -): - """ AccessGroup Summary is what the API returns on searches - - This builds on the Ref class to add the summary fields and is - extended by the Detail class to add the fully remainder of - the fields - """ - description: Optional[str] - parent: Optional[AccessGroupRef] - division: IdentityMixin - cardholders: Optional[HrefMixin] - server_display_name: Optional[str] - -class AccessGroupDetail( - AccessGroupSummary -): - """ - """ - description: Optional[str] - parent: Optional[AccessGroupRef] - division: DivisionDetail - cardholders: Optional[HrefMixin] - server_display_name: Optional[str] - children: list[AccessGroupRef] - personal_data_definitions: list[PDFRef] - - visitor: bool - escort_visitors: bool - lock_unlock_access_zones: bool - enter_during_lockdown: bool - first_card_unlock: bool - override_aperio_privacy: bool - aperio_offline_access: bool - disarm_alarm_zones: bool - arm_alarm_zones: bool - hvLf_fence_zones: bool - view_alarms: bool - shunt: bool - lock_out_fence_zones: bool - cancel_fence_zone_lockout: bool - ack_all: bool - ack_below_high: bool - select_alarm_zone: bool - arm_while_alarm: bool - arm_while_active_alarm: bool - isolate_alarm_zones: bool - - access: list[AccessSummary] - salto_access: list[SaltoAccessItemSummary] - alarm_zones: list[AlarmZoneSummary] diff --git a/gallagher/dto/alarm.py b/gallagher/dto/alarm.py index 1c769881..e61d56a1 100644 --- a/gallagher/dto/alarm.py +++ b/gallagher/dto/alarm.py @@ -11,71 +11,3 @@ ) from .event import EventTypeSummary - - -class AlarmRef( - AppBaseModel, - HrefMixin -): - """ AlarmRef represents a single alarm - """ - state: str - - -class AlarmSource( - AppBaseModel, - HrefMixin, - IdentityMixin, -): - """ AlarmSource represents a device that has triggered an alarm - """ - name: str - - -class AlarmZoneRef( - AppBaseModel, - HrefMixin -): - """ AccessZone represents - """ - name: str - - -class AlarmZoneSummary( - AppBaseModel, - HrefMixin, - IdentityMixin, -): - """ #TODO: Revise this if it shows up in other places - - I have literally named this class to model the alarm_zones - property in the access_group schema. I don't know if this - is appropriate - """ - time: str - message: str - source: AlarmSource - type: str - event_type: Optional[EventTypeSummary] = None - priority: int - state: str - active: bool - division: HrefMixin - event: Optional[HrefMixin] = None - note_presets: list[str] = [] - view: HrefMixin - comment: HrefMixin - acknowledge: Optional[HrefMixin] = None - acknowledge_with_comment: Optional[HrefMixin] = None - process: Optional[HrefMixin] = None - process_with_comment: Optional[HrefMixin] = None - force_process: Optional[HrefMixin] = None - - -class AlarmResponse( - AppBaseModel, -): - """ AlarmResponse represents a single alarm - """ - alarms: list[AlarmZoneSummary] - updates: HrefMixin diff --git a/gallagher/dto/card_type.py b/gallagher/dto/card_type.py index fd8c9267..a26f9177 100644 --- a/gallagher/dto/card_type.py +++ b/gallagher/dto/card_type.py @@ -10,34 +10,3 @@ IdentityMixin, HrefMixin ) - -class CardExpiryType( - AppBaseModel -): - expiry_type: Optional[str] = None - -class CardTypeDetail( - AppBaseModel, - IdentityMixin, - HrefMixin -): - """ Card Types are cards mobile or physical that are supported at a site - """ - name: str - minimum_number: Optional[str] = None - maximum_number: Optional[str] = None - initial_card_state: str - facility_code: str - credential_class: str - available_card_states: list[str] = None - default_expiry: Optional[CardExpiryType] = None - send_registration_email: Optional[bool] = False - send_registration_sms: Optional[bool] = False - -class CardTypeResponse( - AppBaseModel, -): - """ Card Types are cards mobile or physical that are supported at a site - """ - results: list[CardTypeDetail] - next: Optional[HrefMixin] = None diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py index c61a5414..f2af02b1 100644 --- a/gallagher/dto/cardholder.py +++ b/gallagher/dto/cardholder.py @@ -13,108 +13,3 @@ from .division import ( DivisionRef, ) - - -class CardholderRef( - AppBaseModel, - HrefMixin, -): - """ Reference to a Cardholder - """ - name: str - - -class CardholderSummary( - AppBaseModel, - IdentityMixin -): - """ - The cardholder search at /api/cardholders returns an array of these. - It is a subset of what you get from a cardholder's detail page at - /api/cardholders/{id} - - (linked as the href in this object), to be more suitable for large result sets. - """ - - first_name: str - last_name: str - short_name: Optional[str] = None - description: Optional[str] = None - authorised: bool - - def __rich_repr__(self): - return [ - self.id, - self.first_name, - self.last_name, - "yes" if self.authorised else "no" - ] - - async def __str__(self): - return f"{self.id} {self.first_name} {self.last_name}" - - -class CardholderDetail( - CardholderSummary -): - """ Displays a table of cardholders - - Gallagher command centre offers a summary of all the cardholders - provisioned on the system. This command presents a summary table - with the aim of using the identifier to get detailed information. - - """ - disable_cipher_pad: bool = False - division: DivisionRef - edit: HrefMixin - - operator_login_enabled: bool = False - operator_password_expired: bool = False - - update_location: HrefMixin - updates: HrefMixin - - user_extended_access_time: bool = False - windows_login_enabled: bool = False - - def __rich_repr__(self): - return [ - f"[blue bold] person", - f"{'id':>20} {self.id}", - f"{'first_name':>20} {self.first_name}", - f"{'last_name':>20} {self.last_name}", - f"{'short_name':>20} {self.short_name}", - f"{'description':>20} {self.description}", - f"{'authorised':>20} {'yes' if self.authorised else 'no'}", - f"", - f"{'disable_cipher_pad':>20} {'yes' if self.disable_cipher_pad else 'no'}", - f"{'division':>20} {self.division.id}", - f"[blue bold] hrefs", - f"{'edit':>20} [link={self.edit.href}]edit[/link]", - ] - - -class CardholderSummaryResponse( - AppBaseModel -): - """ Summary response for cardholder list and search - - /api/cardholders is generally the endpoint that - - """ - results: list[CardholderSummary] - - @property - def cli_header(self): - return [ - "Id", - "First name", - "Last name", - "Authorised" - ] - - def __rich_repr__(self): - return [r.__rich_repr__() for r in self.results] - - def __str__(self): - return f"{len(self.results)} cardholders" diff --git a/gallagher/dto/day_category.py b/gallagher/dto/day_category.py index 0dceb5e3..139597f9 100644 --- a/gallagher/dto/day_category.py +++ b/gallagher/dto/day_category.py @@ -1,45 +1,2 @@ -""" A day category links a calendar to a schedule. -The calendar determines the days of the year that fall into a day category, -and the schedule determines what happens at certain times on those days. -""" -from typing import Optional -from .utils import ( - AppBaseModel, - HrefMixin -) - - -class DayCategoryRef( - AppBaseModel, - HrefMixin, -): - """ A reference to a day category - - This is what is sent by the day_category endpoint as of v9 - the references can be used from other endpoints. - """ - - name: str - - -class DayCategory( - DayCategoryRef, -): - """ Represents a single entry from the response - """ - - name: str - description: Optional[str] - notes: Optional[str] - - -class DayCategoryResponse( - AppBaseModel, -): - """ The response has a list of results and a link to the next page - """ - - results: list[DayCategoryRef] - next: Optional[HrefMixin] = None diff --git a/gallagher/dto/detail/__init__.py b/gallagher/dto/detail/__init__.py new file mode 100644 index 00000000..70211236 --- /dev/null +++ b/gallagher/dto/detail/__init__.py @@ -0,0 +1,9 @@ +""" + + +""" + + +from .card_type import ( + CardTypeDetail, +) diff --git a/gallagher/dto/detail/access_group.py b/gallagher/dto/detail/access_group.py new file mode 100644 index 00000000..0d470974 --- /dev/null +++ b/gallagher/dto/detail/access_group.py @@ -0,0 +1,70 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + +from ..ref.access_group import ( + AccessGroupRef +) + +from ..ref.division import ( + DivisionRef +) + +from division import ( + DivisionDetail +) + +from ..ref.pdf import ( + PDFRef +) + + +class AccessGroupDetail( + AppBaseModel, + HrefMixin, +): + """ + """ + name: str + description: Optional[str] + parent: Optional[AccessGroupRef] + division: IdentityMixin + cardholders: Optional[HrefMixin] + server_display_name: Optional[str] + + description: Optional[str] + parent: Optional[AccessGroupRef] + division: DivisionDetail + cardholders: Optional[HrefMixin] + server_display_name: Optional[str] + children: list[AccessGroupRef] + personal_data_definitions: list[PDFRef] + + visitor: bool + escort_visitors: bool + lock_unlock_access_zones: bool + enter_during_lockdown: bool + first_card_unlock: bool + override_aperio_privacy: bool + aperio_offline_access: bool + disarm_alarm_zones: bool + arm_alarm_zones: bool + hvLf_fence_zones: bool + view_alarms: bool + shunt: bool + lock_out_fence_zones: bool + cancel_fence_zone_lockout: bool + ack_all: bool + ack_below_high: bool + select_alarm_zone: bool + arm_while_alarm: bool + arm_while_active_alarm: bool + isolate_alarm_zones: bool + + access: list[AccessSummary] + salto_access: list[SaltoAccessItemSummary] + alarm_zones: list[AlarmZoneSummary] diff --git a/gallagher/dto/detail/alarm.py b/gallagher/dto/detail/alarm.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/card_type.py b/gallagher/dto/detail/card_type.py new file mode 100644 index 00000000..0efb4861 --- /dev/null +++ b/gallagher/dto/detail/card_type.py @@ -0,0 +1,30 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + +from ..summary import ( + CardExpiryTypeSummary +) + + +class CardTypeDetail( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ Card Types are cards mobile or physical that are supported at a site + """ + name: str + minimum_number: Optional[str] = None + maximum_number: Optional[str] = None + initial_card_state: str + facility_code: str + credential_class: str + available_card_states: list[str] = None + default_expiry: Optional[CardExpiryTypeSummary] = None + send_registration_email: Optional[bool] = False + send_registration_sms: Optional[bool] = False diff --git a/gallagher/dto/detail/cardholder.py b/gallagher/dto/detail/cardholder.py new file mode 100644 index 00000000..f822f15a --- /dev/null +++ b/gallagher/dto/detail/cardholder.py @@ -0,0 +1,59 @@ +from typing import Optional +from datetime import datetime + +from .utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin, +) + +from .division import ( + DivisionRef, +) + + +class CardholderDetail( + AppBaseModel, + IdentityMixin, +): + """ Displays a table of cardholders + + Gallagher command centre offers a summary of all the cardholders + provisioned on the system. This command presents a summary table + with the aim of using the identifier to get detailed information. + + """ + first_name: str + last_name: str + short_name: Optional[str] = None + description: Optional[str] = None + authorised: bool + + disable_cipher_pad: bool = False + division: DivisionRef + edit: HrefMixin + + operator_login_enabled: bool = False + operator_password_expired: bool = False + + update_location: HrefMixin + updates: HrefMixin + + user_extended_access_time: bool = False + windows_login_enabled: bool = False + + def __rich_repr__(self): + return [ + f"[blue bold] person", + f"{'id':>20} {self.id}", + f"{'first_name':>20} {self.first_name}", + f"{'last_name':>20} {self.last_name}", + f"{'short_name':>20} {self.short_name}", + f"{'description':>20} {self.description}", + f"{'authorised':>20} {'yes' if self.authorised else 'no'}", + f"", + f"{'disable_cipher_pad':>20} {'yes' if self.disable_cipher_pad else 'no'}", + f"{'division':>20} {self.division.id}", + f"[blue bold] hrefs", + f"{'edit':>20} [link={self.edit.href}]edit[/link]", + ] diff --git a/gallagher/dto/detail/day_category.py b/gallagher/dto/detail/day_category.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/discover.py b/gallagher/dto/detail/discover.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/division.py b/gallagher/dto/detail/division.py new file mode 100644 index 00000000..fba21564 --- /dev/null +++ b/gallagher/dto/detail/division.py @@ -0,0 +1,25 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class DivisionDetail( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ + """ + + name: str + description: Optional[str] = None + server_display_name: Optional[str] = None + parent: Optional[HrefMixin] = None + + # TODO: Looks like we don't have access to visitor management + # on our test instance at the moment + # visitor_management: visitor.VisitorManagementSummary diff --git a/gallagher/dto/detail/door.py b/gallagher/dto/detail/door.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/event.py b/gallagher/dto/detail/event.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/features.py b/gallagher/dto/detail/features.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/items.py b/gallagher/dto/detail/items.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/locker.py b/gallagher/dto/detail/locker.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/operator.py b/gallagher/dto/detail/operator.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/pdf.py b/gallagher/dto/detail/pdf.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/reception.py b/gallagher/dto/detail/reception.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/role.py b/gallagher/dto/detail/role.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/salto.py b/gallagher/dto/detail/salto.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/schedule.py b/gallagher/dto/detail/schedule.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/utils.py b/gallagher/dto/detail/utils.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/visit.py b/gallagher/dto/detail/visit.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/visitor.py b/gallagher/dto/detail/visitor.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/detail/zone.py b/gallagher/dto/detail/zone.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/division.py b/gallagher/dto/division.py index abd703d9..aae2f487 100644 --- a/gallagher/dto/division.py +++ b/gallagher/dto/division.py @@ -11,43 +11,3 @@ ) # import visitor.VisitorManagementSummary - - -class DivisionRef( - AppBaseModel, - IdentityMixin, - HrefMixin -): - """ Division reference is used to link to a division - - The Mixins cover all the fields that are returned in the - summary, hence nothing has to be declared in the body - """ - pass - - -class DivisionDetail( - DivisionRef, -): - """ - """ - - name: str - description: Optional[str] = None - server_display_name: Optional[str] = None - parent: Optional[HrefMixin] = None - - # TODO: Looks like we don't have access to visitor management - # on our test instance at the moment - # visitor_management: visitor.VisitorManagementSummary - - -class DivisionDetailResponse( - AppBaseModel -): - """ Division - - """ - - results: list[DivisionDetail] - next: Optional[HrefMixin] = None diff --git a/gallagher/dto/ref/__init__.py b/gallagher/dto/ref/__init__.py new file mode 100644 index 00000000..5eb80ea8 --- /dev/null +++ b/gallagher/dto/ref/__init__.py @@ -0,0 +1,17 @@ +""" Reference classes + +Our design outlines three types of DTO classes, Refs are ones that are +used to reference other objects, think of them as interlinks between +objects. They usually contain a href and some identifying information +such as a name or id. + +This package was introduced in reference to this issue +https://github.com/anomaly/gallagher/issues/21 + +which identified race conditions with circular imports. This is caused +mostly because of the nature of the data that the command centre exposes. +""" + +from .access_group import ( + AccessGroupRef +) diff --git a/gallagher/dto/ref/access_group.py b/gallagher/dto/ref/access_group.py new file mode 100644 index 00000000..b198cc62 --- /dev/null +++ b/gallagher/dto/ref/access_group.py @@ -0,0 +1,13 @@ +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class AccessGroupRef( + AppBaseModel, + HrefMixin +): + """ Access Groups is what a user is assigned to to provide access to doors + """ + name: str diff --git a/gallagher/dto/ref/alarm.py b/gallagher/dto/ref/alarm.py new file mode 100644 index 00000000..6241f051 --- /dev/null +++ b/gallagher/dto/ref/alarm.py @@ -0,0 +1,22 @@ +from ..utils import ( + AppBaseModel, + HrefMixin, +) + + +class AlarmRef( + AppBaseModel, + HrefMixin +): + """ AlarmRef represents a single alarm + """ + state: str + + +class AlarmZoneRef( + AppBaseModel, + HrefMixin +): + """ AccessZone represents + """ + name: str diff --git a/gallagher/dto/ref/card_type.py b/gallagher/dto/ref/card_type.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/cardholder.py b/gallagher/dto/ref/cardholder.py new file mode 100644 index 00000000..07971ca6 --- /dev/null +++ b/gallagher/dto/ref/cardholder.py @@ -0,0 +1,13 @@ +from ..utils import ( + AppBaseModel, + HrefMixin, +) + + +class CardholderRef( + AppBaseModel, + HrefMixin, +): + """ Reference to a Cardholder + """ + name: str diff --git a/gallagher/dto/ref/day_category.py b/gallagher/dto/ref/day_category.py new file mode 100644 index 00000000..9f3a4cdf --- /dev/null +++ b/gallagher/dto/ref/day_category.py @@ -0,0 +1,22 @@ +""" A day category links a calendar to a schedule. + +The calendar determines the days of the year that fall into a day category, +and the schedule determines what happens at certain times on those days. +""" +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class DayCategoryRef( + AppBaseModel, + HrefMixin, +): + """ A reference to a day category + + This is what is sent by the day_category endpoint as of v9 + the references can be used from other endpoints. + """ + + name: str diff --git a/gallagher/dto/ref/discover.py b/gallagher/dto/ref/discover.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/division.py b/gallagher/dto/ref/division.py new file mode 100644 index 00000000..10a33d17 --- /dev/null +++ b/gallagher/dto/ref/division.py @@ -0,0 +1,18 @@ +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class DivisionRef( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ Division reference is used to link to a division + + The Mixins cover all the fields that are returned in the + summary, hence nothing has to be declared in the body + """ + pass diff --git a/gallagher/dto/ref/door.py b/gallagher/dto/ref/door.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/features.py b/gallagher/dto/ref/features.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/items.py b/gallagher/dto/ref/items.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/locker.py b/gallagher/dto/ref/locker.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/operator.py b/gallagher/dto/ref/operator.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/pdf.py b/gallagher/dto/ref/pdf.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/reception.py b/gallagher/dto/ref/reception.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/role.py b/gallagher/dto/ref/role.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/salto.py b/gallagher/dto/ref/salto.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/schedule.py b/gallagher/dto/ref/schedule.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/utils.py b/gallagher/dto/ref/utils.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/visit.py b/gallagher/dto/ref/visit.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/visitor.py b/gallagher/dto/ref/visitor.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/ref/zone.py b/gallagher/dto/ref/zone.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/__init__.py b/gallagher/dto/response/__init__.py new file mode 100644 index 00000000..5af30183 --- /dev/null +++ b/gallagher/dto/response/__init__.py @@ -0,0 +1,4 @@ +""" + + +""" diff --git a/gallagher/dto/response/access_group.py b/gallagher/dto/response/access_group.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/alarm.py b/gallagher/dto/response/alarm.py new file mode 100644 index 00000000..2afc3424 --- /dev/null +++ b/gallagher/dto/response/alarm.py @@ -0,0 +1,17 @@ +from ..utils import ( + AppBaseModel, + HrefMixin, +) + +from ..summary import ( + AlarmZoneSummary, +) + + +class AlarmResponse( + AppBaseModel, +): + """ AlarmResponse represents a single alarm + """ + alarms: list[AlarmZoneSummary] + updates: HrefMixin diff --git a/gallagher/dto/response/card_type.py b/gallagher/dto/response/card_type.py new file mode 100644 index 00000000..bca86f5c --- /dev/null +++ b/gallagher/dto/response/card_type.py @@ -0,0 +1,19 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin +) + +from ..detail import ( + CardTypeDEtail +) + + +class CardTypeResponse( + AppBaseModel, +): + """ Card Types are cards mobile or physical that are supported at a site + """ + results: list[CardTypeDetail] + next: Optional[HrefMixin] = None diff --git a/gallagher/dto/response/cardholder.py b/gallagher/dto/response/cardholder.py new file mode 100644 index 00000000..8f223c11 --- /dev/null +++ b/gallagher/dto/response/cardholder.py @@ -0,0 +1,33 @@ +from ..utils import ( + AppBaseModel, +) + +from ..summary import ( + CardholderSummary +) + + +class CardholderSummaryResponse( + AppBaseModel +): + """ Summary response for cardholder list and search + + /api/cardholders is generally the endpoint that + + """ + results: list[CardholderSummary] + + @property + def cli_header(self): + return [ + "Id", + "First name", + "Last name", + "Authorised" + ] + + def __rich_repr__(self): + return [r.__rich_repr__() for r in self.results] + + def __str__(self): + return f"{len(self.results)} cardholders" diff --git a/gallagher/dto/response/day_category.py b/gallagher/dto/response/day_category.py new file mode 100644 index 00000000..0fa10138 --- /dev/null +++ b/gallagher/dto/response/day_category.py @@ -0,0 +1,20 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin +) + +from ..ref import ( + DayCategoryRef, +) + + +class DayCategoryResponse( + AppBaseModel, +): + """ The response has a list of results and a link to the next page + """ + + results: list[DayCategoryRef] + next: Optional[HrefMixin] = None diff --git a/gallagher/dto/response/discover.py b/gallagher/dto/response/discover.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/division.py b/gallagher/dto/response/division.py new file mode 100644 index 00000000..37eec892 --- /dev/null +++ b/gallagher/dto/response/division.py @@ -0,0 +1,21 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin, +) + +from ..detail import ( + DivisionDetail +) + + +class DivisionDetailResponse( + AppBaseModel +): + """ Division + + """ + + results: list[DivisionDetail] + next: Optional[HrefMixin] = None diff --git a/gallagher/dto/response/door.py b/gallagher/dto/response/door.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/features.py b/gallagher/dto/response/features.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/items.py b/gallagher/dto/response/items.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/locker.py b/gallagher/dto/response/locker.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/operator.py b/gallagher/dto/response/operator.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/pdf.py b/gallagher/dto/response/pdf.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/reception.py b/gallagher/dto/response/reception.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/role.py b/gallagher/dto/response/role.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/salto.py b/gallagher/dto/response/salto.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/schedule.py b/gallagher/dto/response/schedule.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/utils.py b/gallagher/dto/response/utils.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/visit.py b/gallagher/dto/response/visit.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/visitor.py b/gallagher/dto/response/visitor.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/response/zone.py b/gallagher/dto/response/zone.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/__init__.py b/gallagher/dto/summary/__init__.py new file mode 100644 index 00000000..5af30183 --- /dev/null +++ b/gallagher/dto/summary/__init__.py @@ -0,0 +1,4 @@ +""" + + +""" diff --git a/gallagher/dto/summary/access_group.py b/gallagher/dto/summary/access_group.py new file mode 100644 index 00000000..0a1412a1 --- /dev/null +++ b/gallagher/dto/summary/access_group.py @@ -0,0 +1,38 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + +from ..ref.access_group import ( + AccessGroupRef +) + + +class AccessSummary( + AppBaseModel, + HrefMixin, +): + """ Access is zone paired with a schedule + """ + access_zone: AccessZoneRef + schedule: ScheduleRef + + +class AccessGroupSummary( + AppBaseModel, +): + """ AccessGroup Summary is what the API returns on searches + + This builds on the Ref class to add the summary fields and is + extended by the Detail class to add the fully remainder of + the fields + """ + name: str + description: Optional[str] + parent: Optional[AccessGroupRef] + division: IdentityMixin + cardholders: Optional[HrefMixin] + server_display_name: Optional[str] diff --git a/gallagher/dto/summary/alarm.py b/gallagher/dto/summary/alarm.py new file mode 100644 index 00000000..ae181918 --- /dev/null +++ b/gallagher/dto/summary/alarm.py @@ -0,0 +1,52 @@ +from typing import Optional + +from .utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + +from .event import ( + EventTypeSummary +) + + +class AlarmSourceSummary( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ AlarmSource represents a device that has triggered an alarm + """ + name: str + + +class AlarmZoneSummary( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ #TODO: Revise this if it shows up in other places + + I have literally named this class to model the alarm_zones + property in the access_group schema. I don't know if this + is appropriate + """ + time: str + message: str + source: AlarmSourceSummary + type: str + event_type: Optional[EventTypeSummary] = None + priority: int + state: str + active: bool + division: HrefMixin + event: Optional[HrefMixin] = None + note_presets: list[str] = [] + view: HrefMixin + comment: HrefMixin + acknowledge: Optional[HrefMixin] = None + acknowledge_with_comment: Optional[HrefMixin] = None + process: Optional[HrefMixin] = None + process_with_comment: Optional[HrefMixin] = None + force_process: Optional[HrefMixin] = None diff --git a/gallagher/dto/summary/card_type.py b/gallagher/dto/summary/card_type.py new file mode 100644 index 00000000..f93c16b1 --- /dev/null +++ b/gallagher/dto/summary/card_type.py @@ -0,0 +1,13 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class CardExpiryTypeSummary( + AppBaseModel +): + expiry_type: Optional[str] = None diff --git a/gallagher/dto/summary/cardholder.py b/gallagher/dto/summary/cardholder.py new file mode 100644 index 00000000..a646c6de --- /dev/null +++ b/gallagher/dto/summary/cardholder.py @@ -0,0 +1,36 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, +) + + +class CardholderSummary( + AppBaseModel, + IdentityMixin +): + """ + The cardholder search at /api/cardholders returns an array of these. + It is a subset of what you get from a cardholder's detail page at + /api/cardholders/{id} + + (linked as the href in this object), to be more suitable for large result sets. + """ + + first_name: str + last_name: str + short_name: Optional[str] = None + description: Optional[str] = None + authorised: bool + + def __rich_repr__(self): + return [ + self.id, + self.first_name, + self.last_name, + "yes" if self.authorised else "no" + ] + + def __str__(self): + return f"{self.id} {self.first_name} {self.last_name}" diff --git a/gallagher/dto/summary/day_category.py b/gallagher/dto/summary/day_category.py new file mode 100644 index 00000000..7747d2c7 --- /dev/null +++ b/gallagher/dto/summary/day_category.py @@ -0,0 +1,23 @@ +""" A day category links a calendar to a schedule. + +The calendar determines the days of the year that fall into a day category, +and the schedule determines what happens at certain times on those days. +""" +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class DayCategorySummary( + AppBaseModel, + HrefMixin, +): + """ Represents a single entry from the response + """ + + name: str + description: Optional[str] + notes: Optional[str] diff --git a/gallagher/dto/summary/discover.py b/gallagher/dto/summary/discover.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/division.py b/gallagher/dto/summary/division.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/door.py b/gallagher/dto/summary/door.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/features.py b/gallagher/dto/summary/features.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/locker.py b/gallagher/dto/summary/locker.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/operator.py b/gallagher/dto/summary/operator.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/pdf.py b/gallagher/dto/summary/pdf.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/reception.py b/gallagher/dto/summary/reception.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/role.py b/gallagher/dto/summary/role.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/salto.py b/gallagher/dto/summary/salto.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/schedule.py b/gallagher/dto/summary/schedule.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/utils.py b/gallagher/dto/summary/utils.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/visit.py b/gallagher/dto/summary/visit.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/visitor.py b/gallagher/dto/summary/visitor.py new file mode 100644 index 00000000..e69de29b diff --git a/gallagher/dto/summary/zone.py b/gallagher/dto/summary/zone.py new file mode 100644 index 00000000..e69de29b From 4fbb17f112c94e2bfa4ebfb1fe8badc6c176678b Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 13:01:05 +1100 Subject: [PATCH 066/118] docs: adds placeholder for cli --- docs/docs/cli.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/docs/cli.md diff --git a/docs/docs/cli.md b/docs/docs/cli.md new file mode 100644 index 00000000..93c42785 --- /dev/null +++ b/docs/docs/cli.md @@ -0,0 +1 @@ +# Command Line Interface From 3d4985dca57158179b09cae428b14043d193999e Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 13:58:14 +1100 Subject: [PATCH 067/118] feat: finishes separating dto classes --- gallagher/dto/detail/items.py | 3 ++ gallagher/dto/detail/pdf.py | 19 +++++++ gallagher/dto/detail/role.py | 24 +++++++++ gallagher/dto/door.py | 30 ----------- gallagher/dto/event.py | 87 ------------------------------ gallagher/dto/items.py | 65 ---------------------- gallagher/dto/pdf.py | 19 +------ gallagher/dto/ref/door.py | 14 +++++ gallagher/dto/ref/event.py | 15 ++++++ gallagher/dto/ref/items.py | 14 +++++ gallagher/dto/ref/pdf.py | 13 +++++ gallagher/dto/ref/salto.py | 25 +++++++++ gallagher/dto/ref/schedule.py | 17 ++++++ gallagher/dto/ref/zone.py | 13 +++++ gallagher/dto/response/door.py | 12 +++++ gallagher/dto/response/event.py | 52 ++++++++++++++++++ gallagher/dto/response/items.py | 32 +++++++++++ gallagher/dto/response/schedule.py | 15 ++++++ gallagher/dto/role.py | 13 ----- gallagher/dto/salto.py | 25 --------- gallagher/dto/schedule.py | 24 --------- gallagher/dto/summary/door.py | 16 ++++++ gallagher/dto/summary/event.py | 44 +++++++++++++++ gallagher/dto/summary/items.py | 31 +++++++++++ gallagher/dto/summary/salto.py | 20 +++++++ gallagher/dto/summary/schedule.py | 13 +++++ gallagher/dto/summary/visitor.py | 39 ++++++++++++++ gallagher/dto/visitor.py | 23 -------- gallagher/dto/zone.py | 14 ----- 29 files changed, 432 insertions(+), 299 deletions(-) diff --git a/gallagher/dto/detail/items.py b/gallagher/dto/detail/items.py index e69de29b..9f490728 100644 --- a/gallagher/dto/detail/items.py +++ b/gallagher/dto/detail/items.py @@ -0,0 +1,3 @@ +from ..utils import ( + AppBaseModel, +) diff --git a/gallagher/dto/detail/pdf.py b/gallagher/dto/detail/pdf.py index e69de29b..abd10a0a 100644 --- a/gallagher/dto/detail/pdf.py +++ b/gallagher/dto/detail/pdf.py @@ -0,0 +1,19 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin, +) + + +class PDFDetail( + AppBaseModel, + HrefMixin +): + """ Personal Data Fields are custom fields for a card holder + """ + name: str + description: Optional[str] + division: IdentityMixin + server_display_name: Optional[str] diff --git a/gallagher/dto/detail/role.py b/gallagher/dto/detail/role.py index e69de29b..91fd587d 100644 --- a/gallagher/dto/detail/role.py +++ b/gallagher/dto/detail/role.py @@ -0,0 +1,24 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + +from ..ref import ( + DivisionRef +) + + +class RoleDetail( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ + """ + name: str + server_display_name: str + description: Optional[str] + division: DivisionRef diff --git a/gallagher/dto/door.py b/gallagher/dto/door.py index 91edab1c..06a6bd7c 100644 --- a/gallagher/dto/door.py +++ b/gallagher/dto/door.py @@ -8,33 +8,3 @@ IdentityMixin, HrefMixin ) - - -class DoorRef( - AppBaseModel, - HrefMixin -): - """ Door - - """ - name: str - - -class DoorSummary( - AppBaseModel, - IdentityMixin, - HrefMixin, -): - """ - - """ - name: str - - -class DoorSummaryResponse( - AppBaseModel -): - """ - - """ - results: list[DoorSummary] diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py index 7fb9f112..31a6d234 100644 --- a/gallagher/dto/event.py +++ b/gallagher/dto/event.py @@ -13,90 +13,3 @@ # from ..dto.cardholder import CardholderRef # from ..dto.access_group import AccessGroupRef # from ..dto.door import DoorRef - - -class EventTypeSummary( - AppBaseModel, - IdentityMixin, -): - """ An event type has identifiers and names - """ - name: str - - -class EventSummary( - AppBaseModel, - IdentityMixin, - HrefMixin -): - """ Events that the Command Centre generates - - """ - server_display_name: str - time: str - message: Optional[str] - occurrences: int - priority: int - - # Hrefs to follows other events - next: HrefMixin - previous: HrefMixin - updates: HrefMixin - - -class EventGroupSummary( - AppBaseModel, - IdentityMixin, -): - """ Event Groups are a collection of event types - - Each group has names and event types. This is usually used - in an Event Type Response. - """ - name: str - event_types: list[EventTypeSummary] - - -class EventSummaryResponse( - AppBaseModel, - HrefMixin, - IdentityMixin, -): - """ - """ - server_display_name: str - time: str - message: Optional[str] - occurrences: int - priority: int - # alarm: AlarmRef - - # operator: CardholderRef - # source: str - # group: str - # type: str - # event_type: EventTypeSummary - # division: str - # cardholder: str - # entry_access_zone: str - # exit_access_zone: str - # door: DoorRef - # access_group: HrefMixin - # card: str - # modified_item: str - - next: HrefMixin - previous: HrefMixin - updates: HrefMixin - - -class EventTypeResponse( - AppBaseModel, -): - """ Event Type Response - - Event Type Responses return a set of eventGroups which in turn - has identifiers, names and event types. - - """ - event_groups: list[EventGroupSummary] diff --git a/gallagher/dto/items.py b/gallagher/dto/items.py index 48716f84..a0d6dd25 100644 --- a/gallagher/dto/items.py +++ b/gallagher/dto/items.py @@ -12,68 +12,3 @@ ) from .division import DivisionRef - - -class ItemRef( - AppBaseModel, - HrefMixin -): - """ Reference to an ItemType - """ - name: str - - -class ItemTypeDetail( - AppBaseModel, -): - """ Items Types only provide the name and id - - This is used by the discovery endpoint and is used by - the ItemTypesResponse - """ - id: str - name: str - canonical_type_name: str - - -class ItemSummary( - ItemRef, - IdentityMixin, -): - """ Summary of an Item which adds the notes and - server_display_name, this is used by the item summary response - """ - type: ItemTypeDetail - - -class ItemDetail( - ItemSummary, -): - """ All attributes of the Summary plus the division - - While running our tests we found that for some system level - objects the divison can be optional, this attribute is hence - marked optional, please test for availability before using it. - """ - division: Optional[DivisionRef] = None - - -class ItemsSummaryResponse( - AppBaseModel, -): - """ ItemsResponse is the list of items from the API - it provides the summary of all Items Summary - """ - results: list[ItemSummary] - next: Optional[HrefMixin] = None - - -class ItemTypesResponse( - AppBaseModel, -): - """ Every security centre can provide a list of item types - - While the response is rather abridged, this is the detail form - of the Item Types. - """ - item_types: list[ItemTypeDetail] diff --git a/gallagher/dto/pdf.py b/gallagher/dto/pdf.py index c1d9f58f..eadd38bb 100644 --- a/gallagher/dto/pdf.py +++ b/gallagher/dto/pdf.py @@ -4,22 +4,5 @@ from typing import Optional -from .utils import AppBaseModel, IdentityMixin,\ +from .utils import AppBaseModel, IdentityMixin, \ HrefMixin - -class PDFRef( - AppBaseModel, - HrefMixin -): - """ Personal Data Fields are custom fields for a card holder - """ - name: str - -class PDFDetail( - PDFRef -): - """ Personal Data Fields are custom fields for a card holder - """ - description: Optional[str] - division: IdentityMixin - server_display_name: Optional[str] diff --git a/gallagher/dto/ref/door.py b/gallagher/dto/ref/door.py index e69de29b..ad91b6d3 100644 --- a/gallagher/dto/ref/door.py +++ b/gallagher/dto/ref/door.py @@ -0,0 +1,14 @@ +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class DoorRef( + AppBaseModel, + HrefMixin +): + """ Door + + """ + name: str diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py index e69de29b..12f47841 100644 --- a/gallagher/dto/ref/event.py +++ b/gallagher/dto/ref/event.py @@ -0,0 +1,15 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + IdentityMixin, +) + + +class EventTypeSummary( + AppBaseModel, + IdentityMixin, +): + """ An event type has identifiers and names + """ + name: str diff --git a/gallagher/dto/ref/items.py b/gallagher/dto/ref/items.py index e69de29b..78eb5186 100644 --- a/gallagher/dto/ref/items.py +++ b/gallagher/dto/ref/items.py @@ -0,0 +1,14 @@ + +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class ItemRef( + AppBaseModel, + HrefMixin +): + """ Reference to an ItemType + """ + name: str diff --git a/gallagher/dto/ref/pdf.py b/gallagher/dto/ref/pdf.py index e69de29b..a887608b 100644 --- a/gallagher/dto/ref/pdf.py +++ b/gallagher/dto/ref/pdf.py @@ -0,0 +1,13 @@ +from ..utils import ( + AppBaseModel, + HrefMixin, +) + + +class PDFRef( + AppBaseModel, + HrefMixin +): + """ Personal Data Fields are custom fields for a card holder + """ + name: str diff --git a/gallagher/dto/ref/salto.py b/gallagher/dto/ref/salto.py index e69de29b..f4a80c0d 100644 --- a/gallagher/dto/ref/salto.py +++ b/gallagher/dto/ref/salto.py @@ -0,0 +1,25 @@ +from ..utils import ( + AppBaseModel, + HrefMixin +) + +from ..ref import ( + ScheduleRef +) + + +class SaltoItemTypeRef( + AppBaseModel, +): + """ A Salto Item Type is a SALTO product category e.g door + """ + value: str + + +class SaltoItemRef( + AppBaseModel, + HrefMixin +): + """ A Salto Item is a particular product e.g Salto CU5000 + """ + name: str diff --git a/gallagher/dto/ref/schedule.py b/gallagher/dto/ref/schedule.py index e69de29b..a81daa36 100644 --- a/gallagher/dto/ref/schedule.py +++ b/gallagher/dto/ref/schedule.py @@ -0,0 +1,17 @@ +""" Schedules + +""" + +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class ScheduleRef( + AppBaseModel, + HrefMixin +): + """ Schedule is a time + """ + pass diff --git a/gallagher/dto/ref/zone.py b/gallagher/dto/ref/zone.py index e69de29b..97485a14 100644 --- a/gallagher/dto/ref/zone.py +++ b/gallagher/dto/ref/zone.py @@ -0,0 +1,13 @@ +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class AccessZoneRef( + AppBaseModel, + HrefMixin +): + """ AccessZone represents + """ + name: str diff --git a/gallagher/dto/response/door.py b/gallagher/dto/response/door.py index e69de29b..926524c3 100644 --- a/gallagher/dto/response/door.py +++ b/gallagher/dto/response/door.py @@ -0,0 +1,12 @@ +from ..utils import ( + AppBaseModel, +) + + +class DoorSummaryResponse( + AppBaseModel +): + """ + + """ + results: list[DoorSummary] diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index e69de29b..c2fd4457 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -0,0 +1,52 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + + +class EventSummaryResponse( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ + """ + server_display_name: str + time: str + message: Optional[str] + occurrences: int + priority: int + # alarm: AlarmRef + + # operator: CardholderRef + # source: str + # group: str + # type: str + # event_type: EventTypeSummary + # division: str + # cardholder: str + # entry_access_zone: str + # exit_access_zone: str + # door: DoorRef + # access_group: HrefMixin + # card: str + # modified_item: str + + next: HrefMixin + previous: HrefMixin + updates: HrefMixin + + +class EventTypeResponse( + AppBaseModel, +): + """ Event Type Response + + Event Type Responses return a set of eventGroups which in turn + has identifiers, names and event types. + + """ + event_groups: list[EventGroupSummary] diff --git a/gallagher/dto/response/items.py b/gallagher/dto/response/items.py index e69de29b..159cd7d8 100644 --- a/gallagher/dto/response/items.py +++ b/gallagher/dto/response/items.py @@ -0,0 +1,32 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin +) + +from ..summary import ( + ItemSummary, + ItemTypeSummary +) + + +class ItemsSummaryResponse( + AppBaseModel, +): + """ ItemsResponse is the list of items from the API + it provides the summary of all Items Summary + """ + results: list[ItemSummary] + next: Optional[HrefMixin] = None + + +class ItemTypesResponse( + AppBaseModel, +): + """ Every security centre can provide a list of item types + + While the response is rather abridged, this is the detail form + of the Item Types. + """ + item_types: list[ItemTypeSummary] diff --git a/gallagher/dto/response/schedule.py b/gallagher/dto/response/schedule.py index e69de29b..e936316d 100644 --- a/gallagher/dto/response/schedule.py +++ b/gallagher/dto/response/schedule.py @@ -0,0 +1,15 @@ +from ..utils import ( + AppBaseModel, +) + +from ..ref import ( + ScheduleSummary +) + + +class ScheduleSummaryResponse( + AppBaseModel +): + """ Schedule is a time + """ + results: list[ScheduleSummary] diff --git a/gallagher/dto/role.py b/gallagher/dto/role.py index c79c0c49..4ed6c369 100644 --- a/gallagher/dto/role.py +++ b/gallagher/dto/role.py @@ -10,16 +10,3 @@ ) from .division import DivisionRef - -class RoleDetail( - AppBaseModel, - IdentityMixin, - HrefMixin -): - """ - """ - name: str - server_display_name: str - description: Optional[str] - division: DivisionRef - diff --git a/gallagher/dto/salto.py b/gallagher/dto/salto.py index bb7633c7..01bde57d 100644 --- a/gallagher/dto/salto.py +++ b/gallagher/dto/salto.py @@ -9,28 +9,3 @@ ) from .schedule import ScheduleRef - -class SaltoItemTypeRef( - AppBaseModel, -): - """ A Salto Item Type is a SALTO product category e.g door - """ - value: str - -class SaltoItemRef( - AppBaseModel, - HrefMixin -): - """ A Salto Item is a particular product e.g Salto CU5000 - """ - name: str - -class SaltoAccessItemSummary( - AppBaseModel, -): - """ A Summary of Salto items - - """ - salto_item_type: SaltoItemTypeRef - salto_item: SaltoItemRef - schedule: ScheduleRef \ No newline at end of file diff --git a/gallagher/dto/schedule.py b/gallagher/dto/schedule.py index 5f8263c4..7be108d4 100644 --- a/gallagher/dto/schedule.py +++ b/gallagher/dto/schedule.py @@ -6,27 +6,3 @@ AppBaseModel, HrefMixin ) - -class ScheduleRef( - AppBaseModel, - HrefMixin -): - """ Schedule is a time - """ - pass - - -class ScheduleSummary( - ScheduleRef -): - """ Schedule is a time - """ - name: str - - -class ScheduleSummaryResponse( - AppBaseModel -): - """ Schedule is a time - """ - results: list[ScheduleSummary] diff --git a/gallagher/dto/summary/door.py b/gallagher/dto/summary/door.py index e69de29b..4037180e 100644 --- a/gallagher/dto/summary/door.py +++ b/gallagher/dto/summary/door.py @@ -0,0 +1,16 @@ +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class DoorSummary( + AppBaseModel, + IdentityMixin, + HrefMixin, +): + """ + + """ + name: str diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index e69de29b..276018b2 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -0,0 +1,44 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + +from ..summary import ( + EventTypeSummary +) + + +class EventSummary( + AppBaseModel, + IdentityMixin, + HrefMixin +): + """ Events that the Command Centre generates + + """ + server_display_name: str + time: str + message: Optional[str] + occurrences: int + priority: int + + # Hrefs to follows other events + next: HrefMixin + previous: HrefMixin + updates: HrefMixin + + +class EventGroupSummary( + AppBaseModel, + IdentityMixin, +): + """ Event Groups are a collection of event types + + Each group has names and event types. This is usually used + in an Event Type Response. + """ + name: str + event_types: list[EventTypeSummary] diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py index e69de29b..c5ad5425 100644 --- a/gallagher/dto/summary/items.py +++ b/gallagher/dto/summary/items.py @@ -0,0 +1,31 @@ + +from ..utils import ( + AppBaseModel, + IdentityMixin, + HrefMixin +) + + +class ItemTypeDetail( + AppBaseModel, +): + """ Items Types only provide the name and id + + This is used by the discovery endpoint and is used by + the ItemTypesResponse + """ + id: str + name: str + canonical_type_name: str + + +class ItemSummary( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ Summary of an Item which adds the notes and + server_display_name, this is used by the item summary response + """ + name: str + type: ItemTypeDetail diff --git a/gallagher/dto/summary/salto.py b/gallagher/dto/summary/salto.py index e69de29b..4e204e62 100644 --- a/gallagher/dto/summary/salto.py +++ b/gallagher/dto/summary/salto.py @@ -0,0 +1,20 @@ +from ..utils import ( + AppBaseModel, +) + +from ..ref import ( + ScheduleRef, + SaltoItemRef, + SaltoItemTypeRef, +) + + +class SaltoAccessItemSummary( + AppBaseModel, +): + """ A Summary of Salto items + + """ + salto_item_type: SaltoItemTypeRef + salto_item: SaltoItemRef + schedule: ScheduleRef diff --git a/gallagher/dto/summary/schedule.py b/gallagher/dto/summary/schedule.py index e69de29b..eef9257e 100644 --- a/gallagher/dto/summary/schedule.py +++ b/gallagher/dto/summary/schedule.py @@ -0,0 +1,13 @@ +from ..utils import ( + AppBaseModel, + HrefMixin +) + + +class ScheduleSummary( + AppBaseModel, + HrefMixin +): + """ Schedule is a time + """ + name: str diff --git a/gallagher/dto/summary/visitor.py b/gallagher/dto/summary/visitor.py index e69de29b..b5ba9589 100644 --- a/gallagher/dto/summary/visitor.py +++ b/gallagher/dto/summary/visitor.py @@ -0,0 +1,39 @@ +from .utils import ( + AppBaseModel, + IdentityMixin +) + +from ..ref import ( + AccessGroupRef +) + +from ..summary import ( + AccessGroupSummary, + VisitorTypeSummary, +) + + +class VisitorTypeSummary( + AppBaseModel, + IdentityMixin +): + """ Visitor Types are a combination of: + - Access Group + - Host Access Groups + - Visitor Access Groups + + these are represented in Divisions + """ + access_group: AccessGroupRef + host_access_groups: list[AccessGroupSummary] + visitor_access_groups: list[AccessGroupSummary] + + +class VisitorManagementSummary( + AppBaseModel +): + """ This is the summary of the Visitor Management that appears + in Divisions. + """ + active: bool + visitor_types: list[VisitorTypeSummary] diff --git a/gallagher/dto/visitor.py b/gallagher/dto/visitor.py index 040507ba..01d89eac 100644 --- a/gallagher/dto/visitor.py +++ b/gallagher/dto/visitor.py @@ -9,26 +9,3 @@ AccessGroupRef, AccessGroupSummary ) -class VisitorTypeSummary( - AppBaseModel, - IdentityMixin -): - """ Visitor Types are a combination of: - - Access Group - - Host Access Groups - - Visitor Access Groups - - these are represented in Divisions - """ - access_group : AccessGroupRef - host_access_groups: list[AccessGroupSummary] - visitor_access_groups: list[AccessGroupSummary] - -class VisitorManagementSummary( - AppBaseModel -): - """ This is the summary of the Visitor Management that appears - in Divisions. - """ - active: bool - visitor_types: list[VisitorTypeSummary] diff --git a/gallagher/dto/zone.py b/gallagher/dto/zone.py index fd3f6b54..5af30183 100644 --- a/gallagher/dto/zone.py +++ b/gallagher/dto/zone.py @@ -2,17 +2,3 @@ """ - -from .utils import ( - AppBaseModel, - HrefMixin -) - -class AccessZoneRef( - AppBaseModel, - HrefMixin -): - """ AccessZone represents - """ - name: str - From 15e785fda7b72efc6bf703dd0cc1352ab2efc0ce Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 14:08:34 +1100 Subject: [PATCH 068/118] refactor: drops original dto files REFS #21 --- gallagher/dto/access_group.py | 15 --------------- gallagher/dto/alarm.py | 13 ------------- gallagher/dto/card_type.py | 12 ------------ gallagher/dto/cardholder.py | 15 --------------- gallagher/dto/day_category.py | 2 -- gallagher/dto/division.py | 13 ------------- gallagher/dto/door.py | 10 ---------- gallagher/dto/event.py | 15 --------------- gallagher/dto/features.py | 11 ----------- gallagher/dto/items.py | 14 -------------- gallagher/dto/locker.py | 11 ----------- gallagher/dto/operator.py | 11 ----------- gallagher/dto/pdf.py | 8 -------- gallagher/dto/reception.py | 11 ----------- gallagher/dto/role.py | 12 ------------ gallagher/dto/salto.py | 11 ----------- gallagher/dto/schedule.py | 8 -------- gallagher/dto/visit.py | 10 ---------- gallagher/dto/visitor.py | 11 ----------- gallagher/dto/zone.py | 4 ---- 20 files changed, 217 deletions(-) delete mode 100644 gallagher/dto/access_group.py delete mode 100644 gallagher/dto/alarm.py delete mode 100644 gallagher/dto/card_type.py delete mode 100644 gallagher/dto/cardholder.py delete mode 100644 gallagher/dto/day_category.py delete mode 100644 gallagher/dto/division.py delete mode 100644 gallagher/dto/door.py delete mode 100644 gallagher/dto/event.py delete mode 100644 gallagher/dto/features.py delete mode 100644 gallagher/dto/items.py delete mode 100644 gallagher/dto/locker.py delete mode 100644 gallagher/dto/operator.py delete mode 100644 gallagher/dto/pdf.py delete mode 100644 gallagher/dto/reception.py delete mode 100644 gallagher/dto/role.py delete mode 100644 gallagher/dto/salto.py delete mode 100644 gallagher/dto/schedule.py delete mode 100644 gallagher/dto/visit.py delete mode 100644 gallagher/dto/visitor.py delete mode 100644 gallagher/dto/zone.py diff --git a/gallagher/dto/access_group.py b/gallagher/dto/access_group.py deleted file mode 100644 index 1f2e4dbe..00000000 --- a/gallagher/dto/access_group.py +++ /dev/null @@ -1,15 +0,0 @@ - -from typing import Optional - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) - -from .division import DivisionDetail -from .pdf import PDFRef -from .schedule import ScheduleRef -from .zone import AccessZoneRef -from .salto import SaltoAccessItemSummary -from .alarm import AlarmZoneSummary diff --git a/gallagher/dto/alarm.py b/gallagher/dto/alarm.py deleted file mode 100644 index e61d56a1..00000000 --- a/gallagher/dto/alarm.py +++ /dev/null @@ -1,13 +0,0 @@ -""" - -""" - -from typing import Optional - -from .utils import ( - AppBaseModel, - HrefMixin, - IdentityMixin, -) - -from .event import EventTypeSummary diff --git a/gallagher/dto/card_type.py b/gallagher/dto/card_type.py deleted file mode 100644 index a26f9177..00000000 --- a/gallagher/dto/card_type.py +++ /dev/null @@ -1,12 +0,0 @@ -""" - - -""" - -from typing import Optional - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/cardholder.py b/gallagher/dto/cardholder.py deleted file mode 100644 index f2af02b1..00000000 --- a/gallagher/dto/cardholder.py +++ /dev/null @@ -1,15 +0,0 @@ -""" - -""" -from typing import Optional -from datetime import datetime - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin, -) - -from .division import ( - DivisionRef, -) diff --git a/gallagher/dto/day_category.py b/gallagher/dto/day_category.py deleted file mode 100644 index 139597f9..00000000 --- a/gallagher/dto/day_category.py +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/gallagher/dto/division.py b/gallagher/dto/division.py deleted file mode 100644 index aae2f487..00000000 --- a/gallagher/dto/division.py +++ /dev/null @@ -1,13 +0,0 @@ -""" - -""" - -from typing import Optional - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) - -# import visitor.VisitorManagementSummary diff --git a/gallagher/dto/door.py b/gallagher/dto/door.py deleted file mode 100644 index 06a6bd7c..00000000 --- a/gallagher/dto/door.py +++ /dev/null @@ -1,10 +0,0 @@ -""" - - -""" - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/event.py b/gallagher/dto/event.py deleted file mode 100644 index 31a6d234..00000000 --- a/gallagher/dto/event.py +++ /dev/null @@ -1,15 +0,0 @@ -""" - -""" -from typing import Optional - -from .utils import ( - AppBaseModel, - HrefMixin, - IdentityMixin, -) - -# from ..dto.alarm import AlarmRef -# from ..dto.cardholder import CardholderRef -# from ..dto.access_group import AccessGroupRef -# from ..dto.door import DoorRef diff --git a/gallagher/dto/features.py b/gallagher/dto/features.py deleted file mode 100644 index c4626c05..00000000 --- a/gallagher/dto/features.py +++ /dev/null @@ -1,11 +0,0 @@ -""" Represents the capabilities of a Gallagher Command Centre server. - - - -""" - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/items.py b/gallagher/dto/items.py deleted file mode 100644 index a0d6dd25..00000000 --- a/gallagher/dto/items.py +++ /dev/null @@ -1,14 +0,0 @@ -""" - - -""" - -from typing import Optional - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) - -from .division import DivisionRef diff --git a/gallagher/dto/locker.py b/gallagher/dto/locker.py deleted file mode 100644 index 33f91f06..00000000 --- a/gallagher/dto/locker.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - - -""" - - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/operator.py b/gallagher/dto/operator.py deleted file mode 100644 index 33f91f06..00000000 --- a/gallagher/dto/operator.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - - -""" - - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/pdf.py b/gallagher/dto/pdf.py deleted file mode 100644 index eadd38bb..00000000 --- a/gallagher/dto/pdf.py +++ /dev/null @@ -1,8 +0,0 @@ -""" Personal Data Fields (not to be confused with PDF files) - -""" - -from typing import Optional - -from .utils import AppBaseModel, IdentityMixin, \ - HrefMixin diff --git a/gallagher/dto/reception.py b/gallagher/dto/reception.py deleted file mode 100644 index 33f91f06..00000000 --- a/gallagher/dto/reception.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - - -""" - - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) diff --git a/gallagher/dto/role.py b/gallagher/dto/role.py deleted file mode 100644 index 4ed6c369..00000000 --- a/gallagher/dto/role.py +++ /dev/null @@ -1,12 +0,0 @@ -""" - -""" -from typing import Optional - -from .utils import ( - AppBaseModel, - IdentityMixin, - HrefMixin -) - -from .division import DivisionRef diff --git a/gallagher/dto/salto.py b/gallagher/dto/salto.py deleted file mode 100644 index 01bde57d..00000000 --- a/gallagher/dto/salto.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - - -""" - -from .utils import ( - AppBaseModel, - HrefMixin -) - -from .schedule import ScheduleRef diff --git a/gallagher/dto/schedule.py b/gallagher/dto/schedule.py deleted file mode 100644 index 7be108d4..00000000 --- a/gallagher/dto/schedule.py +++ /dev/null @@ -1,8 +0,0 @@ -""" Schedules - -""" - -from .utils import ( - AppBaseModel, - HrefMixin -) diff --git a/gallagher/dto/visit.py b/gallagher/dto/visit.py deleted file mode 100644 index 52f0db0a..00000000 --- a/gallagher/dto/visit.py +++ /dev/null @@ -1,10 +0,0 @@ -""" - - -""" - - -from .utils import ( - AppBaseModel, - HrefMixin -) diff --git a/gallagher/dto/visitor.py b/gallagher/dto/visitor.py deleted file mode 100644 index 01d89eac..00000000 --- a/gallagher/dto/visitor.py +++ /dev/null @@ -1,11 +0,0 @@ - - -from .utils import ( - AppBaseModel, - IdentityMixin -) - -from .access_group import ( - AccessGroupRef, - AccessGroupSummary -) diff --git a/gallagher/dto/zone.py b/gallagher/dto/zone.py deleted file mode 100644 index 5af30183..00000000 --- a/gallagher/dto/zone.py +++ /dev/null @@ -1,4 +0,0 @@ -""" - - -""" From 11a2e01e5b1f0b10152e8cd33628416e660cacff Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:06:25 +1100 Subject: [PATCH 069/118] fix: names of discovery classes discovery classes didn't use the right terminology for the classes this commit fixes it before splitting it into packages --- gallagher/dto/discover.py | 150 +++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py index e3d184dc..987097e7 100644 --- a/gallagher/dto/discover.py +++ b/gallagher/dto/discover.py @@ -15,25 +15,25 @@ ) -class FeatureAccessGroups( +class FeatureAccessGroupsRef( AppBaseModel, ): access_groups: Optional[OptionalHref] = OptionalHref() -class FeatureAccessZones( +class FeatureAccessZonesRef( AppBaseModel, ): access_zones: Optional[OptionalHref] = OptionalHref() -class FeatureAlarmZones( +class FeatureAlarmZonesRef( AppBaseModel, ): alarm_zones: Optional[OptionalHref] = OptionalHref() -class FeatureAlarms( +class FeatureAlarmsRef( AppBaseModel, ): alarms: Optional[OptionalHref] = OptionalHref() @@ -41,14 +41,14 @@ class FeatureAlarms( updates: Optional[OptionalHref] = OptionalHref() -class FeatureCardTypes( +class FeatureCardTypesRef( AppBaseModel, ): assign: Optional[OptionalHref] = OptionalHref() card_types: Optional[OptionalHref] = OptionalHref() -class FeatureCardholders( +class FeatureCardholdersRef( AppBaseModel, ): cardholders: Optional[OptionalHref] = OptionalHref() @@ -56,37 +56,37 @@ class FeatureCardholders( update_location_access_zones: Optional[OptionalHref] = OptionalHref() -class FeatureCompetencies( +class FeatureCompetenciesRef( AppBaseModel, ): competencies: Optional[OptionalHref] = OptionalHref() -class FeatureDayCategories( +class FeatureDayCategoriesRef( AppBaseModel, ): day_categories: Optional[OptionalHref] = OptionalHref() -class FeatureDivisions( +class FeatureDivisionsRef( AppBaseModel, ): divisions: Optional[OptionalHref] = OptionalHref() -class FeatureDoors( +class FeatureDoorsRef( AppBaseModel, ): doors: Optional[OptionalHref] = OptionalHref() -class FeatureElevators( +class FeatureElevatorsRef( AppBaseModel, ): elevator_groups: Optional[OptionalHref] = OptionalHref() -class FeatureEvents( +class FeatureEventsRef( AppBaseModel, ): divisions: Optional[OptionalHref] = OptionalHref() @@ -95,25 +95,25 @@ class FeatureEvents( updates: Optional[OptionalHref] = OptionalHref() -class FeatureFenceZones( +class FeatureFenceZonesRef( AppBaseModel, ): fence_zones: Optional[OptionalHref] = OptionalHref() -class FeatureInputs( +class FeatureInputsRef( AppBaseModel, ): inputs: Optional[OptionalHref] = OptionalHref() -class FeatureInterlockGroups( +class FeatureInterlockGroupsRef( AppBaseModel, ): interlock_groups: Optional[OptionalHref] = OptionalHref() -class FeatureItems( +class FeatureItemsRef( AppBaseModel, ): item_types: Optional[OptionalHref] = OptionalHref() @@ -121,55 +121,55 @@ class FeatureItems( updates: Optional[OptionalHref] = OptionalHref() -class FeatureLockerBanks( +class FeatureLockerBanksRef( AppBaseModel, ): locker_banks: Optional[OptionalHref] = OptionalHref() -class FeatureMacros( +class FeatureMacrosRef( AppBaseModel, ): macros: Optional[OptionalHref] = OptionalHref() -class FeatureOperatorGroups( +class FeatureOperatorGroupsRef( AppBaseModel, ): operator_groups: Optional[OptionalHref] = OptionalHref() -class FeatureOutputs( +class FeatureOutputsRef( AppBaseModel, ): outputs: Optional[OptionalHref] = OptionalHref() -class FeaturePersonalDataFields( +class FeaturePersonalDataFieldsRef( AppBaseModel, ): personal_data_fields: Optional[OptionalHref] = OptionalHref() -class FeatureReceptions( +class FeatureReceptionsRef( AppBaseModel, ): receptions: Optional[OptionalHref] = OptionalHref() -class FeatureRoles( +class FeatureRolesRef( AppBaseModel, ): roles: Optional[OptionalHref] = OptionalHref() -class FeatureSchedules( +class FeatureSchedulesRef( AppBaseModel, ): schedules: Optional[OptionalHref] = OptionalHref() -class FeatureVisits( +class FeatureVisitsRef( AppBaseModel, ): visits: Optional[OptionalHref] = OptionalHref() @@ -187,56 +187,56 @@ class FeaturesDetail( If a feature is unavailable the API client will throw an exception. """ - access_groups: Optional[FeatureAccessGroups]\ - = FeatureAccessGroups() - access_zones: Optional[FeatureAccessZones]\ - = FeatureAccessZones() - alarm_zones: Optional[FeatureAlarmZones]\ - = FeatureAlarmZones() - alarms: Optional[FeatureAlarms]\ - = FeatureAlarms() - card_types: Optional[FeatureCardTypes]\ - = FeatureCardTypes() - cardholders: Optional[FeatureCardholders]\ - = FeatureCardholders() - competencies: Optional[FeatureCompetencies]\ - = FeatureCompetencies() - day_categories: Optional[FeatureDayCategories]\ - = FeatureDayCategories() - divisions: Optional[FeatureDivisions]\ - = FeatureDivisions() - doors: Optional[FeatureDoors]\ - = FeatureDoors() - elevators: Optional[FeatureElevators]\ - = FeatureElevators() - events: Optional[FeatureEvents]\ - = FeatureEvents() - fence_zones: Optional[FeatureFenceZones]\ - = FeatureFenceZones() - inputs: Optional[FeatureInputs]\ - = FeatureInputs() - interlock_groups: Optional[FeatureInterlockGroups]\ - = FeatureInterlockGroups() - items: Optional[FeatureItems]\ - = FeatureItems() - locker_banks: Optional[FeatureLockerBanks]\ - = FeatureLockerBanks() - macros: Optional[FeatureMacros]\ - = FeatureMacros() - operator_groups: Optional[FeatureOperatorGroups]\ - = FeatureOperatorGroups() - outputs: Optional[FeatureOutputs]\ - = FeatureOutputs() - personal_data_fields: Optional[FeaturePersonalDataFields]\ - = FeaturePersonalDataFields() - receptions: Optional[FeatureReceptions]\ - = FeatureReceptions() - roles: Optional[FeatureRoles]\ - = FeatureRoles() - schedules: Optional[FeatureSchedules]\ - = FeatureSchedules() - visits: Optional[FeatureVisits]\ - = FeatureVisits() + access_groups: Optional[FeatureAccessGroupsRef]\ + = FeatureAccessGroupsRef() + access_zones: Optional[FeatureAccessZonesRef]\ + = FeatureAccessZonesRef() + alarm_zones: Optional[FeatureAlarmZonesRef]\ + = FeatureAlarmZonesRef() + alarms: Optional[FeatureAlarmsRef]\ + = FeatureAlarmsRef() + card_types: Optional[FeatureCardTypesRef]\ + = FeatureCardTypesRef() + cardholders: Optional[FeatureCardholdersRef]\ + = FeatureCardholdersRef() + competencies: Optional[FeatureCompetenciesRef]\ + = FeatureCompetenciesRef() + day_categories: Optional[FeatureDayCategoriesRef]\ + = FeatureDayCategoriesRef() + divisions: Optional[FeatureDivisionsRef]\ + = FeatureDivisionsRef() + doors: Optional[FeatureDoorsRef]\ + = FeatureDoorsRef() + elevators: Optional[FeatureElevatorsRef]\ + = FeatureElevatorsRef() + events: Optional[FeatureEventsRef]\ + = FeatureEventsRef() + fence_zones: Optional[FeatureFenceZonesRef]\ + = FeatureFenceZonesRef() + inputs: Optional[FeatureInputsRef]\ + = FeatureInputsRef() + interlock_groups: Optional[FeatureInterlockGroupsRef]\ + = FeatureInterlockGroupsRef() + items: Optional[FeatureItemsRef]\ + = FeatureItemsRef() + locker_banks: Optional[FeatureLockerBanksRef]\ + = FeatureLockerBanksRef() + macros: Optional[FeatureMacrosRef]\ + = FeatureMacrosRef() + operator_groups: Optional[FeatureOperatorGroupsRef]\ + = FeatureOperatorGroupsRef() + outputs: Optional[FeatureOutputsRef]\ + = FeatureOutputsRef() + personal_data_fields: Optional[FeaturePersonalDataFieldsRef]\ + = FeaturePersonalDataFieldsRef() + receptions: Optional[FeatureReceptionsRef]\ + = FeatureReceptionsRef() + roles: Optional[FeatureRolesRef]\ + = FeatureRolesRef() + schedules: Optional[FeatureSchedulesRef]\ + = FeatureSchedulesRef() + visits: Optional[FeatureVisitsRef]\ + = FeatureVisitsRef() class DiscoveryResponse( From 31ed7198f23185f606d8253f9cb1ac8f2101362f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:44:03 +1100 Subject: [PATCH 070/118] fix: imports post dto refactor REFS #21 --- gallagher/cc/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 0e199a20..49305a66 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -21,11 +21,14 @@ UnlicensedFeatureException ) -from ..dto.discover import ( - DiscoveryResponse, +from ..dto.detail import ( FeaturesDetail, ) +from ..dto.response import ( + DiscoveryResponse, +) + def check_api_key_format(api_key): """ Validates that the Gallagher Key is in the right format. From c952888f65f2f83d13a290533c82158265ccd9c5 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:44:24 +1100 Subject: [PATCH 071/118] refactor: wire up ref classes at module level --- gallagher/dto/ref/__init__.py | 66 +++++++++++++ gallagher/dto/ref/discover.py | 174 ++++++++++++++++++++++++++++++++++ gallagher/dto/ref/utils.py | 0 3 files changed, 240 insertions(+) delete mode 100644 gallagher/dto/ref/utils.py diff --git a/gallagher/dto/ref/__init__.py b/gallagher/dto/ref/__init__.py index 5eb80ea8..23540cb5 100644 --- a/gallagher/dto/ref/__init__.py +++ b/gallagher/dto/ref/__init__.py @@ -15,3 +15,69 @@ from .access_group import ( AccessGroupRef ) + +from .alarm import ( + AlarmRef, + AlarmZoneRef, +) + +from .cardholder import ( + CardholderRef, +) + +from .day_category import ( + DayCategoryRef, +) + +from .discover import ( + FeatureAccessGroupsRef, + FeatureAccessZonesRef, + FeatureAlarmZonesRef, + FeatureAlarmsRef, + FeatureCardTypesRef, + FeatureCardholdersRef, + FeatureCompetenciesRef, + FeatureDayCategoriesRef, + FeatureDivisionsRef, + FeatureDoorsRef, + FeatureElevatorsRef, + FeatureEventsRef, + FeatureFenceZonesRef, + FeatureInputsRef, + FeatureInterlockGroupsRef, + FeatureItemsRef, + FeatureLockerBanksRef, + FeatureMacrosRef, + FeatureOperatorGroupsRef, + FeatureOutputsRef, + FeaturePersonalDataFieldsRef, + FeatureReceptionsRef, + FeatureRolesRef, + FeatureSchedulesRef, + FeatureVisitsRef, +) + +from .door import ( + DoorRef, +) + +from .items import ( + ItemRef, +) + +from .pdf import ( + PDFRef, +) + +from .salto import ( + SaltoItemTypeRef, + SaltoItemRef, +) + +from .schedule import ( + ScheduleRef, +) + +from .zone import ( + AccessZoneRef, +) diff --git a/gallagher/dto/ref/discover.py b/gallagher/dto/ref/discover.py index e69de29b..194bd667 100644 --- a/gallagher/dto/ref/discover.py +++ b/gallagher/dto/ref/discover.py @@ -0,0 +1,174 @@ +""" Command Centre API discovery + +The Command Centre API has a discovery endpoint that allows + +""" + +from typing import ( + Optional, +) + +from ..utils import ( + AppBaseModel, + OptionalHref, +) + + +class FeatureAccessGroupsRef( + AppBaseModel, +): + access_groups: Optional[OptionalHref] = OptionalHref() + + +class FeatureAccessZonesRef( + AppBaseModel, +): + access_zones: Optional[OptionalHref] = OptionalHref() + + +class FeatureAlarmZonesRef( + AppBaseModel, +): + alarm_zones: Optional[OptionalHref] = OptionalHref() + + +class FeatureAlarmsRef( + AppBaseModel, +): + alarms: Optional[OptionalHref] = OptionalHref() + divisions: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() + + +class FeatureCardTypesRef( + AppBaseModel, +): + assign: Optional[OptionalHref] = OptionalHref() + card_types: Optional[OptionalHref] = OptionalHref() + + +class FeatureCardholdersRef( + AppBaseModel, +): + cardholders: Optional[OptionalHref] = OptionalHref() + changes: Optional[OptionalHref] = OptionalHref() + update_location_access_zones: Optional[OptionalHref] = OptionalHref() + + +class FeatureCompetenciesRef( + AppBaseModel, +): + competencies: Optional[OptionalHref] = OptionalHref() + + +class FeatureDayCategoriesRef( + AppBaseModel, +): + day_categories: Optional[OptionalHref] = OptionalHref() + + +class FeatureDivisionsRef( + AppBaseModel, +): + divisions: Optional[OptionalHref] = OptionalHref() + + +class FeatureDoorsRef( + AppBaseModel, +): + doors: Optional[OptionalHref] = OptionalHref() + + +class FeatureElevatorsRef( + AppBaseModel, +): + elevator_groups: Optional[OptionalHref] = OptionalHref() + + +class FeatureEventsRef( + AppBaseModel, +): + divisions: Optional[OptionalHref] = OptionalHref() + event_groups: Optional[OptionalHref] = OptionalHref() + events: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() + + +class FeatureFenceZonesRef( + AppBaseModel, +): + fence_zones: Optional[OptionalHref] = OptionalHref() + + +class FeatureInputsRef( + AppBaseModel, +): + inputs: Optional[OptionalHref] = OptionalHref() + + +class FeatureInterlockGroupsRef( + AppBaseModel, +): + interlock_groups: Optional[OptionalHref] = OptionalHref() + + +class FeatureItemsRef( + AppBaseModel, +): + item_types: Optional[OptionalHref] = OptionalHref() + items: Optional[OptionalHref] = OptionalHref() + updates: Optional[OptionalHref] = OptionalHref() + + +class FeatureLockerBanksRef( + AppBaseModel, +): + locker_banks: Optional[OptionalHref] = OptionalHref() + + +class FeatureMacrosRef( + AppBaseModel, +): + macros: Optional[OptionalHref] = OptionalHref() + + +class FeatureOperatorGroupsRef( + AppBaseModel, +): + operator_groups: Optional[OptionalHref] = OptionalHref() + + +class FeatureOutputsRef( + AppBaseModel, +): + outputs: Optional[OptionalHref] = OptionalHref() + + +class FeaturePersonalDataFieldsRef( + AppBaseModel, +): + personal_data_fields: Optional[OptionalHref] = OptionalHref() + + +class FeatureReceptionsRef( + AppBaseModel, +): + receptions: Optional[OptionalHref] = OptionalHref() + + +class FeatureRolesRef( + AppBaseModel, +): + roles: Optional[OptionalHref] = OptionalHref() + + +class FeatureSchedulesRef( + AppBaseModel, +): + schedules: Optional[OptionalHref] = OptionalHref() + + +class FeatureVisitsRef( + AppBaseModel, +): + visits: Optional[OptionalHref] = OptionalHref() diff --git a/gallagher/dto/ref/utils.py b/gallagher/dto/ref/utils.py deleted file mode 100644 index e69de29b..00000000 From fadb74e60d73efce3857bf8d8b0bda7725e79822 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:44:33 +1100 Subject: [PATCH 072/118] refactor: wire up response classes at module level --- gallagher/dto/response/__init__.py | 38 ++++++++++++++++++++++++++++++ gallagher/dto/response/discover.py | 36 ++++++++++++++++++++++++++++ gallagher/dto/response/utils.py | 0 3 files changed, 74 insertions(+) delete mode 100644 gallagher/dto/response/utils.py diff --git a/gallagher/dto/response/__init__.py b/gallagher/dto/response/__init__.py index 5af30183..227a7b1a 100644 --- a/gallagher/dto/response/__init__.py +++ b/gallagher/dto/response/__init__.py @@ -2,3 +2,41 @@ """ + +from .alarm import ( + AlarmResponse, +) + +from .card_type import ( + CardTypeResponse, +) + +from .cardholder import ( + CardholderSummaryResponse, +) + +from .day_category import ( + DayCategoryResponse, +) + +from .discover import ( + DiscoveryResponse, +) + +from .division import ( + DivisionDetailResponse, +) + +from .door import ( + DoorSummaryResponse, +) + +from .event import ( + EventSummaryResponse, + EventTypeResponse, +) + +from .items import ( + ItemsSummaryResponse, + ItemTypesResponse, +) diff --git a/gallagher/dto/response/discover.py b/gallagher/dto/response/discover.py index e69de29b..fac94b60 100644 --- a/gallagher/dto/response/discover.py +++ b/gallagher/dto/response/discover.py @@ -0,0 +1,36 @@ +from typing import ( + Annotated, +) + +from ..utils import ( + AppBaseModel, +) + + +class DiscoveryResponse( + AppBaseModel, +): + """ A response that outlines the capability of the server + + Gallagher requires customers to license individual features, if they are + the server will return a 403 HTTP code. The purpose of this model is to + discover what features are available on the server. + + The response should be memoized as it is unlikely to change during individual + sessions, they can however change over a period of time. + + This API client is updated to work with various versions of the server, the + server responds with a version string that can be used to determine if + the API client can work with the server. + """ + + version: Annotated[str, "The version of the server"] = "0.0.0" + features: Annotated[FeaturesDetail, + "A list of features available on the server" + ] = FeaturesDetail() + + @property + async def get_sem_ver(self): + """ Get a SemVer tuple from the version string + """ + return self.version.split(".") diff --git a/gallagher/dto/response/utils.py b/gallagher/dto/response/utils.py deleted file mode 100644 index e69de29b..00000000 From 1a5cca70032af839c7407cca1c578dbd592499f1 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:44:54 +1100 Subject: [PATCH 073/118] refactor: wire up summary classes at module level --- gallagher/dto/summary/__init__.py | 49 +++++++++++++++++++++++++++ gallagher/dto/summary/access_group.py | 6 ++-- gallagher/dto/summary/discover.py | 0 gallagher/dto/summary/items.py | 4 +-- gallagher/dto/summary/utils.py | 0 5 files changed, 55 insertions(+), 4 deletions(-) delete mode 100644 gallagher/dto/summary/discover.py delete mode 100644 gallagher/dto/summary/utils.py diff --git a/gallagher/dto/summary/__init__.py b/gallagher/dto/summary/__init__.py index 5af30183..b9b5d2e4 100644 --- a/gallagher/dto/summary/__init__.py +++ b/gallagher/dto/summary/__init__.py @@ -2,3 +2,52 @@ """ + +from .access_group import ( + AccessSummary, + AccessGroupSummary +) + +from .alarm import ( + AlarmSourceSummary, + AlarmZoneSummary, +) + +from .card_type import ( + CardExpiryTypeSummary, +) + +from .cardholder import ( + CardholderSummary, +) + +from .day_category import ( + DayCategorySummary, +) + +from .door import ( + DoorSummary, +) + +from .event import ( + EventSummary, + EventGroupSummary, +) + +from .items import ( + ItemTypeSummary, + ItemSummary, +) + +from .salto import ( + SaltoAccessItemSummary, +) + +from .schedule import ( + ScheduleSummary, +) + +from .visitor import ( + VisitorManagementSummary, + VisitorTypeSummary, +) diff --git a/gallagher/dto/summary/access_group.py b/gallagher/dto/summary/access_group.py index 0a1412a1..004c6e32 100644 --- a/gallagher/dto/summary/access_group.py +++ b/gallagher/dto/summary/access_group.py @@ -6,8 +6,10 @@ HrefMixin ) -from ..ref.access_group import ( - AccessGroupRef +from ..ref import ( + AccessGroupRef, + AccessZoneRef, + ScheduleRef, ) diff --git a/gallagher/dto/summary/discover.py b/gallagher/dto/summary/discover.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py index c5ad5425..a9c5233e 100644 --- a/gallagher/dto/summary/items.py +++ b/gallagher/dto/summary/items.py @@ -6,7 +6,7 @@ ) -class ItemTypeDetail( +class ItemTypeSummary( AppBaseModel, ): """ Items Types only provide the name and id @@ -28,4 +28,4 @@ class ItemSummary( server_display_name, this is used by the item summary response """ name: str - type: ItemTypeDetail + type: ItemTypeSummary diff --git a/gallagher/dto/summary/utils.py b/gallagher/dto/summary/utils.py deleted file mode 100644 index e69de29b..00000000 From bd6cb03caa785831ff46e2ed5b0b5e323f190382 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:53:52 +1100 Subject: [PATCH 074/118] chore: dropso discovery top level class --- gallagher/dto/discover.py | 268 -------------------------------------- 1 file changed, 268 deletions(-) delete mode 100644 gallagher/dto/discover.py diff --git a/gallagher/dto/discover.py b/gallagher/dto/discover.py deleted file mode 100644 index 987097e7..00000000 --- a/gallagher/dto/discover.py +++ /dev/null @@ -1,268 +0,0 @@ -""" Command Centre API discovery - -The Command Centre API has a discovery endpoint that allows - -""" - -from typing import ( - Annotated, - Optional, -) - -from .utils import ( - AppBaseModel, - OptionalHref, -) - - -class FeatureAccessGroupsRef( - AppBaseModel, -): - access_groups: Optional[OptionalHref] = OptionalHref() - - -class FeatureAccessZonesRef( - AppBaseModel, -): - access_zones: Optional[OptionalHref] = OptionalHref() - - -class FeatureAlarmZonesRef( - AppBaseModel, -): - alarm_zones: Optional[OptionalHref] = OptionalHref() - - -class FeatureAlarmsRef( - AppBaseModel, -): - alarms: Optional[OptionalHref] = OptionalHref() - divisions: Optional[OptionalHref] = OptionalHref() - updates: Optional[OptionalHref] = OptionalHref() - - -class FeatureCardTypesRef( - AppBaseModel, -): - assign: Optional[OptionalHref] = OptionalHref() - card_types: Optional[OptionalHref] = OptionalHref() - - -class FeatureCardholdersRef( - AppBaseModel, -): - cardholders: Optional[OptionalHref] = OptionalHref() - changes: Optional[OptionalHref] = OptionalHref() - update_location_access_zones: Optional[OptionalHref] = OptionalHref() - - -class FeatureCompetenciesRef( - AppBaseModel, -): - competencies: Optional[OptionalHref] = OptionalHref() - - -class FeatureDayCategoriesRef( - AppBaseModel, -): - day_categories: Optional[OptionalHref] = OptionalHref() - - -class FeatureDivisionsRef( - AppBaseModel, -): - divisions: Optional[OptionalHref] = OptionalHref() - - -class FeatureDoorsRef( - AppBaseModel, -): - doors: Optional[OptionalHref] = OptionalHref() - - -class FeatureElevatorsRef( - AppBaseModel, -): - elevator_groups: Optional[OptionalHref] = OptionalHref() - - -class FeatureEventsRef( - AppBaseModel, -): - divisions: Optional[OptionalHref] = OptionalHref() - event_groups: Optional[OptionalHref] = OptionalHref() - events: Optional[OptionalHref] = OptionalHref() - updates: Optional[OptionalHref] = OptionalHref() - - -class FeatureFenceZonesRef( - AppBaseModel, -): - fence_zones: Optional[OptionalHref] = OptionalHref() - - -class FeatureInputsRef( - AppBaseModel, -): - inputs: Optional[OptionalHref] = OptionalHref() - - -class FeatureInterlockGroupsRef( - AppBaseModel, -): - interlock_groups: Optional[OptionalHref] = OptionalHref() - - -class FeatureItemsRef( - AppBaseModel, -): - item_types: Optional[OptionalHref] = OptionalHref() - items: Optional[OptionalHref] = OptionalHref() - updates: Optional[OptionalHref] = OptionalHref() - - -class FeatureLockerBanksRef( - AppBaseModel, -): - locker_banks: Optional[OptionalHref] = OptionalHref() - - -class FeatureMacrosRef( - AppBaseModel, -): - macros: Optional[OptionalHref] = OptionalHref() - - -class FeatureOperatorGroupsRef( - AppBaseModel, -): - operator_groups: Optional[OptionalHref] = OptionalHref() - - -class FeatureOutputsRef( - AppBaseModel, -): - outputs: Optional[OptionalHref] = OptionalHref() - - -class FeaturePersonalDataFieldsRef( - AppBaseModel, -): - personal_data_fields: Optional[OptionalHref] = OptionalHref() - - -class FeatureReceptionsRef( - AppBaseModel, -): - receptions: Optional[OptionalHref] = OptionalHref() - - -class FeatureRolesRef( - AppBaseModel, -): - roles: Optional[OptionalHref] = OptionalHref() - - -class FeatureSchedulesRef( - AppBaseModel, -): - schedules: Optional[OptionalHref] = OptionalHref() - - -class FeatureVisitsRef( - AppBaseModel, -): - visits: Optional[OptionalHref] = OptionalHref() - - -class FeaturesDetail( - AppBaseModel, -): - """ A detailed list of features that are available on the server. - - All features are marked as Optional, which means that by default - it's assumed that they are not available on the server. Upon discovery - if a feature is enabled on the server then we receive a href which - indicates to the client that the feature is available. - - If a feature is unavailable the API client will throw an exception. - """ - access_groups: Optional[FeatureAccessGroupsRef]\ - = FeatureAccessGroupsRef() - access_zones: Optional[FeatureAccessZonesRef]\ - = FeatureAccessZonesRef() - alarm_zones: Optional[FeatureAlarmZonesRef]\ - = FeatureAlarmZonesRef() - alarms: Optional[FeatureAlarmsRef]\ - = FeatureAlarmsRef() - card_types: Optional[FeatureCardTypesRef]\ - = FeatureCardTypesRef() - cardholders: Optional[FeatureCardholdersRef]\ - = FeatureCardholdersRef() - competencies: Optional[FeatureCompetenciesRef]\ - = FeatureCompetenciesRef() - day_categories: Optional[FeatureDayCategoriesRef]\ - = FeatureDayCategoriesRef() - divisions: Optional[FeatureDivisionsRef]\ - = FeatureDivisionsRef() - doors: Optional[FeatureDoorsRef]\ - = FeatureDoorsRef() - elevators: Optional[FeatureElevatorsRef]\ - = FeatureElevatorsRef() - events: Optional[FeatureEventsRef]\ - = FeatureEventsRef() - fence_zones: Optional[FeatureFenceZonesRef]\ - = FeatureFenceZonesRef() - inputs: Optional[FeatureInputsRef]\ - = FeatureInputsRef() - interlock_groups: Optional[FeatureInterlockGroupsRef]\ - = FeatureInterlockGroupsRef() - items: Optional[FeatureItemsRef]\ - = FeatureItemsRef() - locker_banks: Optional[FeatureLockerBanksRef]\ - = FeatureLockerBanksRef() - macros: Optional[FeatureMacrosRef]\ - = FeatureMacrosRef() - operator_groups: Optional[FeatureOperatorGroupsRef]\ - = FeatureOperatorGroupsRef() - outputs: Optional[FeatureOutputsRef]\ - = FeatureOutputsRef() - personal_data_fields: Optional[FeaturePersonalDataFieldsRef]\ - = FeaturePersonalDataFieldsRef() - receptions: Optional[FeatureReceptionsRef]\ - = FeatureReceptionsRef() - roles: Optional[FeatureRolesRef]\ - = FeatureRolesRef() - schedules: Optional[FeatureSchedulesRef]\ - = FeatureSchedulesRef() - visits: Optional[FeatureVisitsRef]\ - = FeatureVisitsRef() - - -class DiscoveryResponse( - AppBaseModel, -): - """ A response that outlines the capability of the server - - Gallagher requires customers to license individual features, if they are - the server will return a 403 HTTP code. The purpose of this model is to - discover what features are available on the server. - - The response should be memoized as it is unlikely to change during individual - sessions, they can however change over a period of time. - - This API client is updated to work with various versions of the server, the - server responds with a version string that can be used to determine if - the API client can work with the server. - """ - - version: Annotated[str, "The version of the server"] = "0.0.0" - features: Annotated[FeaturesDetail, - "A list of features available on the server" - ] = FeaturesDetail() - - @property - async def get_sem_ver(self): - """ Get a SemVer tuple from the version string - """ - return self.version.split(".") From 32060e6f03140e4f32176630a521e820f0e48917 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:54:00 +1100 Subject: [PATCH 075/118] fix: missing import for division --- gallagher/dto/ref/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gallagher/dto/ref/__init__.py b/gallagher/dto/ref/__init__.py index 23540cb5..19835901 100644 --- a/gallagher/dto/ref/__init__.py +++ b/gallagher/dto/ref/__init__.py @@ -57,6 +57,10 @@ FeatureVisitsRef, ) +from .division import ( + DivisionRef, +) + from .door import ( DoorRef, ) From ecde082e2dcc706b93394ada725185bdf4cfdccf Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 15:56:00 +1100 Subject: [PATCH 076/118] refactor: wire up detail classes at module level --- gallagher/dto/detail/__init__.py | 23 +++++++ gallagher/dto/detail/access_group.py | 2 +- gallagher/dto/detail/cardholder.py | 2 +- gallagher/dto/detail/discover.py | 99 ++++++++++++++++++++++++++++ gallagher/dto/detail/utils.py | 0 5 files changed, 124 insertions(+), 2 deletions(-) delete mode 100644 gallagher/dto/detail/utils.py diff --git a/gallagher/dto/detail/__init__.py b/gallagher/dto/detail/__init__.py index 70211236..763d4995 100644 --- a/gallagher/dto/detail/__init__.py +++ b/gallagher/dto/detail/__init__.py @@ -3,7 +3,30 @@ """ +from .access_group import ( + AccessGroupDetail, +) from .card_type import ( CardTypeDetail, ) + +from .cardholder import ( + CardholderDetail, +) + +from .discover import ( + FeaturesDetail, +) + +from .division import ( + DivisionDetail, +) + +from .pdf import ( + PDFDetail, +) + +from .role import ( + RoleDetail, +) diff --git a/gallagher/dto/detail/access_group.py b/gallagher/dto/detail/access_group.py index 0d470974..430705b1 100644 --- a/gallagher/dto/detail/access_group.py +++ b/gallagher/dto/detail/access_group.py @@ -18,7 +18,7 @@ DivisionDetail ) -from ..ref.pdf import ( +from ..ref import ( PDFRef ) diff --git a/gallagher/dto/detail/cardholder.py b/gallagher/dto/detail/cardholder.py index f822f15a..766a7604 100644 --- a/gallagher/dto/detail/cardholder.py +++ b/gallagher/dto/detail/cardholder.py @@ -7,7 +7,7 @@ HrefMixin, ) -from .division import ( +from ..ref import ( DivisionRef, ) diff --git a/gallagher/dto/detail/discover.py b/gallagher/dto/detail/discover.py index e69de29b..bbfe613d 100644 --- a/gallagher/dto/detail/discover.py +++ b/gallagher/dto/detail/discover.py @@ -0,0 +1,99 @@ +from typing import ( + Optional +) + +from ..utils import ( + AppBaseModel, +) + +from ..ref import ( + FeatureAccessGroupsRef, + FeatureAccessZonesRef, + FeatureAlarmZonesRef, + FeatureAlarmsRef, + FeatureCardTypesRef, + FeatureCardholdersRef, + FeatureCompetenciesRef, + FeatureDayCategoriesRef, + FeatureDivisionsRef, + FeatureDoorsRef, + FeatureElevatorsRef, + FeatureEventsRef, + FeatureFenceZonesRef, + FeatureInputsRef, + FeatureInterlockGroupsRef, + FeatureItemsRef, + FeatureLockerBanksRef, + FeatureMacrosRef, + FeatureOperatorGroupsRef, + FeatureOutputsRef, + FeaturePersonalDataFieldsRef, + FeatureReceptionsRef, + FeatureRolesRef, + FeatureSchedulesRef, + FeatureVisitsRef, +) + + +class FeaturesDetail( + AppBaseModel, +): + """ A detailed list of features that are available on the server. + + All features are marked as Optional, which means that by default + it's assumed that they are not available on the server. Upon discovery + if a feature is enabled on the server then we receive a href which + indicates to the client that the feature is available. + + If a feature is unavailable the API client will throw an exception. + """ + access_groups: Optional[FeatureAccessGroupsRef]\ + = FeatureAccessGroupsRef() + access_zones: Optional[FeatureAccessZonesRef]\ + = FeatureAccessZonesRef() + alarm_zones: Optional[FeatureAlarmZonesRef]\ + = FeatureAlarmZonesRef() + alarms: Optional[FeatureAlarmsRef]\ + = FeatureAlarmsRef() + card_types: Optional[FeatureCardTypesRef]\ + = FeatureCardTypesRef() + cardholders: Optional[FeatureCardholdersRef]\ + = FeatureCardholdersRef() + competencies: Optional[FeatureCompetenciesRef]\ + = FeatureCompetenciesRef() + day_categories: Optional[FeatureDayCategoriesRef]\ + = FeatureDayCategoriesRef() + divisions: Optional[FeatureDivisionsRef]\ + = FeatureDivisionsRef() + doors: Optional[FeatureDoorsRef]\ + = FeatureDoorsRef() + elevators: Optional[FeatureElevatorsRef]\ + = FeatureElevatorsRef() + events: Optional[FeatureEventsRef]\ + = FeatureEventsRef() + fence_zones: Optional[FeatureFenceZonesRef]\ + = FeatureFenceZonesRef() + inputs: Optional[FeatureInputsRef]\ + = FeatureInputsRef() + interlock_groups: Optional[FeatureInterlockGroupsRef]\ + = FeatureInterlockGroupsRef() + items: Optional[FeatureItemsRef]\ + = FeatureItemsRef() + locker_banks: Optional[FeatureLockerBanksRef]\ + = FeatureLockerBanksRef() + macros: Optional[FeatureMacrosRef]\ + = FeatureMacrosRef() + operator_groups: Optional[FeatureOperatorGroupsRef]\ + = FeatureOperatorGroupsRef() + outputs: Optional[FeatureOutputsRef]\ + = FeatureOutputsRef() + personal_data_fields: Optional[FeaturePersonalDataFieldsRef]\ + = FeaturePersonalDataFieldsRef() + receptions: Optional[FeatureReceptionsRef]\ + = FeatureReceptionsRef() + roles: Optional[FeatureRolesRef]\ + = FeatureRolesRef() + schedules: Optional[FeatureSchedulesRef]\ + = FeatureSchedulesRef() + visits: Optional[FeatureVisitsRef]\ + = FeatureVisitsRef() diff --git a/gallagher/dto/detail/utils.py b/gallagher/dto/detail/utils.py deleted file mode 100644 index e69de29b..00000000 From 797b30d2e3a254188a714766a9efdd7adc4813c5 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 16:11:11 +1100 Subject: [PATCH 077/118] refactor: various imports across the library --- gallagher/cc/__init__.py | 4 ---- gallagher/cc/alarms/__init__.py | 7 +++++-- tests/test_alarm.py | 8 ++++++-- tests/test_card_types.py | 8 ++++++-- tests/test_cardholder.py | 18 +++++++++++++----- tests/test_day_category.py | 8 ++++++-- tests/test_discover.py | 6 ++++-- tests/test_divisions.py | 18 +++++++++++++----- tests/test_events.py | 7 +++++-- tests/test_items.py | 28 ++++++++++++++++++++-------- tests/test_schedule.py | 8 ++++++-- 11 files changed, 84 insertions(+), 36 deletions(-) diff --git a/gallagher/cc/__init__.py b/gallagher/cc/__init__.py index ca030378..ca1a02e7 100644 --- a/gallagher/cc/__init__.py +++ b/gallagher/cc/__init__.py @@ -24,10 +24,6 @@ from ..const import URL -from ..dto.discover import ( - DiscoveryResponse, - FeaturesDetail, -) # Follow the instructions in the Gallagher documentation # to obtain an API key diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 943de9b2..623e59a4 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -9,11 +9,14 @@ Capabilities ) -from ...dto.alarm import ( - AlarmResponse, +from ...dto.summary import ( AlarmZoneSummary ) +from ...dto.response import ( + AlarmResponse, +) + class Alarms( APIEndpoint diff --git a/tests/test_alarm.py b/tests/test_alarm.py index ae84e377..b304123b 100644 --- a/tests/test_alarm.py +++ b/tests/test_alarm.py @@ -9,8 +9,12 @@ async def test_alarms_list(): these are a summary response """ - from gallagher.cc.alarms import Alarms - from gallagher.dto.alarm import AlarmResponse + from gallagher.cc.alarms import ( + Alarms + ) + from gallagher.dto.response import ( + AlarmResponse + ) response = await Alarms.list() assert type(response) is AlarmResponse diff --git a/tests/test_card_types.py b/tests/test_card_types.py index 18bdb122..0325b5c5 100644 --- a/tests/test_card_types.py +++ b/tests/test_card_types.py @@ -4,8 +4,12 @@ async def test_get_card_types(): - from gallagher.cc.cardholders.card_type import CardType - from gallagher.dto.card_type import CardTypeResponse + from gallagher.cc.cardholders.card_type import ( + CardType + ) + from gallagher.dto.response import ( + CardTypeResponse + ) response = await CardType.list() assert type(response) is CardTypeResponse diff --git a/tests/test_cardholder.py b/tests/test_cardholder.py index ed57c341..74a407c1 100644 --- a/tests/test_cardholder.py +++ b/tests/test_cardholder.py @@ -5,8 +5,12 @@ async def test_cardholder_list(): - from gallagher.cc.cardholders.cardholders import Cardholder - from gallagher.dto.cardholder import CardholderSummaryResponse + from gallagher.cc.cardholders.cardholders import ( + Cardholder + ) + from gallagher.dto.response import ( + CardholderSummaryResponse + ) response = await Cardholder.list() assert type(response) is CardholderSummaryResponse @@ -16,11 +20,15 @@ async def test_cardholder_list(): async def test_cardholder_detail(): - from gallagher.cc.cardholders.cardholders import Cardholder - from gallagher.dto.cardholder import ( - CardholderSummaryResponse, + from gallagher.cc.cardholders.cardholders import ( + Cardholder + ) + from gallagher.dto.detail import ( CardholderDetail, ) + from gallagher.dto.response import ( + CardholderSummaryResponse, + ) response = await Cardholder.list() assert type(response) is CardholderSummaryResponse diff --git a/tests/test_day_category.py b/tests/test_day_category.py index c0f784a2..813e625c 100644 --- a/tests/test_day_category.py +++ b/tests/test_day_category.py @@ -5,8 +5,12 @@ async def test_day_category(): - from gallagher.cc.alarms.day_category import DayCategory - from gallagher.dto.day_category import DayCategoryResponse + from gallagher.cc.alarms.day_category import ( + DayCategory + ) + from gallagher.dto.response import ( + DayCategoryResponse + ) response = await DayCategory.list() assert type(response) is DayCategoryResponse diff --git a/tests/test_discover.py b/tests/test_discover.py index f9caa99d..29932a01 100644 --- a/tests/test_discover.py +++ b/tests/test_discover.py @@ -9,9 +9,11 @@ async def test_discover(): """ from gallagher.cc.core import APIEndpoint - from gallagher.dto.discover import ( + from gallagher.dto.detail import ( + FeaturesDetail, + ) + from gallagher.dto.response import ( DiscoveryResponse, - FeaturesDetail ) assert 1 == 1 diff --git a/tests/test_divisions.py b/tests/test_divisions.py index 3d1af3d4..1aeb9954 100644 --- a/tests/test_divisions.py +++ b/tests/test_divisions.py @@ -1,6 +1,10 @@ async def test_division_list(): - from gallagher.cc.alarms.divisions import Division - from gallagher.dto.division import DivisionDetailResponse + from gallagher.cc.alarms.divisions import ( + Division + ) + from gallagher.dto.response import ( + DivisionDetailResponse + ) response = await Division.list() assert type(response) is DivisionDetailResponse @@ -9,11 +13,15 @@ async def test_division_list(): async def test_division_detail(): - from gallagher.cc.alarms.divisions import Division - from gallagher.dto.division import ( - DivisionDetailResponse, + from gallagher.cc.alarms.divisions import ( + Division + ) + from gallagher.dto.detail import ( DivisionDetail, ) + from gallagher.dto.response import ( + DivisionDetailResponse, + ) response = await Division.list() assert type(response) is DivisionDetailResponse diff --git a/tests/test_events.py b/tests/test_events.py index 97b5e974..23ec66e6 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -1,8 +1,11 @@ async def test_event_types(): - from gallagher.cc.alarms.events import EventType - from gallagher.dto.event import ( + + from gallagher.cc.alarms.events import ( + EventType + ) + from gallagher.dto.response import ( EventTypeResponse, ) diff --git a/tests/test_items.py b/tests/test_items.py index 49a9f93a..2003931e 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -9,8 +9,12 @@ async def test_items_types_list(): these are a summary response """ - from gallagher.cc.alarms.items import ItemsTypes - from gallagher.dto.items import ItemTypesResponse + from gallagher.cc.alarms.items import ( + ItemsTypes + ) + from gallagher.dto.response import ( + ItemTypesResponse + ) response = await ItemsTypes.list() assert type(response) is ItemTypesResponse @@ -23,8 +27,12 @@ async def test_items_list(): each one of these on it's own. """ - from gallagher.cc.alarms.items import Item - from gallagher.dto.items import ItemsSummaryResponse + from gallagher.cc.alarms.items import ( + Item + ) + from gallagher.dto.response import ( + ItemsSummaryResponse + ) response = await Item.list() assert type(response) is ItemsSummaryResponse @@ -36,10 +44,14 @@ async def test_item_detail(): """ Get each item in the list and make sure it's a valid item """ - from gallagher.cc.alarms.items import Item - from gallagher.dto.items import ( + from gallagher.cc.alarms.items import ( + Item + ) + from gallagher.dto.summary import ( + ItemSummary, + ) + from gallagher.dto.response import ( ItemsSummaryResponse, - ItemDetail ) response: ItemsSummaryResponse = await Item.list() @@ -48,5 +60,5 @@ async def test_item_detail(): for item_summary in response.results: # Get the detail of the item item_detail_response = await Item.retrieve(item_summary.id) - assert type(item_detail_response) is ItemDetail + assert type(item_detail_response) is ItemSummary assert (item_detail_response.id == item_summary.id) diff --git a/tests/test_schedule.py b/tests/test_schedule.py index 94f3fb64..ac92cb80 100644 --- a/tests/test_schedule.py +++ b/tests/test_schedule.py @@ -6,8 +6,12 @@ async def test_schedules_list(): - from gallagher.cc.alarms.schedule import Schedule - from gallagher.dto.schedule import ScheduleSummaryResponse + from gallagher.cc.alarms.schedule import ( + Schedule + ) + from gallagher.dto.response import ( + ScheduleSummaryResponse, + ) response = await Schedule.list() assert type(response) is ScheduleSummaryResponse From d74ca9a2ce25f984a0cf87e79cfc4ee7e0260db4 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 17:58:07 +1100 Subject: [PATCH 078/118] refactor: circular depdency issues --- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 7 +++++-- gallagher/cc/alarms/events.py | 2 +- gallagher/cc/alarms/items.py | 8 +++++--- gallagher/cc/alarms/schedule.py | 2 +- gallagher/cc/cardholders/card_type.py | 2 +- gallagher/cc/cardholders/cardholders.py | 7 +++++-- gallagher/dto/detail/access_group.py | 12 +++++------- gallagher/dto/detail/card_type.py | 2 +- gallagher/dto/detail/discover.py | 2 +- gallagher/dto/detail/division.py | 2 +- gallagher/dto/detail/items.py | 2 +- gallagher/dto/detail/pdf.py | 2 +- gallagher/dto/detail/role.py | 2 +- gallagher/dto/ref/access_group.py | 2 +- gallagher/dto/ref/alarm.py | 2 +- gallagher/dto/ref/cardholder.py | 2 +- gallagher/dto/ref/day_category.py | 2 +- gallagher/dto/ref/discover.py | 2 +- gallagher/dto/ref/division.py | 2 +- gallagher/dto/ref/door.py | 2 +- gallagher/dto/ref/event.py | 2 +- gallagher/dto/ref/items.py | 2 +- gallagher/dto/ref/pdf.py | 2 +- gallagher/dto/ref/salto.py | 6 +----- gallagher/dto/ref/schedule.py | 2 +- gallagher/dto/ref/zone.py | 2 +- gallagher/dto/response/__init__.py | 4 ++++ gallagher/dto/response/alarm.py | 2 +- gallagher/dto/response/card_type.py | 2 +- gallagher/dto/response/cardholder.py | 2 +- gallagher/dto/response/day_category.py | 2 +- gallagher/dto/response/discover.py | 2 +- gallagher/dto/response/division.py | 2 +- gallagher/dto/response/door.py | 2 +- gallagher/dto/response/event.py | 2 +- gallagher/dto/response/items.py | 2 +- gallagher/dto/response/schedule.py | 2 +- gallagher/dto/summary/access_group.py | 2 +- gallagher/dto/summary/alarm.py | 2 +- gallagher/dto/summary/card_type.py | 2 +- gallagher/dto/summary/cardholder.py | 2 +- gallagher/dto/summary/day_category.py | 2 +- gallagher/dto/summary/door.py | 2 +- gallagher/dto/summary/event.py | 2 +- gallagher/dto/summary/items.py | 2 +- gallagher/dto/summary/salto.py | 2 +- gallagher/dto/summary/schedule.py | 2 +- 48 files changed, 67 insertions(+), 61 deletions(-) diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index ca9112f8..108c37e4 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -9,7 +9,7 @@ EndpointConfig ) -from ...dto.day_category import ( +from ...dto.response import ( DayCategoryResponse ) diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 383e4838..7093449d 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -8,11 +8,14 @@ EndpointConfig ) -from ...dto.division import ( - DivisionDetailResponse, +from ...dto.detail import ( DivisionDetail, ) +from ...dto.response import ( + DivisionDetailResponse, +) + class Division(APIEndpoint): """ diff --git a/gallagher/cc/alarms/events.py b/gallagher/cc/alarms/events.py index 4b843d21..dd3e6e90 100644 --- a/gallagher/cc/alarms/events.py +++ b/gallagher/cc/alarms/events.py @@ -10,7 +10,7 @@ EndpointConfig ) -from ...dto.event import ( +from ...dto.response import ( EventTypeResponse, ) diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index b5aa226d..6f2d3a24 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -9,10 +9,12 @@ EndpointConfig, ) -from ...dto.items import ( +from ...dto.summary import ( + ItemSummary, +) +from ...dto.response import ( ItemTypesResponse, ItemsSummaryResponse, - ItemDetail ) @@ -43,5 +45,5 @@ async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.items.items, dto_list=ItemsSummaryResponse, - dto_retrieve=ItemDetail, + dto_retrieve=ItemSummary, ) diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 77c8f3bb..1fb50c9c 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -8,7 +8,7 @@ EndpointConfig ) -from ...dto.schedule import ( +from ...dto.response import ( ScheduleSummaryResponse ) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index d523b778..9560abe9 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -7,7 +7,7 @@ EndpointConfig ) -from ...dto.card_type import ( +from ...dto.response import ( CardTypeResponse ) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 9341d1dc..87d2fce4 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -7,11 +7,14 @@ EndpointConfig ) -from ...dto.cardholder import ( - CardholderSummaryResponse, +from ...dto.detail import ( CardholderDetail, ) +from ...dto.response import ( + CardholderSummaryResponse, +) + class Cardholder(APIEndpoint): """ Cardholder endpoints allow you to search for and retrieve cardholder details. diff --git a/gallagher/dto/detail/access_group.py b/gallagher/dto/detail/access_group.py index 430705b1..38fe57b5 100644 --- a/gallagher/dto/detail/access_group.py +++ b/gallagher/dto/detail/access_group.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin @@ -10,14 +10,12 @@ AccessGroupRef ) -from ..ref.division import ( - DivisionRef -) - -from division import ( +from .division import ( DivisionDetail ) - +from ..summary import ( + AccessSummary +) from ..ref import ( PDFRef ) diff --git a/gallagher/dto/detail/card_type.py b/gallagher/dto/detail/card_type.py index 0efb4861..2ab40cf2 100644 --- a/gallagher/dto/detail/card_type.py +++ b/gallagher/dto/detail/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/detail/discover.py b/gallagher/dto/detail/discover.py index bbfe613d..0e58e08f 100644 --- a/gallagher/dto/detail/discover.py +++ b/gallagher/dto/detail/discover.py @@ -2,7 +2,7 @@ Optional ) -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/detail/division.py b/gallagher/dto/detail/division.py index fba21564..342f2759 100644 --- a/gallagher/dto/detail/division.py +++ b/gallagher/dto/detail/division.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/detail/items.py b/gallagher/dto/detail/items.py index 9f490728..7579dcb9 100644 --- a/gallagher/dto/detail/items.py +++ b/gallagher/dto/detail/items.py @@ -1,3 +1,3 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/detail/pdf.py b/gallagher/dto/detail/pdf.py index abd10a0a..7b61faeb 100644 --- a/gallagher/dto/detail/pdf.py +++ b/gallagher/dto/detail/pdf.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin, diff --git a/gallagher/dto/detail/role.py b/gallagher/dto/detail/role.py index 91fd587d..104d5a96 100644 --- a/gallagher/dto/detail/role.py +++ b/gallagher/dto/detail/role.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/ref/access_group.py b/gallagher/dto/ref/access_group.py index b198cc62..5ea43a5f 100644 --- a/gallagher/dto/ref/access_group.py +++ b/gallagher/dto/ref/access_group.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/alarm.py b/gallagher/dto/ref/alarm.py index 6241f051..225a9e4a 100644 --- a/gallagher/dto/ref/alarm.py +++ b/gallagher/dto/ref/alarm.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/cardholder.py b/gallagher/dto/ref/cardholder.py index 07971ca6..8a2d6529 100644 --- a/gallagher/dto/ref/cardholder.py +++ b/gallagher/dto/ref/cardholder.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/day_category.py b/gallagher/dto/ref/day_category.py index 9f3a4cdf..0b4eeec2 100644 --- a/gallagher/dto/ref/day_category.py +++ b/gallagher/dto/ref/day_category.py @@ -3,7 +3,7 @@ The calendar determines the days of the year that fall into a day category, and the schedule determines what happens at certain times on those days. """ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/discover.py b/gallagher/dto/ref/discover.py index 194bd667..bc0756bf 100644 --- a/gallagher/dto/ref/discover.py +++ b/gallagher/dto/ref/discover.py @@ -8,7 +8,7 @@ Optional, ) -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, OptionalHref, ) diff --git a/gallagher/dto/ref/division.py b/gallagher/dto/ref/division.py index 10a33d17..34049250 100644 --- a/gallagher/dto/ref/division.py +++ b/gallagher/dto/ref/division.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/ref/door.py b/gallagher/dto/ref/door.py index ad91b6d3..0da35a96 100644 --- a/gallagher/dto/ref/door.py +++ b/gallagher/dto/ref/door.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py index 12f47841..370aa389 100644 --- a/gallagher/dto/ref/event.py +++ b/gallagher/dto/ref/event.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, ) diff --git a/gallagher/dto/ref/items.py b/gallagher/dto/ref/items.py index 78eb5186..ca817920 100644 --- a/gallagher/dto/ref/items.py +++ b/gallagher/dto/ref/items.py @@ -1,5 +1,5 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/pdf.py b/gallagher/dto/ref/pdf.py index a887608b..9ac1292b 100644 --- a/gallagher/dto/ref/pdf.py +++ b/gallagher/dto/ref/pdf.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/salto.py b/gallagher/dto/ref/salto.py index f4a80c0d..be272932 100644 --- a/gallagher/dto/ref/salto.py +++ b/gallagher/dto/ref/salto.py @@ -1,12 +1,8 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) -from ..ref import ( - ScheduleRef -) - class SaltoItemTypeRef( AppBaseModel, diff --git a/gallagher/dto/ref/schedule.py b/gallagher/dto/ref/schedule.py index a81daa36..fb247d0a 100644 --- a/gallagher/dto/ref/schedule.py +++ b/gallagher/dto/ref/schedule.py @@ -2,7 +2,7 @@ """ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/zone.py b/gallagher/dto/ref/zone.py index 97485a14..b5e84456 100644 --- a/gallagher/dto/ref/zone.py +++ b/gallagher/dto/ref/zone.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/__init__.py b/gallagher/dto/response/__init__.py index 227a7b1a..e3c07363 100644 --- a/gallagher/dto/response/__init__.py +++ b/gallagher/dto/response/__init__.py @@ -40,3 +40,7 @@ ItemsSummaryResponse, ItemTypesResponse, ) + +from .schedule import ( + ScheduleSummaryResponse, +) diff --git a/gallagher/dto/response/alarm.py b/gallagher/dto/response/alarm.py index 2afc3424..cef45a4f 100644 --- a/gallagher/dto/response/alarm.py +++ b/gallagher/dto/response/alarm.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/response/card_type.py b/gallagher/dto/response/card_type.py index bca86f5c..5d340b60 100644 --- a/gallagher/dto/response/card_type.py +++ b/gallagher/dto/response/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/cardholder.py b/gallagher/dto/response/cardholder.py index 8f223c11..7bd3f3da 100644 --- a/gallagher/dto/response/cardholder.py +++ b/gallagher/dto/response/cardholder.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/day_category.py b/gallagher/dto/response/day_category.py index 0fa10138..080efc4a 100644 --- a/gallagher/dto/response/day_category.py +++ b/gallagher/dto/response/day_category.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/discover.py b/gallagher/dto/response/discover.py index fac94b60..6618f945 100644 --- a/gallagher/dto/response/discover.py +++ b/gallagher/dto/response/discover.py @@ -2,7 +2,7 @@ Annotated, ) -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/division.py b/gallagher/dto/response/division.py index 37eec892..11e11927 100644 --- a/gallagher/dto/response/division.py +++ b/gallagher/dto/response/division.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/response/door.py b/gallagher/dto/response/door.py index 926524c3..e92f822f 100644 --- a/gallagher/dto/response/door.py +++ b/gallagher/dto/response/door.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index c2fd4457..abfa86e5 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/response/items.py b/gallagher/dto/response/items.py index 159cd7d8..c745eee0 100644 --- a/gallagher/dto/response/items.py +++ b/gallagher/dto/response/items.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/schedule.py b/gallagher/dto/response/schedule.py index e936316d..54b09995 100644 --- a/gallagher/dto/response/schedule.py +++ b/gallagher/dto/response/schedule.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/summary/access_group.py b/gallagher/dto/summary/access_group.py index 004c6e32..882ba5d0 100644 --- a/gallagher/dto/summary/access_group.py +++ b/gallagher/dto/summary/access_group.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/alarm.py b/gallagher/dto/summary/alarm.py index ae181918..44f8ec73 100644 --- a/gallagher/dto/summary/alarm.py +++ b/gallagher/dto/summary/alarm.py @@ -1,6 +1,6 @@ from typing import Optional -from .utils import ( +from ..utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/summary/card_type.py b/gallagher/dto/summary/card_type.py index f93c16b1..5f9fb84f 100644 --- a/gallagher/dto/summary/card_type.py +++ b/gallagher/dto/summary/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/cardholder.py b/gallagher/dto/summary/cardholder.py index a646c6de..bfbd775c 100644 --- a/gallagher/dto/summary/cardholder.py +++ b/gallagher/dto/summary/cardholder.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, ) diff --git a/gallagher/dto/summary/day_category.py b/gallagher/dto/summary/day_category.py index 7747d2c7..113ecb7d 100644 --- a/gallagher/dto/summary/day_category.py +++ b/gallagher/dto/summary/day_category.py @@ -5,7 +5,7 @@ """ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/summary/door.py b/gallagher/dto/summary/door.py index 4037180e..b1a11624 100644 --- a/gallagher/dto/summary/door.py +++ b/gallagher/dto/summary/door.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index 276018b2..e6eb295c 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -1,6 +1,6 @@ from typing import Optional -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py index a9c5233e..6d38d54a 100644 --- a/gallagher/dto/summary/items.py +++ b/gallagher/dto/summary/items.py @@ -1,5 +1,5 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/salto.py b/gallagher/dto/summary/salto.py index 4e204e62..66dbef6d 100644 --- a/gallagher/dto/summary/salto.py +++ b/gallagher/dto/summary/salto.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, ) diff --git a/gallagher/dto/summary/schedule.py b/gallagher/dto/summary/schedule.py index eef9257e..3ef630f4 100644 --- a/gallagher/dto/summary/schedule.py +++ b/gallagher/dto/summary/schedule.py @@ -1,4 +1,4 @@ -from ..utils import ( +from gallagher.dto.utils import ( AppBaseModel, HrefMixin ) From ad698017249cff2b5a7caaba59230f9e70d269c2 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 20 Dec 2023 17:59:39 +1100 Subject: [PATCH 079/118] refactor: switches to relative imports --- gallagher/dto/detail/access_group.py | 2 +- gallagher/dto/detail/card_type.py | 2 +- gallagher/dto/detail/discover.py | 2 +- gallagher/dto/detail/division.py | 2 +- gallagher/dto/detail/items.py | 2 +- gallagher/dto/detail/pdf.py | 2 +- gallagher/dto/detail/role.py | 2 +- gallagher/dto/ref/access_group.py | 2 +- gallagher/dto/ref/alarm.py | 2 +- gallagher/dto/ref/cardholder.py | 2 +- gallagher/dto/ref/day_category.py | 2 +- gallagher/dto/ref/discover.py | 2 +- gallagher/dto/ref/division.py | 2 +- gallagher/dto/ref/door.py | 2 +- gallagher/dto/ref/event.py | 2 +- gallagher/dto/ref/items.py | 2 +- gallagher/dto/ref/pdf.py | 2 +- gallagher/dto/ref/salto.py | 2 +- gallagher/dto/ref/schedule.py | 2 +- gallagher/dto/ref/zone.py | 2 +- gallagher/dto/response/alarm.py | 2 +- gallagher/dto/response/card_type.py | 2 +- gallagher/dto/response/cardholder.py | 2 +- gallagher/dto/response/day_category.py | 2 +- gallagher/dto/response/discover.py | 2 +- gallagher/dto/response/division.py | 2 +- gallagher/dto/response/door.py | 2 +- gallagher/dto/response/event.py | 2 +- gallagher/dto/response/items.py | 2 +- gallagher/dto/response/schedule.py | 2 +- gallagher/dto/summary/access_group.py | 2 +- gallagher/dto/summary/card_type.py | 2 +- gallagher/dto/summary/cardholder.py | 2 +- gallagher/dto/summary/day_category.py | 2 +- gallagher/dto/summary/door.py | 2 +- gallagher/dto/summary/event.py | 2 +- gallagher/dto/summary/items.py | 2 +- gallagher/dto/summary/salto.py | 2 +- gallagher/dto/summary/schedule.py | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/gallagher/dto/detail/access_group.py b/gallagher/dto/detail/access_group.py index 38fe57b5..d196d03d 100644 --- a/gallagher/dto/detail/access_group.py +++ b/gallagher/dto/detail/access_group.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/detail/card_type.py b/gallagher/dto/detail/card_type.py index 2ab40cf2..0efb4861 100644 --- a/gallagher/dto/detail/card_type.py +++ b/gallagher/dto/detail/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/detail/discover.py b/gallagher/dto/detail/discover.py index 0e58e08f..bbfe613d 100644 --- a/gallagher/dto/detail/discover.py +++ b/gallagher/dto/detail/discover.py @@ -2,7 +2,7 @@ Optional ) -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/detail/division.py b/gallagher/dto/detail/division.py index 342f2759..fba21564 100644 --- a/gallagher/dto/detail/division.py +++ b/gallagher/dto/detail/division.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/detail/items.py b/gallagher/dto/detail/items.py index 7579dcb9..9f490728 100644 --- a/gallagher/dto/detail/items.py +++ b/gallagher/dto/detail/items.py @@ -1,3 +1,3 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/detail/pdf.py b/gallagher/dto/detail/pdf.py index 7b61faeb..abd10a0a 100644 --- a/gallagher/dto/detail/pdf.py +++ b/gallagher/dto/detail/pdf.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin, diff --git a/gallagher/dto/detail/role.py b/gallagher/dto/detail/role.py index 104d5a96..91fd587d 100644 --- a/gallagher/dto/detail/role.py +++ b/gallagher/dto/detail/role.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/ref/access_group.py b/gallagher/dto/ref/access_group.py index 5ea43a5f..b198cc62 100644 --- a/gallagher/dto/ref/access_group.py +++ b/gallagher/dto/ref/access_group.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/alarm.py b/gallagher/dto/ref/alarm.py index 225a9e4a..6241f051 100644 --- a/gallagher/dto/ref/alarm.py +++ b/gallagher/dto/ref/alarm.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/cardholder.py b/gallagher/dto/ref/cardholder.py index 8a2d6529..07971ca6 100644 --- a/gallagher/dto/ref/cardholder.py +++ b/gallagher/dto/ref/cardholder.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/day_category.py b/gallagher/dto/ref/day_category.py index 0b4eeec2..9f3a4cdf 100644 --- a/gallagher/dto/ref/day_category.py +++ b/gallagher/dto/ref/day_category.py @@ -3,7 +3,7 @@ The calendar determines the days of the year that fall into a day category, and the schedule determines what happens at certain times on those days. """ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/discover.py b/gallagher/dto/ref/discover.py index bc0756bf..194bd667 100644 --- a/gallagher/dto/ref/discover.py +++ b/gallagher/dto/ref/discover.py @@ -8,7 +8,7 @@ Optional, ) -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, OptionalHref, ) diff --git a/gallagher/dto/ref/division.py b/gallagher/dto/ref/division.py index 34049250..10a33d17 100644 --- a/gallagher/dto/ref/division.py +++ b/gallagher/dto/ref/division.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/ref/door.py b/gallagher/dto/ref/door.py index 0da35a96..ad91b6d3 100644 --- a/gallagher/dto/ref/door.py +++ b/gallagher/dto/ref/door.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py index 370aa389..12f47841 100644 --- a/gallagher/dto/ref/event.py +++ b/gallagher/dto/ref/event.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, ) diff --git a/gallagher/dto/ref/items.py b/gallagher/dto/ref/items.py index ca817920..78eb5186 100644 --- a/gallagher/dto/ref/items.py +++ b/gallagher/dto/ref/items.py @@ -1,5 +1,5 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/pdf.py b/gallagher/dto/ref/pdf.py index 9ac1292b..a887608b 100644 --- a/gallagher/dto/ref/pdf.py +++ b/gallagher/dto/ref/pdf.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/ref/salto.py b/gallagher/dto/ref/salto.py index be272932..76649ede 100644 --- a/gallagher/dto/ref/salto.py +++ b/gallagher/dto/ref/salto.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/schedule.py b/gallagher/dto/ref/schedule.py index fb247d0a..a81daa36 100644 --- a/gallagher/dto/ref/schedule.py +++ b/gallagher/dto/ref/schedule.py @@ -2,7 +2,7 @@ """ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/ref/zone.py b/gallagher/dto/ref/zone.py index b5e84456..97485a14 100644 --- a/gallagher/dto/ref/zone.py +++ b/gallagher/dto/ref/zone.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/alarm.py b/gallagher/dto/response/alarm.py index cef45a4f..2afc3424 100644 --- a/gallagher/dto/response/alarm.py +++ b/gallagher/dto/response/alarm.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/response/card_type.py b/gallagher/dto/response/card_type.py index 5d340b60..bca86f5c 100644 --- a/gallagher/dto/response/card_type.py +++ b/gallagher/dto/response/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/cardholder.py b/gallagher/dto/response/cardholder.py index 7bd3f3da..8f223c11 100644 --- a/gallagher/dto/response/cardholder.py +++ b/gallagher/dto/response/cardholder.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/day_category.py b/gallagher/dto/response/day_category.py index 080efc4a..0fa10138 100644 --- a/gallagher/dto/response/day_category.py +++ b/gallagher/dto/response/day_category.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/discover.py b/gallagher/dto/response/discover.py index 6618f945..fac94b60 100644 --- a/gallagher/dto/response/discover.py +++ b/gallagher/dto/response/discover.py @@ -2,7 +2,7 @@ Annotated, ) -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/division.py b/gallagher/dto/response/division.py index 11e11927..37eec892 100644 --- a/gallagher/dto/response/division.py +++ b/gallagher/dto/response/division.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, ) diff --git a/gallagher/dto/response/door.py b/gallagher/dto/response/door.py index e92f822f..926524c3 100644 --- a/gallagher/dto/response/door.py +++ b/gallagher/dto/response/door.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index abfa86e5..c2fd4457 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/response/items.py b/gallagher/dto/response/items.py index c745eee0..159cd7d8 100644 --- a/gallagher/dto/response/items.py +++ b/gallagher/dto/response/items.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/response/schedule.py b/gallagher/dto/response/schedule.py index 54b09995..e936316d 100644 --- a/gallagher/dto/response/schedule.py +++ b/gallagher/dto/response/schedule.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/summary/access_group.py b/gallagher/dto/summary/access_group.py index 882ba5d0..004c6e32 100644 --- a/gallagher/dto/summary/access_group.py +++ b/gallagher/dto/summary/access_group.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/card_type.py b/gallagher/dto/summary/card_type.py index 5f9fb84f..f93c16b1 100644 --- a/gallagher/dto/summary/card_type.py +++ b/gallagher/dto/summary/card_type.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/cardholder.py b/gallagher/dto/summary/cardholder.py index bfbd775c..a646c6de 100644 --- a/gallagher/dto/summary/cardholder.py +++ b/gallagher/dto/summary/cardholder.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, ) diff --git a/gallagher/dto/summary/day_category.py b/gallagher/dto/summary/day_category.py index 113ecb7d..7747d2c7 100644 --- a/gallagher/dto/summary/day_category.py +++ b/gallagher/dto/summary/day_category.py @@ -5,7 +5,7 @@ """ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) diff --git a/gallagher/dto/summary/door.py b/gallagher/dto/summary/door.py index b1a11624..4037180e 100644 --- a/gallagher/dto/summary/door.py +++ b/gallagher/dto/summary/door.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index e6eb295c..276018b2 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -1,6 +1,6 @@ from typing import Optional -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin, IdentityMixin, diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py index 6d38d54a..a9c5233e 100644 --- a/gallagher/dto/summary/items.py +++ b/gallagher/dto/summary/items.py @@ -1,5 +1,5 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin diff --git a/gallagher/dto/summary/salto.py b/gallagher/dto/summary/salto.py index 66dbef6d..4e204e62 100644 --- a/gallagher/dto/summary/salto.py +++ b/gallagher/dto/summary/salto.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, ) diff --git a/gallagher/dto/summary/schedule.py b/gallagher/dto/summary/schedule.py index 3ef630f4..eef9257e 100644 --- a/gallagher/dto/summary/schedule.py +++ b/gallagher/dto/summary/schedule.py @@ -1,4 +1,4 @@ -from gallagher.dto.utils import ( +from ..utils import ( AppBaseModel, HrefMixin ) From c8b40c04c7ebe8b605264bb9578f5f5bd2aa9ab6 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 21 Dec 2023 08:12:22 +1100 Subject: [PATCH 080/118] fix: circular depdency issues for summary --- gallagher/dto/summary/__init__.py | 1 + gallagher/dto/summary/event.py | 11 ++++++++--- gallagher/dto/summary/visitor.py | 5 ++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/gallagher/dto/summary/__init__.py b/gallagher/dto/summary/__init__.py index b9b5d2e4..b987548c 100644 --- a/gallagher/dto/summary/__init__.py +++ b/gallagher/dto/summary/__init__.py @@ -32,6 +32,7 @@ from .event import ( EventSummary, EventGroupSummary, + EventTypeSummary, ) from .items import ( diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index 276018b2..5eaacbe9 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -6,9 +6,14 @@ IdentityMixin, ) -from ..summary import ( - EventTypeSummary -) + +class EventTypeSummary( + AppBaseModel, + IdentityMixin, +): + """ An event type has identifiers and names + """ + name: str class EventSummary( diff --git a/gallagher/dto/summary/visitor.py b/gallagher/dto/summary/visitor.py index b5ba9589..215e8229 100644 --- a/gallagher/dto/summary/visitor.py +++ b/gallagher/dto/summary/visitor.py @@ -1,4 +1,4 @@ -from .utils import ( +from ..utils import ( AppBaseModel, IdentityMixin ) @@ -7,9 +7,8 @@ AccessGroupRef ) -from ..summary import ( +from .access_group import ( AccessGroupSummary, - VisitorTypeSummary, ) From 780269e81b822bb10b4492d5664dfd8333b623e5 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 21 Dec 2023 08:16:17 +1100 Subject: [PATCH 081/118] fix: import issues with detail package --- gallagher/dto/detail/access_group.py | 17 ++++++++--------- gallagher/dto/detail/cardholder.py | 3 +-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/gallagher/dto/detail/access_group.py b/gallagher/dto/detail/access_group.py index d196d03d..00b9baf5 100644 --- a/gallagher/dto/detail/access_group.py +++ b/gallagher/dto/detail/access_group.py @@ -5,20 +5,19 @@ IdentityMixin, HrefMixin ) - -from ..ref.access_group import ( - AccessGroupRef +from ..summary import ( + AlarmZoneSummary, + AccessSummary, + SaltoAccessItemSummary, +) +from ..ref import ( + AccessGroupRef, + PDFRef, ) from .division import ( DivisionDetail ) -from ..summary import ( - AccessSummary -) -from ..ref import ( - PDFRef -) class AccessGroupDetail( diff --git a/gallagher/dto/detail/cardholder.py b/gallagher/dto/detail/cardholder.py index 766a7604..f4057af6 100644 --- a/gallagher/dto/detail/cardholder.py +++ b/gallagher/dto/detail/cardholder.py @@ -1,7 +1,6 @@ from typing import Optional -from datetime import datetime -from .utils import ( +from ..utils import ( AppBaseModel, IdentityMixin, HrefMixin, From 99e6d2d2e865fa0376cdee70ffbf7f99745b0c8f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 21 Dec 2023 08:22:06 +1100 Subject: [PATCH 082/118] fix: import issues in response package --- gallagher/dto/response/card_type.py | 2 +- gallagher/dto/response/discover.py | 4 ++++ gallagher/dto/response/door.py | 4 ++++ gallagher/dto/response/event.py | 4 ++++ gallagher/dto/response/schedule.py | 2 +- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/gallagher/dto/response/card_type.py b/gallagher/dto/response/card_type.py index bca86f5c..482978c7 100644 --- a/gallagher/dto/response/card_type.py +++ b/gallagher/dto/response/card_type.py @@ -6,7 +6,7 @@ ) from ..detail import ( - CardTypeDEtail + CardTypeDetail ) diff --git a/gallagher/dto/response/discover.py b/gallagher/dto/response/discover.py index fac94b60..a42be437 100644 --- a/gallagher/dto/response/discover.py +++ b/gallagher/dto/response/discover.py @@ -6,6 +6,10 @@ AppBaseModel, ) +from ..detail import ( + FeaturesDetail, +) + class DiscoveryResponse( AppBaseModel, diff --git a/gallagher/dto/response/door.py b/gallagher/dto/response/door.py index 926524c3..da5b906f 100644 --- a/gallagher/dto/response/door.py +++ b/gallagher/dto/response/door.py @@ -2,6 +2,10 @@ AppBaseModel, ) +from ..summary import ( + DoorSummary, +) + class DoorSummaryResponse( AppBaseModel diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index c2fd4457..c93d9133 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -6,6 +6,10 @@ IdentityMixin, ) +from ..summary import ( + EventGroupSummary, +) + class EventSummaryResponse( AppBaseModel, diff --git a/gallagher/dto/response/schedule.py b/gallagher/dto/response/schedule.py index e936316d..3c0479fe 100644 --- a/gallagher/dto/response/schedule.py +++ b/gallagher/dto/response/schedule.py @@ -2,7 +2,7 @@ AppBaseModel, ) -from ..ref import ( +from ..summary import ( ScheduleSummary ) From 87079981c47fe8dedba302b105528f7dd9ac7292 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 21 Dec 2023 08:46:17 +1100 Subject: [PATCH 083/118] refactor: enables event response these attributes highlighted the original cause of the circular imports enabling the attributes now proves that the circular depdenncy is no longer an issue and that the library should scale as expected this should now be merged into dto-implementaiton for completion of the models and endpoints REFS #21 --- gallagher/dto/response/event.py | 37 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index c93d9133..bed7c23b 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -6,8 +6,15 @@ IdentityMixin, ) +from ..ref import ( + AlarmRef, + CardholderRef, + DoorRef, +) + from ..summary import ( EventGroupSummary, + EventTypeSummary, ) @@ -23,21 +30,21 @@ class EventSummaryResponse( message: Optional[str] occurrences: int priority: int - # alarm: AlarmRef - - # operator: CardholderRef - # source: str - # group: str - # type: str - # event_type: EventTypeSummary - # division: str - # cardholder: str - # entry_access_zone: str - # exit_access_zone: str - # door: DoorRef - # access_group: HrefMixin - # card: str - # modified_item: str + alarm: AlarmRef + + operator: CardholderRef + source: str + group: str + type: str + event_type: EventTypeSummary + division: str + cardholder: str + entry_access_zone: str + exit_access_zone: str + door: DoorRef + access_group: HrefMixin + card: str + modified_item: str next: HrefMixin previous: HrefMixin From b8efb17487384767bd084c8e4d05ba085f83e702 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 21 Dec 2023 09:55:52 +1100 Subject: [PATCH 084/118] fix: ref configuration --- gallagher/dto/ref/zone.py | 4 +++- gallagher/dto/response/event.py | 15 +++++++++------ gallagher/dto/summary/cardholder.py | 4 +++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/gallagher/dto/ref/zone.py b/gallagher/dto/ref/zone.py index 97485a14..64adac8e 100644 --- a/gallagher/dto/ref/zone.py +++ b/gallagher/dto/ref/zone.py @@ -1,11 +1,13 @@ from ..utils import ( AppBaseModel, - HrefMixin + IdentityMixin, + HrefMixin, ) class AccessZoneRef( AppBaseModel, + IdentityMixin, HrefMixin ): """ AccessZone represents diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index bed7c23b..42942648 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -10,11 +10,14 @@ AlarmRef, CardholderRef, DoorRef, + AccessZoneRef, + DivisionRef, ) from ..summary import ( EventGroupSummary, EventTypeSummary, + CardholderSummary, ) @@ -34,13 +37,13 @@ class EventSummaryResponse( operator: CardholderRef source: str - group: str - type: str + group: EventGroupSummary + type: EventTypeSummary event_type: EventTypeSummary - division: str - cardholder: str - entry_access_zone: str - exit_access_zone: str + division: DivisionRef + cardholder: CardholderSummary + entry_access_zone: AccessZoneRef + exit_access_zone: AccessZoneRef door: DoorRef access_group: HrefMixin card: str diff --git a/gallagher/dto/summary/cardholder.py b/gallagher/dto/summary/cardholder.py index a646c6de..e68f7ace 100644 --- a/gallagher/dto/summary/cardholder.py +++ b/gallagher/dto/summary/cardholder.py @@ -3,12 +3,14 @@ from ..utils import ( AppBaseModel, IdentityMixin, + HrefMixin, ) class CardholderSummary( AppBaseModel, - IdentityMixin + IdentityMixin, + HrefMixin, ): """ The cardholder search at /api/cardholders returns an array of these. From 8d3e93c5b8e8f3a00b7311239e68ac4eba8ea49f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 15:52:11 +1100 Subject: [PATCH 085/118] refactor: removes unsolicitated code --- gallagher/dto/ref/event.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py index 12f47841..443965d7 100644 --- a/gallagher/dto/ref/event.py +++ b/gallagher/dto/ref/event.py @@ -4,12 +4,3 @@ AppBaseModel, IdentityMixin, ) - - -class EventTypeSummary( - AppBaseModel, - IdentityMixin, -): - """ An event type has identifiers and names - """ - name: str From 32352abc5126ad84a92ff19ac0209164995f6cce Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 15:52:25 +1100 Subject: [PATCH 086/118] test: adds event test --- tests/test_events.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_events.py b/tests/test_events.py index 23ec66e6..3a04cd7f 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -11,3 +11,16 @@ async def test_event_types(): response = await EventType.list() assert type(response) is EventTypeResponse + + +async def test_event_summary(): + + from gallagher.cc.alarms.events import ( + Event + ) + from gallagher.dto.response import ( + EventSummaryResponse, + ) + + response = await Event.list() + assert type(response) is EventSummaryResponse From 5017e84fe11bd925e46ef2ad08cf3da0d97cd013 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 15:53:27 +1100 Subject: [PATCH 087/118] fix: optionals on cardholder missing default value --- gallagher/dto/summary/cardholder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gallagher/dto/summary/cardholder.py b/gallagher/dto/summary/cardholder.py index e68f7ace..ed041010 100644 --- a/gallagher/dto/summary/cardholder.py +++ b/gallagher/dto/summary/cardholder.py @@ -24,7 +24,7 @@ class CardholderSummary( last_name: str short_name: Optional[str] = None description: Optional[str] = None - authorised: bool + authorised: bool = False def __rich_repr__(self): return [ From ef68cf68c2d7adbf980b9963320f329b86846fbc Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 15:54:08 +1100 Subject: [PATCH 088/118] refactor: event log parsing working --- gallagher/cc/alarms/events.py | 7 +++- gallagher/dto/detail/__init__.py | 4 ++ gallagher/dto/detail/event.py | 45 ++++++++++++++++++++++ gallagher/dto/ref/__init__.py | 1 + gallagher/dto/ref/cardholder.py | 20 ++++++++++ gallagher/dto/response/event.py | 35 +---------------- gallagher/dto/summary/event.py | 64 ++++++++++++++++++++++---------- 7 files changed, 122 insertions(+), 54 deletions(-) diff --git a/gallagher/cc/alarms/events.py b/gallagher/cc/alarms/events.py index dd3e6e90..d32083fc 100644 --- a/gallagher/cc/alarms/events.py +++ b/gallagher/cc/alarms/events.py @@ -10,8 +10,13 @@ EndpointConfig ) +from ...dto.detail import ( + EventDetail, +) + from ...dto.response import ( EventTypeResponse, + EventSummaryResponse, ) @@ -26,7 +31,7 @@ class Event( async def get_config(cls): return EndpointConfig( endpoint=Capabilities.CURRENT.features.events.events, - dto_list=EventResponse, + dto_list=EventSummaryResponse, dto_retrieve=EventDetail, ) diff --git a/gallagher/dto/detail/__init__.py b/gallagher/dto/detail/__init__.py index 763d4995..942bc432 100644 --- a/gallagher/dto/detail/__init__.py +++ b/gallagher/dto/detail/__init__.py @@ -23,6 +23,10 @@ DivisionDetail, ) +from .event import ( + EventDetail, +) + from .pdf import ( PDFDetail, ) diff --git a/gallagher/dto/detail/event.py b/gallagher/dto/detail/event.py index e69de29b..1ed751fa 100644 --- a/gallagher/dto/detail/event.py +++ b/gallagher/dto/detail/event.py @@ -0,0 +1,45 @@ +from typing import Optional + +from ..utils import ( + AppBaseModel, + HrefMixin, + IdentityMixin, +) + +from ..ref import ( + AlarmRef, + CardholderRef, + DoorRef, + AccessZoneRef, + DivisionRef, + ItemRef, +) + + +class EventDetail( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ Details of an event that took place on a server + """ + server_display_name: str + time: str + message: Optional[str] + occurrences: int + priority: int + alarm: AlarmRef + + operator: CardholderRef + source: ItemRef + # group: EventGroupSummary + # type: EventTypeSummary + # event_type: EventTypeSummary + # division: DivisionRef + # cardholder: CardholderSummary + entry_access_zone: AccessZoneRef + exit_access_zone: AccessZoneRef + door: DoorRef + access_group: HrefMixin + # card: str + # modified_item: str diff --git a/gallagher/dto/ref/__init__.py b/gallagher/dto/ref/__init__.py index 19835901..7b76815b 100644 --- a/gallagher/dto/ref/__init__.py +++ b/gallagher/dto/ref/__init__.py @@ -23,6 +23,7 @@ from .cardholder import ( CardholderRef, + CardholderEventRef, ) from .day_category import ( diff --git a/gallagher/dto/ref/cardholder.py b/gallagher/dto/ref/cardholder.py index 07971ca6..1043cf79 100644 --- a/gallagher/dto/ref/cardholder.py +++ b/gallagher/dto/ref/cardholder.py @@ -1,6 +1,10 @@ +from typing import Optional + from ..utils import ( AppBaseModel, HrefMixin, + IdentityMixin, + OptionalHref, ) @@ -11,3 +15,19 @@ class CardholderRef( """ Reference to a Cardholder """ name: str + + +class CardholderEventRef( + AppBaseModel, + IdentityMixin, + OptionalHref, +): + """ Cardholder ref used with events + + Events seem to send partial information where href is missing at times + (this is contrary to the documentation), name and id seem to always + be present. This is likely because this is a summary + """ + name: str + first_name: Optional[str] = None + last_name: Optional[str] = None diff --git a/gallagher/dto/response/event.py b/gallagher/dto/response/event.py index 42942648..9ac45a93 100644 --- a/gallagher/dto/response/event.py +++ b/gallagher/dto/response/event.py @@ -6,48 +6,17 @@ IdentityMixin, ) -from ..ref import ( - AlarmRef, - CardholderRef, - DoorRef, - AccessZoneRef, - DivisionRef, -) - from ..summary import ( EventGroupSummary, - EventTypeSummary, - CardholderSummary, + EventSummary, ) class EventSummaryResponse( AppBaseModel, - HrefMixin, - IdentityMixin, ): - """ - """ - server_display_name: str - time: str - message: Optional[str] - occurrences: int - priority: int - alarm: AlarmRef - operator: CardholderRef - source: str - group: EventGroupSummary - type: EventTypeSummary - event_type: EventTypeSummary - division: DivisionRef - cardholder: CardholderSummary - entry_access_zone: AccessZoneRef - exit_access_zone: AccessZoneRef - door: DoorRef - access_group: HrefMixin - card: str - modified_item: str + events: list[EventSummary] next: HrefMixin previous: HrefMixin diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index 5eaacbe9..b21c80d3 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -4,6 +4,21 @@ AppBaseModel, HrefMixin, IdentityMixin, + OptionalHref, +) + +from ..ref import ( + AlarmRef, + CardholderRef, + CardholderEventRef, + DoorRef, + AccessZoneRef, + DivisionRef, + ItemRef, +) + +from .cardholder import ( + CardholderSummary, ) @@ -16,26 +31,6 @@ class EventTypeSummary( name: str -class EventSummary( - AppBaseModel, - IdentityMixin, - HrefMixin -): - """ Events that the Command Centre generates - - """ - server_display_name: str - time: str - message: Optional[str] - occurrences: int - priority: int - - # Hrefs to follows other events - next: HrefMixin - previous: HrefMixin - updates: HrefMixin - - class EventGroupSummary( AppBaseModel, IdentityMixin, @@ -47,3 +42,32 @@ class EventGroupSummary( """ name: str event_types: list[EventTypeSummary] + + +class EventSummary( + AppBaseModel, + HrefMixin, + IdentityMixin, +): + """ Summary of events that have occurred on the server + """ + server_display_name: Optional[str] = None + time: str + message: Optional[str] = None + occurrences: Optional[int] = 0 + priority: int + alarm: Optional[AlarmRef] = None + + operator: Optional[CardholderRef] = None + source: ItemRef + # group: Optional[EventGroupSummary] = None + type: Optional[EventTypeSummary] = None + event_type: Optional[EventTypeSummary] = None + division: Optional[DivisionRef] = None + cardholder: Optional[CardholderEventRef] = None + entry_access_zone: Optional[AccessZoneRef] = None + exit_access_zone: Optional[AccessZoneRef] = None + door: Optional[DoorRef] = None + access_group: OptionalHref = None + # card: str + # modified_item: str From 425f3adbd4d482910a1da94807f33c4497845af6 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 16:44:31 +1100 Subject: [PATCH 089/118] refactor: finishes events handler wires up remainder of the EventSummary class to parse cardholder and modifiedItem, relaxes a couple of rules in other ItemSummary to reuse that class --- gallagher/dto/ref/__init__.py | 4 ++++ gallagher/dto/ref/event.py | 12 ++++++++++++ gallagher/dto/summary/__init__.py | 1 + gallagher/dto/summary/card_type.py | 15 ++++++++++++++- gallagher/dto/summary/event.py | 15 ++++++++++----- gallagher/dto/summary/items.py | 5 +++-- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/gallagher/dto/ref/__init__.py b/gallagher/dto/ref/__init__.py index 7b76815b..8530250a 100644 --- a/gallagher/dto/ref/__init__.py +++ b/gallagher/dto/ref/__init__.py @@ -66,6 +66,10 @@ DoorRef, ) +from .event import ( + EventGroupRef, +) + from .items import ( ItemRef, ) diff --git a/gallagher/dto/ref/event.py b/gallagher/dto/ref/event.py index 443965d7..df4312bd 100644 --- a/gallagher/dto/ref/event.py +++ b/gallagher/dto/ref/event.py @@ -4,3 +4,15 @@ AppBaseModel, IdentityMixin, ) + + +class EventGroupRef( + AppBaseModel, + IdentityMixin, +): + """ Event Group Reference + + This is a reference to an event group, it is used in the + event type response. + """ + name: str diff --git a/gallagher/dto/summary/__init__.py b/gallagher/dto/summary/__init__.py index b987548c..12b0795d 100644 --- a/gallagher/dto/summary/__init__.py +++ b/gallagher/dto/summary/__init__.py @@ -15,6 +15,7 @@ from .card_type import ( CardExpiryTypeSummary, + CardSummary, ) from .cardholder import ( diff --git a/gallagher/dto/summary/card_type.py b/gallagher/dto/summary/card_type.py index f93c16b1..67ef118c 100644 --- a/gallagher/dto/summary/card_type.py +++ b/gallagher/dto/summary/card_type.py @@ -3,7 +3,6 @@ from ..utils import ( AppBaseModel, IdentityMixin, - HrefMixin ) @@ -11,3 +10,17 @@ class CardExpiryTypeSummary( AppBaseModel ): expiry_type: Optional[str] = None + + +class CardSummary( + AppBaseModel, + IdentityMixin, +): + """ Card summary as sent by the Event objects + + Note: that we should revise this if required + + """ + facility_code: str + number: str + issue_level: int diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index b21c80d3..6b9470f2 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -15,10 +15,15 @@ AccessZoneRef, DivisionRef, ItemRef, + EventGroupRef, ) -from .cardholder import ( - CardholderSummary, +from .card_type import ( + CardSummary, +) + +from .items import ( + ItemSummary, ) @@ -60,7 +65,7 @@ class EventSummary( operator: Optional[CardholderRef] = None source: ItemRef - # group: Optional[EventGroupSummary] = None + group: Optional[EventGroupRef] = None type: Optional[EventTypeSummary] = None event_type: Optional[EventTypeSummary] = None division: Optional[DivisionRef] = None @@ -69,5 +74,5 @@ class EventSummary( exit_access_zone: Optional[AccessZoneRef] = None door: Optional[DoorRef] = None access_group: OptionalHref = None - # card: str - # modified_item: str + card: Optional[CardSummary] = None + modified_item: Optional[ItemSummary] = None diff --git a/gallagher/dto/summary/items.py b/gallagher/dto/summary/items.py index a9c5233e..593390e1 100644 --- a/gallagher/dto/summary/items.py +++ b/gallagher/dto/summary/items.py @@ -1,3 +1,4 @@ +from typing import Optional from ..utils import ( AppBaseModel, @@ -16,7 +17,7 @@ class ItemTypeSummary( """ id: str name: str - canonical_type_name: str + canonical_type_name: Optional[str] = None class ItemSummary( @@ -27,5 +28,5 @@ class ItemSummary( """ Summary of an Item which adds the notes and server_display_name, this is used by the item summary response """ - name: str + name: Optional[str] = None type: ItemTypeSummary From 538251a76fe29f30125b43ef2ff0bf4c27a37953 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 17:23:09 +1100 Subject: [PATCH 090/118] refactor: time stamps are now datetime objects pyndatic can parse iso date strings into datetime objects, this turns all timestamp attributes into datetime objects --- gallagher/dto/detail/event.py | 3 ++- gallagher/dto/summary/alarm.py | 3 ++- gallagher/dto/summary/event.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gallagher/dto/detail/event.py b/gallagher/dto/detail/event.py index 1ed751fa..85d933c6 100644 --- a/gallagher/dto/detail/event.py +++ b/gallagher/dto/detail/event.py @@ -1,4 +1,5 @@ from typing import Optional +from datetime import datetime from ..utils import ( AppBaseModel, @@ -24,7 +25,7 @@ class EventDetail( """ Details of an event that took place on a server """ server_display_name: str - time: str + time: datetime message: Optional[str] occurrences: int priority: int diff --git a/gallagher/dto/summary/alarm.py b/gallagher/dto/summary/alarm.py index 44f8ec73..90226578 100644 --- a/gallagher/dto/summary/alarm.py +++ b/gallagher/dto/summary/alarm.py @@ -1,4 +1,5 @@ from typing import Optional +from datetime import datetime from ..utils import ( AppBaseModel, @@ -32,7 +33,7 @@ class AlarmZoneSummary( property in the access_group schema. I don't know if this is appropriate """ - time: str + time: datetime message: str source: AlarmSourceSummary type: str diff --git a/gallagher/dto/summary/event.py b/gallagher/dto/summary/event.py index 6b9470f2..775fec9d 100644 --- a/gallagher/dto/summary/event.py +++ b/gallagher/dto/summary/event.py @@ -1,4 +1,5 @@ from typing import Optional +from datetime import datetime from ..utils import ( AppBaseModel, @@ -57,7 +58,7 @@ class EventSummary( """ Summary of events that have occurred on the server """ server_display_name: Optional[str] = None - time: str + time: datetime message: Optional[str] = None occurrences: Optional[int] = 0 priority: int From e5cb185e83d9ba7b555f1cfd1ad42b2711757e83 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 17:23:14 +1100 Subject: [PATCH 091/118] docs: update todo --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 54267bdf..99a88668 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Alarm history entry - [ ] Alarm update request - [ ] Event search -- [ ] Event summary +- [x] Event summary - [ ] Event detail - [ ] Event POST body - [x] Event groups From aac14f87ecb832bcdd54ec71c0c042f0e4f46a2f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 23 Dec 2023 17:23:22 +1100 Subject: [PATCH 092/118] feat: adds events cli --- gallagher/cli/__init__.py | 3 ++- gallagher/cli/alarms.py | 4 +++- gallagher/cli/cardholders.py | 4 +++- gallagher/cli/events.py | 12 ++++++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 gallagher/cli/events.py diff --git a/gallagher/cli/__init__.py b/gallagher/cli/__init__.py index 97f75a11..01b52b4c 100644 --- a/gallagher/cli/__init__.py +++ b/gallagher/cli/__init__.py @@ -18,6 +18,7 @@ from .alarms import app as alarms_app from .cardholders import app as cardholders_app +from .events import app as events_app # Load the API key for the package so all entry points # can use it to query the service @@ -29,7 +30,7 @@ # Load up all sub commands app.add_typer(alarms_app, name="alarms") app.add_typer(cardholders_app, name="ch") - +app.add_typer(events_app, name="events") if __name__ == "__main__": """ In case you are invoking this via Python directly diff --git a/gallagher/cli/alarms.py b/gallagher/cli/alarms.py index bc1dc811..4fd3ce4c 100644 --- a/gallagher/cli/alarms.py +++ b/gallagher/cli/alarms.py @@ -7,4 +7,6 @@ from .utils import AsyncTyper -app = AsyncTyper() +app = AsyncTyper( + help="list or query alarms in the command centre" +) diff --git a/gallagher/cli/cardholders.py b/gallagher/cli/cardholders.py index 218e0579..a2c3c00b 100644 --- a/gallagher/cli/cardholders.py +++ b/gallagher/cli/cardholders.py @@ -11,7 +11,9 @@ Cardholder ) -app = AsyncTyper(help="query or manage cardholders") +app = AsyncTyper( + help="query or manage cardholders" +) @app.command("list") diff --git a/gallagher/cli/events.py b/gallagher/cli/events.py new file mode 100644 index 00000000..5ea2b24f --- /dev/null +++ b/gallagher/cli/events.py @@ -0,0 +1,12 @@ +""" Cardholder events + + +""" + + +from .utils import AsyncTyper + + +app = AsyncTyper( + help="query command centre events" +) From cb13421482a7f6c3e9e5aa7044f8ea8fa05b1a84 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 24 Dec 2023 13:03:13 +1100 Subject: [PATCH 093/118] feat: use pyndatic hooks to set timestamp the base model has a field called good_known_since, the intention is to set a timestamp once a response has been parsed, so the application can use this to identify if the response is stale and if it requires revalidation pyndatic provides a post_init hook, this introduces use of this hook in the BaseModel https://docs.pydantic.dev/2.0/api/main/\#pydantic.main.BaseModel.model_post_init --- gallagher/dto/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gallagher/dto/utils.py b/gallagher/dto/utils.py index f0806010..73f2e685 100644 --- a/gallagher/dto/utils.py +++ b/gallagher/dto/utils.py @@ -48,6 +48,15 @@ class AppBaseModel(BaseModel): # This is generally used for caching good_known_since: Optional[datetime] = None + def model_post_init(self, __context) -> None: + """ + The model_post_init method is called after the model is + initialized, this is used to set the good_known_since + + https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_post_init + """ + self.good_known_since = datetime.now() + class IdentityMixin(BaseModel): """ Identifier From 36728fa39f014e665e06f1827469ead933d38142 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 24 Dec 2023 15:00:11 +1100 Subject: [PATCH 094/118] fix: disable mode_post_init to see if tests pass --- gallagher/dto/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gallagher/dto/utils.py b/gallagher/dto/utils.py index 73f2e685..b953fa05 100644 --- a/gallagher/dto/utils.py +++ b/gallagher/dto/utils.py @@ -48,14 +48,14 @@ class AppBaseModel(BaseModel): # This is generally used for caching good_known_since: Optional[datetime] = None - def model_post_init(self, __context) -> None: - """ - The model_post_init method is called after the model is - initialized, this is used to set the good_known_since - - https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_post_init - """ - self.good_known_since = datetime.now() + # def model_post_init(self, __context) -> None: + # """ + # The model_post_init method is called after the model is + # initialized, this is used to set the good_known_since + + # https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_post_init + # """ + # self.good_known_since = datetime.now() class IdentityMixin(BaseModel): From 94d2af7751217ca0d8a440805cc9d7c061312b1c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 27 Dec 2023 11:47:02 +1100 Subject: [PATCH 095/118] fix: discovery response cache not being utilised the original design intended to cache the Discovery response to conform hateoas https://github.com/anomaly/gallagher/issues/5 overriding the pydantic method model_post_init to set the good_known_since var introduced an error where the test first test would run the subsequent calls would fail https://docs.pydantic.dev/2.0/api/main/\#pydantic.main.BaseModel.model_post_init debugging this i found that the discover was running every time as the good_known_since would never have been set and the cache check would fail. initially thought of this as a bug with how parsing was working but turns out that if the cache was detected then we were returning without initialising the configuration against the discovery response. --- gallagher/cc/core.py | 7 ++++--- gallagher/dto/utils.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 49305a66..647a61e6 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -170,9 +170,10 @@ async def _discover(cls): if Capabilities.CURRENT.version != "0.0.0" and\ type(Capabilities.CURRENT.good_known_since) is datetime: - # We've already discovered the endpoint hence - # we can stop execution to improve performance - # and avoid network round trips. + # We've already discovered the endpoints as per HATEOAS + # design requirement, however because the endpoint configuration is + # dynamically populated, we have to call the get_config method + cls.__config__ = await cls.get_config() return # Auto-discovery of the API endpoints, this will diff --git a/gallagher/dto/utils.py b/gallagher/dto/utils.py index b953fa05..7eac5472 100644 --- a/gallagher/dto/utils.py +++ b/gallagher/dto/utils.py @@ -4,7 +4,7 @@ """ from typing import ( - Optional + Optional, ) from datetime import datetime @@ -48,14 +48,14 @@ class AppBaseModel(BaseModel): # This is generally used for caching good_known_since: Optional[datetime] = None - # def model_post_init(self, __context) -> None: - # """ - # The model_post_init method is called after the model is - # initialized, this is used to set the good_known_since + def model_post_init(self, __context) -> None: + """ + The model_post_init method is called after the model is + initialized, this is used to set the good_known_since - # https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_post_init - # """ - # self.good_known_since = datetime.now() + https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_post_init + """ + self.good_known_since = datetime.now() class IdentityMixin(BaseModel): From 69ca87c2f6643652c8468fd9ed1df0f9e544c358 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Wed, 27 Dec 2023 11:55:38 +1100 Subject: [PATCH 096/118] chore: update depdenencies and version number --- gallagher/__init__.py | 2 +- poetry.lock | 334 +++++++++++++++++++++--------------------- pyproject.toml | 6 +- tests/test_version.py | 2 +- 4 files changed, 172 insertions(+), 172 deletions(-) diff --git a/gallagher/__init__.py b/gallagher/__init__.py index 391ec27b..3852360a 100644 --- a/gallagher/__init__.py +++ b/gallagher/__init__.py @@ -7,4 +7,4 @@ Distributed under the terms of the MIT License. """ -__version__ = "0.1.0-alpha.1" +__version__ = "0.1.0-alpha.2" diff --git a/poetry.lock b/poetry.lock index 251e3065..58f08182 100644 --- a/poetry.lock +++ b/poetry.lock @@ -83,63 +83,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.3" +version = "7.3.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d874434e0cb7b90f7af2b6e3309b0733cde8ec1476eb47db148ed7deeb2a9494"}, - {file = "coverage-7.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee6621dccce8af666b8c4651f9f43467bfbf409607c604b840b78f4ff3619aeb"}, - {file = "coverage-7.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1367aa411afb4431ab58fd7ee102adb2665894d047c490649e86219327183134"}, - {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f0f8f0c497eb9c9f18f21de0750c8d8b4b9c7000b43996a094290b59d0e7523"}, - {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0338c4b0951d93d547e0ff8d8ea340fecf5885f5b00b23be5aa99549e14cfd"}, - {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d31650d313bd90d027f4be7663dfa2241079edd780b56ac416b56eebe0a21aab"}, - {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9437a4074b43c177c92c96d051957592afd85ba00d3e92002c8ef45ee75df438"}, - {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17d9cb06c13b4f2ef570355fa45797d10f19ca71395910b249e3f77942a837"}, - {file = "coverage-7.3.3-cp310-cp310-win32.whl", hash = "sha256:eee5e741b43ea1b49d98ab6e40f7e299e97715af2488d1c77a90de4a663a86e2"}, - {file = "coverage-7.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:593efa42160c15c59ee9b66c5f27a453ed3968718e6e58431cdfb2d50d5ad284"}, - {file = "coverage-7.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c944cf1775235c0857829c275c777a2c3e33032e544bcef614036f337ac37bb"}, - {file = "coverage-7.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eda7f6e92358ac9e1717ce1f0377ed2b9320cea070906ece4e5c11d172a45a39"}, - {file = "coverage-7.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c854c1d2c7d3e47f7120b560d1a30c1ca221e207439608d27bc4d08fd4aeae8"}, - {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:222b038f08a7ebed1e4e78ccf3c09a1ca4ac3da16de983e66520973443b546bc"}, - {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff4800783d85bff132f2cc7d007426ec698cdce08c3062c8d501ad3f4ea3d16c"}, - {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fc200cec654311ca2c3f5ab3ce2220521b3d4732f68e1b1e79bef8fcfc1f2b97"}, - {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:307aecb65bb77cbfebf2eb6e12009e9034d050c6c69d8a5f3f737b329f4f15fb"}, - {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ffb0eacbadb705c0a6969b0adf468f126b064f3362411df95f6d4f31c40d31c1"}, - {file = "coverage-7.3.3-cp311-cp311-win32.whl", hash = "sha256:79c32f875fd7c0ed8d642b221cf81feba98183d2ff14d1f37a1bbce6b0347d9f"}, - {file = "coverage-7.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:243576944f7c1a1205e5cd658533a50eba662c74f9be4c050d51c69bd4532936"}, - {file = "coverage-7.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a2ac4245f18057dfec3b0074c4eb366953bca6787f1ec397c004c78176a23d56"}, - {file = "coverage-7.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9191be7af41f0b54324ded600e8ddbcabea23e1e8ba419d9a53b241dece821d"}, - {file = "coverage-7.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c0b1b8b5a4aebf8fcd227237fc4263aa7fa0ddcd4d288d42f50eff18b0bac4"}, - {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee453085279df1bac0996bc97004771a4a052b1f1e23f6101213e3796ff3cb85"}, - {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1191270b06ecd68b1d00897b2daddb98e1719f63750969614ceb3438228c088e"}, - {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:007a7e49831cfe387473e92e9ff07377f6121120669ddc39674e7244350a6a29"}, - {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:af75cf83c2d57717a8493ed2246d34b1f3398cb8a92b10fd7a1858cad8e78f59"}, - {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:811ca7373da32f1ccee2927dc27dc523462fd30674a80102f86c6753d6681bc6"}, - {file = "coverage-7.3.3-cp312-cp312-win32.whl", hash = "sha256:733537a182b5d62184f2a72796eb6901299898231a8e4f84c858c68684b25a70"}, - {file = "coverage-7.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:e995efb191f04b01ced307dbd7407ebf6e6dc209b528d75583277b10fd1800ee"}, - {file = "coverage-7.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbd8a5fe6c893de21a3c6835071ec116d79334fbdf641743332e442a3466f7ea"}, - {file = "coverage-7.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:50c472c1916540f8b2deef10cdc736cd2b3d1464d3945e4da0333862270dcb15"}, - {file = "coverage-7.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e9223a18f51d00d3ce239c39fc41410489ec7a248a84fab443fbb39c943616c"}, - {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f501e36ac428c1b334c41e196ff6bd550c0353c7314716e80055b1f0a32ba394"}, - {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475de8213ed95a6b6283056d180b2442eee38d5948d735cd3d3b52b86dd65b92"}, - {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afdcc10c01d0db217fc0a64f58c7edd635b8f27787fea0a3054b856a6dff8717"}, - {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fff0b2f249ac642fd735f009b8363c2b46cf406d3caec00e4deeb79b5ff39b40"}, - {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a1f76cfc122c9e0f62dbe0460ec9cc7696fc9a0293931a33b8870f78cf83a327"}, - {file = "coverage-7.3.3-cp38-cp38-win32.whl", hash = "sha256:757453848c18d7ab5d5b5f1827293d580f156f1c2c8cef45bfc21f37d8681069"}, - {file = "coverage-7.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad2453b852a1316c8a103c9c970db8fbc262f4f6b930aa6c606df9b2766eee06"}, - {file = "coverage-7.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b15e03b8ee6a908db48eccf4e4e42397f146ab1e91c6324da44197a45cb9132"}, - {file = "coverage-7.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89400aa1752e09f666cc48708eaa171eef0ebe3d5f74044b614729231763ae69"}, - {file = "coverage-7.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c59a3e59fb95e6d72e71dc915e6d7fa568863fad0a80b33bc7b82d6e9f844973"}, - {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ede881c7618f9cf93e2df0421ee127afdfd267d1b5d0c59bcea771cf160ea4a"}, - {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3bfd2c2f0e5384276e12b14882bf2c7621f97c35320c3e7132c156ce18436a1"}, - {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f3bad1a9313401ff2964e411ab7d57fb700a2d5478b727e13f156c8f89774a0"}, - {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:65d716b736f16e250435473c5ca01285d73c29f20097decdbb12571d5dfb2c94"}, - {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a702e66483b1fe602717020a0e90506e759c84a71dbc1616dd55d29d86a9b91f"}, - {file = "coverage-7.3.3-cp39-cp39-win32.whl", hash = "sha256:7fbf3f5756e7955174a31fb579307d69ffca91ad163467ed123858ce0f3fd4aa"}, - {file = "coverage-7.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cad9afc1644b979211989ec3ff7d82110b2ed52995c2f7263e7841c846a75348"}, - {file = "coverage-7.3.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:d299d379b676812e142fb57662a8d0d810b859421412b4d7af996154c00c31bb"}, - {file = "coverage-7.3.3.tar.gz", hash = "sha256:df04c64e58df96b4427db8d0559e95e2df3138c9916c96f9f6a4dd220db2fdb7"}, + {file = "coverage-7.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aff2bd3d585969cc4486bfc69655e862028b689404563e6b549e6a8244f226df"}, + {file = "coverage-7.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4353923f38d752ecfbd3f1f20bf7a3546993ae5ecd7c07fd2f25d40b4e54571"}, + {file = "coverage-7.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea473c37872f0159294f7073f3fa72f68b03a129799f3533b2bb44d5e9fa4f82"}, + {file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5214362abf26e254d749fc0c18af4c57b532a4bfde1a057565616dd3b8d7cc94"}, + {file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99b7d3f7a7adfa3d11e3a48d1a91bb65739555dd6a0d3fa68aa5852d962e5b1"}, + {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:74397a1263275bea9d736572d4cf338efaade2de9ff759f9c26bcdceb383bb49"}, + {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f154bd866318185ef5865ace5be3ac047b6d1cc0aeecf53bf83fe846f4384d5d"}, + {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e0d84099ea7cba9ff467f9c6f747e3fc3906e2aadac1ce7b41add72e8d0a3712"}, + {file = "coverage-7.3.4-cp310-cp310-win32.whl", hash = "sha256:3f477fb8a56e0c603587b8278d9dbd32e54bcc2922d62405f65574bd76eba78a"}, + {file = "coverage-7.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:c75738ce13d257efbb6633a049fb2ed8e87e2e6c2e906c52d1093a4d08d67c6b"}, + {file = "coverage-7.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:997aa14b3e014339d8101b9886063c5d06238848905d9ad6c6eabe533440a9a7"}, + {file = "coverage-7.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a9c5bc5db3eb4cd55ecb8397d8e9b70247904f8eca718cc53c12dcc98e59fc8"}, + {file = "coverage-7.3.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27ee94f088397d1feea3cb524e4313ff0410ead7d968029ecc4bc5a7e1d34fbf"}, + {file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ce03e25e18dd9bf44723e83bc202114817f3367789052dc9e5b5c79f40cf59d"}, + {file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85072e99474d894e5df582faec04abe137b28972d5e466999bc64fc37f564a03"}, + {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a877810ef918d0d345b783fc569608804f3ed2507bf32f14f652e4eaf5d8f8d0"}, + {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9ac17b94ab4ca66cf803f2b22d47e392f0977f9da838bf71d1f0db6c32893cb9"}, + {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36d75ef2acab74dc948d0b537ef021306796da551e8ac8b467810911000af66a"}, + {file = "coverage-7.3.4-cp311-cp311-win32.whl", hash = "sha256:47ee56c2cd445ea35a8cc3ad5c8134cb9bece3a5cb50bb8265514208d0a65928"}, + {file = "coverage-7.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:11ab62d0ce5d9324915726f611f511a761efcca970bd49d876cf831b4de65be5"}, + {file = "coverage-7.3.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:33e63c578f4acce1b6cd292a66bc30164495010f1091d4b7529d014845cd9bee"}, + {file = "coverage-7.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:782693b817218169bfeb9b9ba7f4a9f242764e180ac9589b45112571f32a0ba6"}, + {file = "coverage-7.3.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c4277ddaad9293454da19121c59f2d850f16bcb27f71f89a5c4836906eb35ef"}, + {file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d892a19ae24b9801771a5a989fb3e850bd1ad2e2b6e83e949c65e8f37bc67a1"}, + {file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3024ec1b3a221bd10b5d87337d0373c2bcaf7afd86d42081afe39b3e1820323b"}, + {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1c3e9d2bbd6f3f79cfecd6f20854f4dc0c6e0ec317df2b265266d0dc06535f1"}, + {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e91029d7f151d8bf5ab7d8bfe2c3dbefd239759d642b211a677bc0709c9fdb96"}, + {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6879fe41c60080aa4bb59703a526c54e0412b77e649a0d06a61782ecf0853ee1"}, + {file = "coverage-7.3.4-cp312-cp312-win32.whl", hash = "sha256:fd2f8a641f8f193968afdc8fd1697e602e199931012b574194052d132a79be13"}, + {file = "coverage-7.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:d1d0ce6c6947a3a4aa5479bebceff2c807b9f3b529b637e2b33dea4468d75fc7"}, + {file = "coverage-7.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36797b3625d1da885b369bdaaa3b0d9fb8865caed3c2b8230afaa6005434aa2f"}, + {file = "coverage-7.3.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfed0ec4b419fbc807dec417c401499ea869436910e1ca524cfb4f81cf3f60e7"}, + {file = "coverage-7.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f97ff5a9fc2ca47f3383482858dd2cb8ddbf7514427eecf5aa5f7992d0571429"}, + {file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:607b6c6b35aa49defaebf4526729bd5238bc36fe3ef1a417d9839e1d96ee1e4c"}, + {file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8e258dcc335055ab59fe79f1dec217d9fb0cdace103d6b5c6df6b75915e7959"}, + {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a02ac7c51819702b384fea5ee033a7c202f732a2a2f1fe6c41e3d4019828c8d3"}, + {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b710869a15b8caf02e31d16487a931dbe78335462a122c8603bb9bd401ff6fb2"}, + {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6a23ae9348a7a92e7f750f9b7e828448e428e99c24616dec93a0720342f241d"}, + {file = "coverage-7.3.4-cp38-cp38-win32.whl", hash = "sha256:758ebaf74578b73f727acc4e8ab4b16ab6f22a5ffd7dd254e5946aba42a4ce76"}, + {file = "coverage-7.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:309ed6a559bc942b7cc721f2976326efbfe81fc2b8f601c722bff927328507dc"}, + {file = "coverage-7.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aefbb29dc56317a4fcb2f3857d5bce9b881038ed7e5aa5d3bcab25bd23f57328"}, + {file = "coverage-7.3.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:183c16173a70caf92e2dfcfe7c7a576de6fa9edc4119b8e13f91db7ca33a7923"}, + {file = "coverage-7.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a4184dcbe4f98d86470273e758f1d24191ca095412e4335ff27b417291f5964"}, + {file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93698ac0995516ccdca55342599a1463ed2e2d8942316da31686d4d614597ef9"}, + {file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb220b3596358a86361139edce40d97da7458412d412e1e10c8e1970ee8c09ab"}, + {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5b14abde6f8d969e6b9dd8c7a013d9a2b52af1235fe7bebef25ad5c8f47fa18"}, + {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:610afaf929dc0e09a5eef6981edb6a57a46b7eceff151947b836d869d6d567c1"}, + {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d6ed790728fb71e6b8247bd28e77e99d0c276dff952389b5388169b8ca7b1c28"}, + {file = "coverage-7.3.4-cp39-cp39-win32.whl", hash = "sha256:c15fdfb141fcf6a900e68bfa35689e1256a670db32b96e7a931cab4a0e1600e5"}, + {file = "coverage-7.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:38d0b307c4d99a7aca4e00cad4311b7c51b7ac38fb7dea2abe0d182dd4008e05"}, + {file = "coverage-7.3.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b1e0f25ae99cf247abfb3f0fac7ae25739e4cd96bf1afa3537827c576b4847e5"}, + {file = "coverage-7.3.4.tar.gz", hash = "sha256:020d56d2da5bc22a0e00a5b0d54597ee91ad72446fa4cf1b97c35022f6b6dbf0"}, ] [package.extras] @@ -179,13 +179,13 @@ trio = ["trio (>=0.22.0,<0.23.0)"] [[package]] name = "httpx" -version = "0.25.2" +version = "0.26.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"}, - {file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"}, + {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, + {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, ] [package.dependencies] @@ -286,18 +286,18 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.5.2" +version = "2.5.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, - {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, + {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, + {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.5" +pydantic-core = "2.14.6" typing-extensions = ">=4.6.1" [package.extras] @@ -305,116 +305,116 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.5" +version = "2.14.6" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, - {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, - {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, - {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, - {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, - {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, - {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, - {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, - {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, - {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, - {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, - {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, - {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, - {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, - {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, - {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, + {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, + {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, + {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, + {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, + {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, + {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, + {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, + {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, + {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, + {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, + {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, + {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, + {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, + {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, + {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, ] [package.dependencies] @@ -583,4 +583,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e89eca878d5a623d4c943065a133978fafba45e1928b87024480f11a1b16357b" +content-hash = "e890536ac968b6a67565b478490f501c69f9015327a1ba2cd20b8db789e45742" diff --git a/pyproject.toml b/pyproject.toml index c17c0b51..75da485b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,10 +18,10 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.11" -httpx = "^0.25.2" -pydantic = "^2.5.2" +httpx = "^0.26.0" +pydantic = "^2.5.3" typing-extensions = "^4.9.0" -coverage = "^7.3.3" +coverage = "^7.3.4" pytest = "^7.4.3" pytest-order = "^1.2.0" annotated-types = "^0.6.0" diff --git a/tests/test_version.py b/tests/test_version.py index 0944a78f..cea33e34 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -7,4 +7,4 @@ async def test_version(): - assert __version__ == '0.1.0-alpha.1' + assert __version__ == '0.1.0-alpha.2' From 1d201f0a464a4d0817e0d04688477a621fcba230 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 2 Jan 2024 08:01:12 +1100 Subject: [PATCH 097/118] chore: add annotations --- gallagher/cc/alarms/__init__.py | 2 +- gallagher/cc/alarms/day_category.py | 2 +- gallagher/cc/alarms/divisions.py | 2 +- gallagher/cc/alarms/events.py | 4 ++-- gallagher/cc/alarms/items.py | 4 ++-- gallagher/cc/alarms/schedule.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gallagher/cc/alarms/__init__.py b/gallagher/cc/alarms/__init__.py index 623e59a4..e9cb0185 100644 --- a/gallagher/cc/alarms/__init__.py +++ b/gallagher/cc/alarms/__init__.py @@ -25,7 +25,7 @@ class Alarms( """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.alarms.alarms, dto_list=AlarmResponse, diff --git a/gallagher/cc/alarms/day_category.py b/gallagher/cc/alarms/day_category.py index 108c37e4..021d26bb 100644 --- a/gallagher/cc/alarms/day_category.py +++ b/gallagher/cc/alarms/day_category.py @@ -19,7 +19,7 @@ class DayCategory(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.day_categories .day_categories, diff --git a/gallagher/cc/alarms/divisions.py b/gallagher/cc/alarms/divisions.py index 7093449d..661c7e44 100644 --- a/gallagher/cc/alarms/divisions.py +++ b/gallagher/cc/alarms/divisions.py @@ -26,7 +26,7 @@ class Division(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.divisions.divisions, dto_list=DivisionDetailResponse, diff --git a/gallagher/cc/alarms/events.py b/gallagher/cc/alarms/events.py index d32083fc..54daef02 100644 --- a/gallagher/cc/alarms/events.py +++ b/gallagher/cc/alarms/events.py @@ -28,7 +28,7 @@ class Event( """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.events.events, dto_list=EventSummaryResponse, @@ -44,7 +44,7 @@ class EventType( """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.events.event_groups, dto_list=EventTypeResponse, diff --git a/gallagher/cc/alarms/items.py b/gallagher/cc/alarms/items.py index 6f2d3a24..62175e71 100644 --- a/gallagher/cc/alarms/items.py +++ b/gallagher/cc/alarms/items.py @@ -24,7 +24,7 @@ class ItemsTypes(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.items.item_types, dto_list=ItemTypesResponse, @@ -41,7 +41,7 @@ class Item(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.items.items, dto_list=ItemsSummaryResponse, diff --git a/gallagher/cc/alarms/schedule.py b/gallagher/cc/alarms/schedule.py index 1fb50c9c..49dceeda 100644 --- a/gallagher/cc/alarms/schedule.py +++ b/gallagher/cc/alarms/schedule.py @@ -18,7 +18,7 @@ class Schedule(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.schedules.schedules, dto_list=ScheduleSummaryResponse, From 55b77ecab0e12ce2be719d36cedde9735c90c99c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 2 Jan 2024 08:01:26 +1100 Subject: [PATCH 098/118] docs: adds thinking beihnd cli and api design --- docs/docs/cli.md | 46 +++++++++++++++++++++++++++++++++++++ docs/docs/design.md | 55 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 93c42785..c74a96d7 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -1 +1,47 @@ # Command Line Interface + +We provide a command line interface to interact with the Gallagher Command Centre. It uses the API client to communicate with the server, which doubly serves as a reference example of how to use the API client. + +> We use [typer](https://typer.tiangolo.com) to construct the CLI, which in turn uses [click](https://click.palletsprojects.com). We also use [rich](https://rich.readthedocs.io/en/stable/) to make the output nicer. The CLI is decoupled from the API client, and is not install by default. + +We follow a `git` like `command`, `sub-command` pattern, so it should feel quite familiar. + +poetry will install the alias `gal` for you to interact with the CLI. You can ask for help with: + +``` +gal --help +``` + +which will list the available commands: + +``` + Usage: gal [OPTIONS] COMMAND [ARGS]... + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --install-completion Install completion for the current shell. │ +│ --show-completion Show completion for the current shell, to copy it or customize the installation. │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ alarms list or query alarms in the command centre │ +│ ch query or manage cardholders │ +│ events query command centre events │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + +A simple example to get a list of cardholders looks like: + +``` +(gallagher-py3.11) ➜ gallagher git:(dto-implementation) ✗ gal ch list + Cardholders +┏━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ +┃ Id ┃ First name ┃ Last name ┃ Authorised ┃ +┡━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ +│ 8246 │ Johnetta │ Abdallah │ yes │ +│ 7936 │ Socorro │ Abrahams │ yes │ +│ 8374 │ Geoffrey │ Acey │ yes │ +│ 8370 │ Weldon │ Acuff │ yes │ +│ 7922 │ Rusty │ Adelsperger │ yes │ +``` + +you can also ask each sub command to give you in diff --git a/docs/docs/design.md b/docs/docs/design.md index 8659fcfa..90509957 100644 --- a/docs/docs/design.md +++ b/docs/docs/design.md @@ -1,9 +1,58 @@ # Design -This follows how we have designed +A central feature to this client is it's detailed design, focused on a superior developer experience and performance. We also ensure that we follow design patterns outlined by Gallagher + +## Data Transfer Objects + +This a central part of our design. There are three types of schema definitions, each one of them suffixed with their intent: + +- **Ref** are `References` to other objects, they using contain a `href` and possibly additional meta data such as a `name` or `id` +- **Summary** is what is returned by the Gallagher API in operations such as [searches](https://gallaghersecurity.github.io/cc-rest-docs/ref/cardholders.html), these are generally a subset of the full object +- **Detail** are the full object found at a particular `href`, they compound on the `Summary` schema and add additional attributes + +I additional we have classes that defined responses which are suffixed with **Response**, these wrap structures which returns `hrefs` for `next` and `previous` responses and usually have a collection to hold the response. + +Ensure that each Endpoint defines their own DTOs so you can test them for authenticity. Avoid writing generic classes. + +While `Refs`, `Summary` and `Detail` responses have fields, and it would make sense from an efficiency point of view to inherit e.g `Summary` builds on `Ref`, this should be avoided so logically an instance of a `Ref` class doesn't assert true for `isinstance` of a `Summary` class. + +## API Client Core + +The `core` package in `cc` provides two important classes: + +- `APIEndpoint` which all endpoint consumers configuration must inherit from +- `EndpointConfig` an instance of which each class must return as a result of the `get_config` method + +Every Endpoint Consumer Class is expected to return an instance of `EndpointConfig` from the `get_config` method. Each configuration provides references to paths that are dynamically discovered as part of our bootstrapping process. + +Never hard code URLs as this violates the [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) design principle. + +Additionally each configuration will provide references to DTO classes that is used to parse responses, and details of the body. + +``` +class Alarms( + APIEndpoint +): + """ Alarms + """ + + @classmethod + async def get_config(cls) -> EndpointConfig: + return EndpointConfig( + endpoint=Capabilities.CURRENT.features.alarms.alarms, + dto_list=AlarmResponse, + dto_retrieve=AlarmZoneSummary, + ) +``` + +The above example shows the `Alarms` class which is a consumer of the `alarms` endpoint. It nominates `AlarmResponse` as the class the infrastructure will use to parse `list` responses and `AlarmZoneSummary` as the class to parse `retrieve` responses. + +It references the `Capabilities.CURRENT` singleton which is a `Capabilities` instance that is bootstrapped at runtime. This is a singleton that is used to provide references to all endpoints. + +If a command centre does not have a certain capability then the objects are set to `None` and accessing the feature raises an exception (more on this in other sections). + +### Designing Endpoints ## Layout Layout of our files - -## Data Transfer Objects From a818cd2925f702cef3bf57d7b55736e9ee00896a Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 2 Jan 2024 08:01:38 +1100 Subject: [PATCH 099/118] refactor: adds annotations --- gallagher/cc/cardholders/card_type.py | 2 +- gallagher/cc/cardholders/cardholders.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gallagher/cc/cardholders/card_type.py b/gallagher/cc/cardholders/card_type.py index 9560abe9..2aece3f9 100644 --- a/gallagher/cc/cardholders/card_type.py +++ b/gallagher/cc/cardholders/card_type.py @@ -21,7 +21,7 @@ class CardType(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.card_types.card_types, dto_list=CardTypeResponse, diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 87d2fce4..426aa097 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -25,7 +25,7 @@ class Cardholder(APIEndpoint): """ @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: return EndpointConfig( endpoint=Capabilities.CURRENT.features.cardholders.cardholders, dto_list=CardholderSummaryResponse, From 53bd8b8ba18adfde5cb729fd1ed05a5aafd635aa Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 2 Jan 2024 08:02:41 +1100 Subject: [PATCH 100/118] refactor: adds annotations and raises exception by default by default we raise an exception on the get_config method in the base class this ensures that only classes that implement (i.e real classes) the get_config method execute --- gallagher/cc/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gallagher/cc/core.py b/gallagher/cc/core.py index 647a61e6..8a8cd422 100644 --- a/gallagher/cc/core.py +++ b/gallagher/cc/core.py @@ -139,13 +139,15 @@ class APIEndpoint: __config__ = None @classmethod - async def get_config(cls): + async def get_config(cls) -> EndpointConfig: """ Returns the configuration for the endpoint This method can be overridden by the child class to provide additional configuration options. """ - return None + raise NotImplementedError( + "get_config method not implemented" + ) @classmethod async def _discover(cls): From 27242e9576a36e077f69a6191fa5f89feee753d7 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 5 Jan 2024 17:36:35 +1100 Subject: [PATCH 101/118] chore: adds tap support test anything protocol is a highly parseable output format https://testanything.org/philosophy.html this adds the plugin to pytest to produce tap output --- Taskfile.yml | 2 +- poetry.lock | 31 ++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 919cfb10..848c1d53 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -14,7 +14,7 @@ tasks: runs all the tests inside the virtualenv, optionally provide the name of the test as an argument to run a single test cmds: - - poetry run coverage run -m pytest -s tests/{{.CLI_ARGS}} + - poetry run coverage run -m pytest -s --tap tests/{{.CLI_ARGS}} test:list: desc: lists the available tests summary: diff --git a/poetry.lock b/poetry.lock index 58f08182..d21671e8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -505,6 +505,21 @@ files = [ [package.dependencies] pytest = {version = ">=6.2.4", markers = "python_version >= \"3.10\""} +[[package]] +name = "pytest-tap" +version = "3.4" +description = "Test Anything Protocol (TAP) reporting plugin for pytest" +optional = false +python-versions = "*" +files = [ + {file = "pytest-tap-3.4.tar.gz", hash = "sha256:a7c2a4a3e8b4bf18522e46d74208f8579a191dd972c59182104ad9a4967318fb"}, + {file = "pytest_tap-3.4-py3-none-any.whl", hash = "sha256:d97a2115c94415086f6faec395d243b3c18ea846ce1c1653a4b2588082be35d8"}, +] + +[package.dependencies] +pytest = ">=3.0" +"tap.py" = ">=3.0,<4.0" + [[package]] name = "rich" version = "13.7.0" @@ -545,6 +560,20 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "tap-py" +version = "3.1" +description = "Test Anything Protocol (TAP) tools" +optional = false +python-versions = "*" +files = [ + {file = "tap.py-3.1-py3-none-any.whl", hash = "sha256:928c852f3361707b796c93730cc5402c6378660b161114461066acf53d65bf5d"}, + {file = "tap.py-3.1.tar.gz", hash = "sha256:3c0cd45212ad5a25b35445964e2517efa000a118a1bfc3437dae828892eaf1e1"}, +] + +[package.extras] +yaml = ["PyYAML (>=5.1)", "more-itertools"] + [[package]] name = "typer" version = "0.9.0" @@ -583,4 +612,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e890536ac968b6a67565b478490f501c69f9015327a1ba2cd20b8db789e45742" +content-hash = "a8da851337e41b4386ea4d299182ca1de4ae9ed837b7c9b396c55a4481b28dc6" diff --git a/pyproject.toml b/pyproject.toml index 75da485b..2a3e5c6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ typer = {extras = ["all"], version = "^0.9.0"} rich = "^13.7.0" pytest-asyncio = "^0.23.2" asyncer = "^0.0.2" +pytest-tap = "^3.4" [tool.poetry.group.dev.dependencies] pytest = "^7.2.2" From 269c9db0acbdbabac87665b24fdd70c58ac87e3c Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 6 Jan 2024 15:43:10 +1100 Subject: [PATCH 102/118] refactor: separate poetry packages starts work on #26 in readiness #25 --- README.md | 2 +- poetry.lock | 134 ++++++++++++++++++++++--------------------------- pyproject.toml | 26 +++++----- 3 files changed, 74 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 75ab96f7..93285790 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Gallagher Command Centre REST API Client -> Python idiomatic client for Gallagher Command Centre API +> Python idiomatic client and tools for Gallagher Command Centre API Gallagher Security manufacture a variety of [security products](https://security.gallagher.com) all of which are controlled by their [Command Centre](https://products.security.gallagher.com/security/au/en_AU/products/software/command-centre/p/C201311) software. Traditionally Command Centre has been a Windows based server product. Version `8.6` introduced a REST API which allows you to interact with the system via HTTP requests. Gallagher also provide a [Cloud API Gateway](https://gallaghersecurity.github.io/docs/Command%20Centre%20Cloud%20Api%20Gateway%20TIP.pdf) which allows third party integrations to securely communicate with the Command Centre on site. diff --git a/poetry.lock b/poetry.lock index d21671e8..c7ad7538 100644 --- a/poetry.lock +++ b/poetry.lock @@ -83,63 +83,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.4" +version = "7.4.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aff2bd3d585969cc4486bfc69655e862028b689404563e6b549e6a8244f226df"}, - {file = "coverage-7.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4353923f38d752ecfbd3f1f20bf7a3546993ae5ecd7c07fd2f25d40b4e54571"}, - {file = "coverage-7.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea473c37872f0159294f7073f3fa72f68b03a129799f3533b2bb44d5e9fa4f82"}, - {file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5214362abf26e254d749fc0c18af4c57b532a4bfde1a057565616dd3b8d7cc94"}, - {file = "coverage-7.3.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99b7d3f7a7adfa3d11e3a48d1a91bb65739555dd6a0d3fa68aa5852d962e5b1"}, - {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:74397a1263275bea9d736572d4cf338efaade2de9ff759f9c26bcdceb383bb49"}, - {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f154bd866318185ef5865ace5be3ac047b6d1cc0aeecf53bf83fe846f4384d5d"}, - {file = "coverage-7.3.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e0d84099ea7cba9ff467f9c6f747e3fc3906e2aadac1ce7b41add72e8d0a3712"}, - {file = "coverage-7.3.4-cp310-cp310-win32.whl", hash = "sha256:3f477fb8a56e0c603587b8278d9dbd32e54bcc2922d62405f65574bd76eba78a"}, - {file = "coverage-7.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:c75738ce13d257efbb6633a049fb2ed8e87e2e6c2e906c52d1093a4d08d67c6b"}, - {file = "coverage-7.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:997aa14b3e014339d8101b9886063c5d06238848905d9ad6c6eabe533440a9a7"}, - {file = "coverage-7.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a9c5bc5db3eb4cd55ecb8397d8e9b70247904f8eca718cc53c12dcc98e59fc8"}, - {file = "coverage-7.3.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27ee94f088397d1feea3cb524e4313ff0410ead7d968029ecc4bc5a7e1d34fbf"}, - {file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ce03e25e18dd9bf44723e83bc202114817f3367789052dc9e5b5c79f40cf59d"}, - {file = "coverage-7.3.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85072e99474d894e5df582faec04abe137b28972d5e466999bc64fc37f564a03"}, - {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a877810ef918d0d345b783fc569608804f3ed2507bf32f14f652e4eaf5d8f8d0"}, - {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9ac17b94ab4ca66cf803f2b22d47e392f0977f9da838bf71d1f0db6c32893cb9"}, - {file = "coverage-7.3.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36d75ef2acab74dc948d0b537ef021306796da551e8ac8b467810911000af66a"}, - {file = "coverage-7.3.4-cp311-cp311-win32.whl", hash = "sha256:47ee56c2cd445ea35a8cc3ad5c8134cb9bece3a5cb50bb8265514208d0a65928"}, - {file = "coverage-7.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:11ab62d0ce5d9324915726f611f511a761efcca970bd49d876cf831b4de65be5"}, - {file = "coverage-7.3.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:33e63c578f4acce1b6cd292a66bc30164495010f1091d4b7529d014845cd9bee"}, - {file = "coverage-7.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:782693b817218169bfeb9b9ba7f4a9f242764e180ac9589b45112571f32a0ba6"}, - {file = "coverage-7.3.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c4277ddaad9293454da19121c59f2d850f16bcb27f71f89a5c4836906eb35ef"}, - {file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d892a19ae24b9801771a5a989fb3e850bd1ad2e2b6e83e949c65e8f37bc67a1"}, - {file = "coverage-7.3.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3024ec1b3a221bd10b5d87337d0373c2bcaf7afd86d42081afe39b3e1820323b"}, - {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1c3e9d2bbd6f3f79cfecd6f20854f4dc0c6e0ec317df2b265266d0dc06535f1"}, - {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e91029d7f151d8bf5ab7d8bfe2c3dbefd239759d642b211a677bc0709c9fdb96"}, - {file = "coverage-7.3.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6879fe41c60080aa4bb59703a526c54e0412b77e649a0d06a61782ecf0853ee1"}, - {file = "coverage-7.3.4-cp312-cp312-win32.whl", hash = "sha256:fd2f8a641f8f193968afdc8fd1697e602e199931012b574194052d132a79be13"}, - {file = "coverage-7.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:d1d0ce6c6947a3a4aa5479bebceff2c807b9f3b529b637e2b33dea4468d75fc7"}, - {file = "coverage-7.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36797b3625d1da885b369bdaaa3b0d9fb8865caed3c2b8230afaa6005434aa2f"}, - {file = "coverage-7.3.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfed0ec4b419fbc807dec417c401499ea869436910e1ca524cfb4f81cf3f60e7"}, - {file = "coverage-7.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f97ff5a9fc2ca47f3383482858dd2cb8ddbf7514427eecf5aa5f7992d0571429"}, - {file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:607b6c6b35aa49defaebf4526729bd5238bc36fe3ef1a417d9839e1d96ee1e4c"}, - {file = "coverage-7.3.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8e258dcc335055ab59fe79f1dec217d9fb0cdace103d6b5c6df6b75915e7959"}, - {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a02ac7c51819702b384fea5ee033a7c202f732a2a2f1fe6c41e3d4019828c8d3"}, - {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b710869a15b8caf02e31d16487a931dbe78335462a122c8603bb9bd401ff6fb2"}, - {file = "coverage-7.3.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6a23ae9348a7a92e7f750f9b7e828448e428e99c24616dec93a0720342f241d"}, - {file = "coverage-7.3.4-cp38-cp38-win32.whl", hash = "sha256:758ebaf74578b73f727acc4e8ab4b16ab6f22a5ffd7dd254e5946aba42a4ce76"}, - {file = "coverage-7.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:309ed6a559bc942b7cc721f2976326efbfe81fc2b8f601c722bff927328507dc"}, - {file = "coverage-7.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aefbb29dc56317a4fcb2f3857d5bce9b881038ed7e5aa5d3bcab25bd23f57328"}, - {file = "coverage-7.3.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:183c16173a70caf92e2dfcfe7c7a576de6fa9edc4119b8e13f91db7ca33a7923"}, - {file = "coverage-7.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a4184dcbe4f98d86470273e758f1d24191ca095412e4335ff27b417291f5964"}, - {file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93698ac0995516ccdca55342599a1463ed2e2d8942316da31686d4d614597ef9"}, - {file = "coverage-7.3.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb220b3596358a86361139edce40d97da7458412d412e1e10c8e1970ee8c09ab"}, - {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5b14abde6f8d969e6b9dd8c7a013d9a2b52af1235fe7bebef25ad5c8f47fa18"}, - {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:610afaf929dc0e09a5eef6981edb6a57a46b7eceff151947b836d869d6d567c1"}, - {file = "coverage-7.3.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d6ed790728fb71e6b8247bd28e77e99d0c276dff952389b5388169b8ca7b1c28"}, - {file = "coverage-7.3.4-cp39-cp39-win32.whl", hash = "sha256:c15fdfb141fcf6a900e68bfa35689e1256a670db32b96e7a931cab4a0e1600e5"}, - {file = "coverage-7.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:38d0b307c4d99a7aca4e00cad4311b7c51b7ac38fb7dea2abe0d182dd4008e05"}, - {file = "coverage-7.3.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b1e0f25ae99cf247abfb3f0fac7ae25739e4cd96bf1afa3537827c576b4847e5"}, - {file = "coverage-7.3.4.tar.gz", hash = "sha256:020d56d2da5bc22a0e00a5b0d54597ee91ad72446fa4cf1b97c35022f6b6dbf0"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, + {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, + {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, + {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, + {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, + {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, + {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, + {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, + {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, + {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, + {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, + {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, + {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, ] [package.extras] @@ -437,13 +437,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -457,13 +457,13 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-asyncio" -version = "0.23.2" +version = "0.23.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"}, - {file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"}, + {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, + {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, ] [package.dependencies] @@ -538,17 +538,6 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - [[package]] name = "sniffio" version = "1.3.0" @@ -587,9 +576,6 @@ files = [ [package.dependencies] click = ">=7.1.1,<9.0.0" -colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"all\""} -rich = {version = ">=10.11.0,<14.0.0", optional = true, markers = "extra == \"all\""} -shellingham = {version = ">=1.3.0,<2.0.0", optional = true, markers = "extra == \"all\""} typing-extensions = ">=3.7.4.3" [package.extras] @@ -612,4 +598,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "a8da851337e41b4386ea4d299182ca1de4ae9ed837b7c9b396c55a4481b28dc6" +content-hash = "688521e38c539e40084fbe09aec58f45db17435a3fd416bebdbe61ce1979092a" diff --git a/pyproject.toml b/pyproject.toml index 2a3e5c6f..7c844599 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "gallagher" -version = "0.1.0-alpha.1" -description = "Python idiomatic client for Gallagher Command Centre API" +version = "0.1.0-alpha.2" +description = "Python idiomatic client and tools for Gallagher Command Centre API" authors = ["Dev Mukherjee "] readme = "README.md" license = "MIT" @@ -21,25 +21,25 @@ python = "^3.11" httpx = "^0.26.0" pydantic = "^2.5.3" typing-extensions = "^4.9.0" -coverage = "^7.3.4" -pytest = "^7.4.3" -pytest-order = "^1.2.0" annotated-types = "^0.6.0" certifi = "^2023.11.17" idna = "^3.6" packaging = "^23.2" pluggy = "^1.3.0" -typer = {extras = ["all"], version = "^0.9.0"} -rich = "^13.7.0" -pytest-asyncio = "^0.23.2" -asyncer = "^0.0.2" -pytest-tap = "^3.4" [tool.poetry.group.dev.dependencies] -pytest = "^7.2.2" -coverage = "^7.2.1" -pytest-order = "^1.0.1" pytest-cov = "^4.1.0" +coverage = "^7.4.0" +pytest = "^7.4.4" +pytest-order = "^1.2.0" +pytest-tap = "^3.4" +pytest-asyncio = "^0.23.3" + + +[tool.poetry.group.cli.dependencies] +asyncer = "^0.0.2" +typer = "^0.9.0" +rich = "^13.7.0" [tool.poetry.scripts] gal = "gallagher.cli:app" From 223733044b3991b6425d06e903d1f98d5d2175b4 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 7 Jan 2024 17:08:10 +1100 Subject: [PATCH 103/118] refactor: pyproject to separate installables REFS #26 --- README.md | 2 +- gallagher/console/__init__.py | 35 +++++++++++++++ poetry.lock | 80 ++++++++++++++++++++++++++++++++++- pyproject.toml | 13 ++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 gallagher/console/__init__.py diff --git a/README.md b/README.md index 93285790..3857cbe4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ > Currently under heavy development, please check for stable release before use -# Gallagher Command Centre REST API Client +# Gallagher Command Centre tools - A REST client, a Command Line Interface and a textual console > Python idiomatic client and tools for Gallagher Command Centre API diff --git a/gallagher/console/__init__.py b/gallagher/console/__init__.py new file mode 100644 index 00000000..a7b5f449 --- /dev/null +++ b/gallagher/console/__init__.py @@ -0,0 +1,35 @@ +""" Console +""" + +from textual.app import App, ComposeResult +from textual.widgets import Header, Footer + + +class GallagherConsole(App): + """A Textual app to manage stopwatches.""" + + BINDINGS = [ + ("d", "toggle_dark", "Toggle dark mode") + ] + + def on_mount(self) -> None: + self.title = "Gallagher" + self.sub_title = "Super charged textual console" + + def compose(self) -> ComposeResult: + """Create child widgets for the app.""" + yield Header() + yield Footer() + + def action_toggle_dark(self) -> None: + """An action to toggle dark mode.""" + self.dark = not self.dark + + +def main(): + app = GallagherConsole() + app.run() + + +if __name__ == "__main__": + main() diff --git a/poetry.lock b/poetry.lock index c7ad7538..3bae8705 100644 --- a/poetry.lock +++ b/poetry.lock @@ -223,6 +223,26 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "linkify-it-py" +version = "2.0.2" +description = "Links recognition library with FULL unicode support." +optional = false +python-versions = ">=3.7" +files = [ + {file = "linkify-it-py-2.0.2.tar.gz", hash = "sha256:19f3060727842c254c808e99d465c80c49d2c7306788140987a1a7a29b0d6ad2"}, + {file = "linkify_it_py-2.0.2-py3-none-any.whl", hash = "sha256:a3a24428f6c96f27370d7fe61d2ac0be09017be5190d68d8658233171f1b6541"}, +] + +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -235,6 +255,8 @@ files = [ ] [package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} mdurl = ">=0.1,<1.0" [package.extras] @@ -247,6 +269,25 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "mdurl" version = "0.1.2" @@ -563,6 +604,25 @@ files = [ [package.extras] yaml = ["PyYAML (>=5.1)", "more-itertools"] +[[package]] +name = "textual" +version = "0.47.1" +description = "Modern Text User Interface framework" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "textual-0.47.1-py3-none-any.whl", hash = "sha256:da79df2e138f6de51bda84a1ee1460936bb2ecf5527ca2d47b9b59c584323327"}, + {file = "textual-0.47.1.tar.gz", hash = "sha256:4b82e317884bb1092f693f474c319ceb068b5a0b128b121f1aa53a2d48b4b80c"}, +] + +[package.dependencies] +markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] + [[package]] name = "typer" version = "0.9.0" @@ -595,7 +655,25 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] +[[package]] +name = "uc-micro-py" +version = "1.0.2" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.2.tar.gz", hash = "sha256:30ae2ac9c49f39ac6dce743bd187fcd2b574b16ca095fa74cd9396795c954c54"}, + {file = "uc_micro_py-1.0.2-py3-none-any.whl", hash = "sha256:8c9110c309db9d9e87302e2f4ad2c3152770930d88ab385cd544e7a7e75f3de0"}, +] + +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + +[extras] +cli = [] +console = [] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "688521e38c539e40084fbe09aec58f45db17435a3fd416bebdbe61ce1979092a" +content-hash = "85503610d2e6f5c0a80c5b9e723ce5bcc17c67ce6e7aa56225ed6afae0cf766c" diff --git a/pyproject.toml b/pyproject.toml index 7c844599..f7d99929 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,13 +9,21 @@ repository = "https://github.com/anomaly/gallagher" documentation = "https://anomaly.github.io/gallagher/" keywords = ["gallagher", "rest", "api"] classifiers = [ + "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Build Tools", "Topic :: Software Development :: Libraries :: Python Modules", + "Operating System :: OS Independent", + "Framework :: Pydantic", + "Framework :: Pydantic :: 2", ] [tool.poetry.urls] "Bug Tracker" = "https://github.com/anomaly/gallagher/issues" +[tool.poetry.extras] +cli = ["asyncer", "typer", "rich"] +console = ["textual"] + [tool.poetry.dependencies] python = "^3.11" httpx = "^0.26.0" @@ -41,8 +49,13 @@ asyncer = "^0.0.2" typer = "^0.9.0" rich = "^13.7.0" + +[tool.poetry.group.console.dependencies] +textual = "^0.47.1" + [tool.poetry.scripts] gal = "gallagher.cli:app" +gcon = "gallagher.console:main" [build-system] requires = ["poetry-core"] From 68e6d2dbda004f93189b39afa676687d5a68370e Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 7 Jan 2024 17:15:35 +1100 Subject: [PATCH 104/118] docs: updated readme --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3857cbe4..73e790a6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -> Currently under heavy development, please check for stable release before use +# Gallagher Python toolkit -# Gallagher Command Centre tools - A REST client, a Command Line Interface and a textual console - -> Python idiomatic client and tools for Gallagher Command Centre API +> Python idiomatic client and tools for Gallagher Command Centre API, featuring a REST API client, a command line interface and a text based console Gallagher Security manufacture a variety of [security products](https://security.gallagher.com) all of which are controlled by their [Command Centre](https://products.security.gallagher.com/security/au/en_AU/products/software/command-centre/p/C201311) software. Traditionally Command Centre has been a Windows based server product. Version `8.6` introduced a REST API which allows you to interact with the system via HTTP requests. Gallagher also provide a [Cloud API Gateway](https://gallaghersecurity.github.io/docs/Command%20Centre%20Cloud%20Api%20Gateway%20TIP.pdf) which allows third party integrations to securely communicate with the Command Centre on site. From c97a80f91524e77ec8213a6fa51b9bf7b12c9ac8 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sun, 7 Jan 2024 17:26:44 +1100 Subject: [PATCH 105/118] fix: typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73e790a6..00102c61 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Gallagher Python toolkit +# Gallagher Python Toolkit > Python idiomatic client and tools for Gallagher Command Centre API, featuring a REST API client, a command line interface and a text based console From d1b6443c37c3dc59e05f90dcdff2bc8c62b0f688 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 22 Jan 2024 10:03:08 +1100 Subject: [PATCH 106/118] feat: adds pdf discovery inline with completing the parsing portion of the library we need to address the pdf data which is dynamically discovered from the command centre, REFS #1 we have started a discussion around this on the pydantic repo https://github.com/pydantic/pydantic/discussions/8596 and must implement the most pydantic version of the solution --- gallagher/cc/cardholders/cardholders.py | 6 +++++- gallagher/dto/ref/pdf.py | 16 ++++++++++++++++ gallagher/dto/response/cardholder.py | 6 +++++- gallagher/dto/response/pdf.py | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 426aa097..7eaa9173 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -33,5 +33,9 @@ async def get_config(cls) -> EndpointConfig: ) @classmethod - async def search(cls, name: str, sort: str = "id", top: int = 100): + async def search(cls, + name: str, + sort: str = "id", + top: int = 100 + ): pass diff --git a/gallagher/dto/ref/pdf.py b/gallagher/dto/ref/pdf.py index a887608b..bd573baa 100644 --- a/gallagher/dto/ref/pdf.py +++ b/gallagher/dto/ref/pdf.py @@ -1,3 +1,12 @@ +""" Personal Data Fields + +Custom fields defined per command centre installation, these need +to be discovered ahead of time before we work with them. + +Note: +- Keys are prefixed with @ +- Presence of space in the field name +""" from ..utils import ( AppBaseModel, HrefMixin, @@ -9,5 +18,12 @@ class PDFRef( HrefMixin ): """ Personal Data Fields are custom fields for a card holder + + These are defined per command centre installation and the fields + are prefixed with @ e.g @Personal URL, note the presence of the space + in the field name. + + Since they are dynamic we will have to discover the personal data + fields much like the URL discovery before we are able to parse the data """ name: str diff --git a/gallagher/dto/response/cardholder.py b/gallagher/dto/response/cardholder.py index 8f223c11..41c2f2f5 100644 --- a/gallagher/dto/response/cardholder.py +++ b/gallagher/dto/response/cardholder.py @@ -1,3 +1,6 @@ +""" + +""" from ..utils import ( AppBaseModel, ) @@ -12,7 +15,8 @@ class CardholderSummaryResponse( ): """ Summary response for cardholder list and search - /api/cardholders is generally the endpoint that + /api/cardholders is generally the endpoint that responds + to the query, it is dynamically configured from the discovery """ results: list[CardholderSummary] diff --git a/gallagher/dto/response/pdf.py b/gallagher/dto/response/pdf.py index e69de29b..2fa3f424 100644 --- a/gallagher/dto/response/pdf.py +++ b/gallagher/dto/response/pdf.py @@ -0,0 +1,16 @@ +""" Personal Data Fields + +""" +from ..utils import ( + AppBaseModel, +) + +from ..ref import ( + PDFRef, +) + + +class PdfResponse( + AppBaseModel, +): + results: list[PDFRef] From 365219dff85109c575c290b0bcb53fa3a56c1b6f Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 22 Jan 2024 10:03:30 +1100 Subject: [PATCH 107/118] chore: adds textual REFS #25 --- poetry.lock | 525 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 3 + 2 files changed, 527 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 3bae8705..54db4796 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,114 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "aiohttp" +version = "3.9.1" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, + {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, + {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, + {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, + {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, + {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, + {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, + {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, + {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, + {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, + {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, + {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "annotated-types" version = "0.6.0" @@ -45,6 +154,25 @@ files = [ [package.dependencies] anyio = ">=3.4.0,<4.0.0" +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + [[package]] name = "certifi" version = "2023.11.17" @@ -145,6 +273,92 @@ files = [ [package.extras] toml = ["tomli"] +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -299,6 +513,154 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "msgpack" +version = "1.0.7" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, + {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, + {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, + {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, + {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, + {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, + {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, + {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, + {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, + {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, + {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, + {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + [[package]] name = "packaging" version = "23.2" @@ -325,6 +687,17 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pprintpp" +version = "0.4.0" +description = "A drop-in replacement for pprint that's actually pretty" +optional = false +python-versions = "*" +files = [ + {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, + {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, +] + [[package]] name = "pydantic" version = "2.5.3" @@ -514,6 +887,21 @@ pytest = ">=7.0.0" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-clarity" +version = "1.0.1" +description = "A plugin providing an alternative, colourful diff output for failing assertions." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pytest-clarity-1.0.1.tar.gz", hash = "sha256:505fe345fad4fe11c6a4187fe683f2c7c52c077caa1e135f3e483fe112db7772"}, +] + +[package.dependencies] +pprintpp = ">=0.4.0" +pytest = ">=3.5.0" +rich = ">=8.0.0" + [[package]] name = "pytest-cov" version = "4.1.0" @@ -623,6 +1011,38 @@ typing-extensions = ">=4.4.0,<5.0.0" [package.extras] syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] +[[package]] +name = "textual-dev" +version = "1.4.0" +description = "Development tools for working with Textual" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "textual_dev-1.4.0-py3-none-any.whl", hash = "sha256:330beec18b8f469adf7cdf9d69cc94965bf3e69d9aec23625d62cdedcadab044"}, + {file = "textual_dev-1.4.0.tar.gz", hash = "sha256:a20ea746a93e66978e9dfe71a7e5409854c96cc3e46550cc40760b199d7a5d3b"}, +] + +[package.dependencies] +aiohttp = ">=3.8.1" +click = ">=8.1.2" +msgpack = ">=1.0.3" +textual = ">=0.36.0" +typing-extensions = ">=4.4.0,<5.0.0" + +[[package]] +name = "textual-select" +version = "0.3.4" +description = "A select widget (aka dropdown) for Textual." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "textual_select-0.3.4-py3-none-any.whl", hash = "sha256:8b315dd041178f95c20000bab4358d49abae4c46fb222c9ec71eebe11d9e3a88"}, + {file = "textual_select-0.3.4.tar.gz", hash = "sha256:ae6767bb9078d2e451caf8f1606785cc9e99037f509a714942fe15660aeaa3e3"}, +] + +[package.dependencies] +textual = ">=0.14.0" + [[package]] name = "typer" version = "0.9.0" @@ -669,6 +1089,109 @@ files = [ [package.extras] test = ["coverage", "pytest", "pytest-cov"] +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + [extras] cli = [] console = [] @@ -676,4 +1199,4 @@ console = [] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "85503610d2e6f5c0a80c5b9e723ce5bcc17c67ce6e7aa56225ed6afae0cf766c" +content-hash = "737e15ffae2c8b60f43e1440d1c7b1e36478248be81093c9dac2d1d79900ae22" diff --git a/pyproject.toml b/pyproject.toml index f7d99929..1929b98f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,8 @@ pytest = "^7.4.4" pytest-order = "^1.2.0" pytest-tap = "^3.4" pytest-asyncio = "^0.23.3" +pytest-clarity = "^1.0.1" +textual-dev = "^1.4.0" [tool.poetry.group.cli.dependencies] @@ -52,6 +54,7 @@ rich = "^13.7.0" [tool.poetry.group.console.dependencies] textual = "^0.47.1" +textual-select = "^0.3.4" [tool.poetry.scripts] gal = "gallagher.cli:app" From 6ea4c2d9ff00331ae0a1569560f8a7f7d11655ca Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 22 Jan 2024 10:03:44 +1100 Subject: [PATCH 108/118] docs: update project definition --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 00102c61..acb14499 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gallagher Python Toolkit -> Python idiomatic client and tools for Gallagher Command Centre API, featuring a REST API client, a command line interface and a text based console +> Python idiomatic REST API client, a command line interface and a text based console for Gallagher Command Centre API Gallagher Security manufacture a variety of [security products](https://security.gallagher.com) all of which are controlled by their [Command Centre](https://products.security.gallagher.com/security/au/en_AU/products/software/command-centre/p/C201311) software. Traditionally Command Centre has been a Windows based server product. Version `8.6` introduced a REST API which allows you to interact with the system via HTTP requests. Gallagher also provide a [Cloud API Gateway](https://gallaghersecurity.github.io/docs/Command%20Centre%20Cloud%20Api%20Gateway%20TIP.pdf) which allows third party integrations to securely communicate with the Command Centre on site. @@ -17,8 +17,7 @@ from gallagher import cc, const cc.api_key = "GH_" -cc.discover() -cc.Customer.create() +cc.Customer.list() ``` > Note this project is **NOT** officially affiliated with Gallagher Security @@ -129,7 +128,7 @@ We use [Taskfile](https://taskfile.dev) to automate running tasks. The project provides a comprehensive set of tests which can be run with `task test`. These tests do create objects in the Command Centre, we advice you to obtain a test license. -**DO NOT** run the tests against a production system. +> It's **not recommended** to run tests against a production system. ### Data Transfer Objects @@ -153,7 +152,7 @@ Resources are `fetchable`, `queryable`, `creatable`, `updatable` and `deletable` Responses can be the object itself or a response layout -# Configuring the Command Centre +## Configuring the Command Centre The following requires you to have an understanding of the Gallagher Command Centre and how to configure it. If you are unsure, please contact your Gallagher representative. @@ -181,6 +180,6 @@ To check your API key: ![Command Centre Cloud Connections](assets/gallagher-rest-properties.png) -# License +## License Distributed under the MIT License. From 5a6ae43701a65066c52f4b0dc52c43752e7b7358 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 22 Jan 2024 10:03:56 +1100 Subject: [PATCH 109/118] feat: adds runner for textual console REFS #25 --- Taskfile.yml | 9 +++++++ gallagher/console/__init__.py | 44 +++++++++++++++++++++++++------- gallagher/console/gallagher.tcss | 6 +++++ gallagher/console/overview.py | 11 ++++++++ 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 gallagher/console/gallagher.tcss create mode 100644 gallagher/console/overview.py diff --git a/Taskfile.yml b/Taskfile.yml index 848c1d53..d6141f68 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,3 +25,12 @@ tasks: desc: runs coverage inside the server container cmds: - poetry run coverage report -m + dev:textual: + desc: runs the textual cli + cmds: + - poetry run textual -- + dev:gcon: + desc: runs text gallagher console in dev mode + cmds: + - poetry run textual run --dev gallagher.console:main + diff --git a/gallagher/console/__init__.py b/gallagher/console/__init__.py index a7b5f449..c72dab19 100644 --- a/gallagher/console/__init__.py +++ b/gallagher/console/__init__.py @@ -1,28 +1,54 @@ """ Console """ -from textual.app import App, ComposeResult -from textual.widgets import Header, Footer +from textual.app import ( + App, + ComposeResult +) + +from textual.containers import ( + Container, + Horizontal, + Grid, +) + +from textual.widgets import ( + Header, + Footer, + Placeholder, +) class GallagherConsole(App): - """A Textual app to manage stopwatches.""" + """ A console interface for the Gallagher Command Centre. + """ + + CSS_PATH = "gallagher.tcss" BINDINGS = [ - ("d", "toggle_dark", "Toggle dark mode") + ("d", "toggle_dark", "Toggle dark mode"), + ("ctrl+q", "quit", "Quit") ] - def on_mount(self) -> None: - self.title = "Gallagher" - self.sub_title = "Super charged textual console" + # Decorative constants + TITLE = "Gallagher" + SUB_TITLE = "Super charged textual console" + + # def on_mount(self) -> None: def compose(self) -> ComposeResult: - """Create child widgets for the app.""" + """ Create child widgets for the app. + """ yield Header() + with Grid(): + yield Placeholder(id="a") + yield Placeholder(id="b") + yield Placeholder(id="c") yield Footer() def action_toggle_dark(self) -> None: - """An action to toggle dark mode.""" + """ An action to toggle dark mode. + """ self.dark = not self.dark diff --git a/gallagher/console/gallagher.tcss b/gallagher/console/gallagher.tcss new file mode 100644 index 00000000..7c5c2f0c --- /dev/null +++ b/gallagher/console/gallagher.tcss @@ -0,0 +1,6 @@ +Grid { + grid-size: 3 3; + layout: grid; +} +#a { +} \ No newline at end of file diff --git a/gallagher/console/overview.py b/gallagher/console/overview.py new file mode 100644 index 00000000..747eea34 --- /dev/null +++ b/gallagher/console/overview.py @@ -0,0 +1,11 @@ +""" + + +""" +from textual.containers import ( + Container, +) + + +class Dashboard(Container): + pass From dfb3ef28902843ce6ad6b06dfd9161b6f3413de0 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 22 Jan 2024 10:05:00 +1100 Subject: [PATCH 110/118] chore: update version number --- gallagher/__init__.py | 2 +- tests/test_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gallagher/__init__.py b/gallagher/__init__.py index 3852360a..9398a6aa 100644 --- a/gallagher/__init__.py +++ b/gallagher/__init__.py @@ -7,4 +7,4 @@ Distributed under the terms of the MIT License. """ -__version__ = "0.1.0-alpha.2" +__version__ = "0.1.0-alpha.3" diff --git a/tests/test_version.py b/tests/test_version.py index cea33e34..77e8770f 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -7,4 +7,4 @@ async def test_version(): - assert __version__ == '0.1.0-alpha.2' + assert __version__ == '0.1.0-alpha.3' From e2c14aef825ea2eb03526b573f99152ba7f48ed2 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Tue, 23 Jan 2024 09:13:50 +1100 Subject: [PATCH 111/118] refactor: adds tests for pdf list doing some ground work towards #1 where we need pdf fields to be auto discovered for the dynamically parseable feature to work properly this commit parse the basic list call for PdfDefinitions, detail and other preloading functions to be added post this commit --- gallagher/cc/cardholders/cardholders.py | 28 +++++++++++++++++++++++++ gallagher/dto/detail/__init__.py | 2 +- gallagher/dto/detail/pdf.py | 2 +- gallagher/dto/response/__init__.py | 4 ++++ gallagher/dto/response/pdf.py | 13 ++++++++++++ tests/test_cardholder.py | 2 +- tests/test_pdf.py | 13 ++++++++++++ 7 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/test_pdf.py diff --git a/gallagher/cc/cardholders/cardholders.py b/gallagher/cc/cardholders/cardholders.py index 7eaa9173..6a96164e 100644 --- a/gallagher/cc/cardholders/cardholders.py +++ b/gallagher/cc/cardholders/cardholders.py @@ -9,10 +9,12 @@ from ...dto.detail import ( CardholderDetail, + PdfDetail, ) from ...dto.response import ( CardholderSummaryResponse, + PdfResponse, ) @@ -39,3 +41,29 @@ async def search(cls, top: int = 100 ): pass + + +class PdfDefinition(APIEndpoint): + """ PDF Definitions provide a list of support PDF definitions for the instance. + + These can vary between using physical cards, mobile credentials, or + biometrics. The card type is used to dynamically determine the types + of credentials available on this particular instance. + """ + + @classmethod + async def get_config(cls) -> EndpointConfig: + return EndpointConfig( + endpoint=Capabilities.CURRENT.features. + personal_data_fields.personal_data_fields, + dto_list=PdfResponse, + dto_retrieve=PdfDetail, + ) + + @classmethod + async def search(cls, + name: str, + sort: str = "id", + top: int = 100 + ): + pass diff --git a/gallagher/dto/detail/__init__.py b/gallagher/dto/detail/__init__.py index 942bc432..e0df6b56 100644 --- a/gallagher/dto/detail/__init__.py +++ b/gallagher/dto/detail/__init__.py @@ -28,7 +28,7 @@ ) from .pdf import ( - PDFDetail, + PdfDetail, ) from .role import ( diff --git a/gallagher/dto/detail/pdf.py b/gallagher/dto/detail/pdf.py index abd10a0a..a72fe814 100644 --- a/gallagher/dto/detail/pdf.py +++ b/gallagher/dto/detail/pdf.py @@ -7,7 +7,7 @@ ) -class PDFDetail( +class PdfDetail( AppBaseModel, HrefMixin ): diff --git a/gallagher/dto/response/__init__.py b/gallagher/dto/response/__init__.py index e3c07363..e9fed1fc 100644 --- a/gallagher/dto/response/__init__.py +++ b/gallagher/dto/response/__init__.py @@ -36,6 +36,10 @@ EventTypeResponse, ) +from .pdf import ( + PdfResponse, +) + from .items import ( ItemsSummaryResponse, ItemTypesResponse, diff --git a/gallagher/dto/response/pdf.py b/gallagher/dto/response/pdf.py index 2fa3f424..ebf64486 100644 --- a/gallagher/dto/response/pdf.py +++ b/gallagher/dto/response/pdf.py @@ -1,5 +1,12 @@ """ Personal Data Fields +Dynamically occurring fields are defined per site and are +prefixed by an @ symbol as the key of the field, as they +appear in the API responses. + +These fields appear either under the personalDataFields as +href references or as individual fields in the user's profile. + """ from ..utils import ( AppBaseModel, @@ -13,4 +20,10 @@ class PdfResponse( AppBaseModel, ): + """ Personal Definition fields + + Returned as a set of results, each result is a PDFRef + these are to be cached just as we do the URL endpoints, + note that this must be done after the discovery completes + """ results: list[PDFRef] diff --git a/tests/test_cardholder.py b/tests/test_cardholder.py index 74a407c1..f515e6ad 100644 --- a/tests/test_cardholder.py +++ b/tests/test_cardholder.py @@ -1,4 +1,4 @@ -""" +""" Cardholders """ diff --git a/tests/test_pdf.py b/tests/test_pdf.py new file mode 100644 index 00000000..f9bf0b31 --- /dev/null +++ b/tests/test_pdf.py @@ -0,0 +1,13 @@ +async def test_pdf_list(): + + from gallagher.cc.cardholders.cardholders import ( + PdfDefinition + ) + from gallagher.dto.response import ( + PdfResponse + ) + + response = await PdfDefinition.list() + assert type(response) is PdfResponse + assert type(response.results) is list + assert len(response.results) > 0 From db010fac7418b74fde0f732f6e5bc8067f78232d Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Fri, 2 Feb 2024 12:17:54 +1100 Subject: [PATCH 112/118] refactor: rename console package to tui --- gallagher/{console => tui}/__init__.py | 0 gallagher/{console => tui}/gallagher.tcss | 0 gallagher/{console => tui}/overview.py | 0 poetry.lock | 291 ++++++++++++++++++++-- pyproject.toml | 8 +- 5 files changed, 277 insertions(+), 22 deletions(-) rename gallagher/{console => tui}/__init__.py (100%) rename gallagher/{console => tui}/gallagher.tcss (100%) rename gallagher/{console => tui}/overview.py (100%) diff --git a/gallagher/console/__init__.py b/gallagher/tui/__init__.py similarity index 100% rename from gallagher/console/__init__.py rename to gallagher/tui/__init__.py diff --git a/gallagher/console/gallagher.tcss b/gallagher/tui/gallagher.tcss similarity index 100% rename from gallagher/console/gallagher.tcss rename to gallagher/tui/gallagher.tcss diff --git a/gallagher/console/overview.py b/gallagher/tui/overview.py similarity index 100% rename from gallagher/console/overview.py rename to gallagher/tui/overview.py diff --git a/poetry.lock b/poetry.lock index 54db4796..a887d563 100644 --- a/poetry.lock +++ b/poetry.lock @@ -109,6 +109,25 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + [[package]] name = "annotated-types" version = "0.6.0" @@ -142,17 +161,17 @@ trio = ["trio (<0.22)"] [[package]] name = "asyncer" -version = "0.0.2" +version = "0.0.3" description = "Asyncer, async and await, focused on developer experience." optional = false -python-versions = ">=3.6.2,<4.0.0" +python-versions = ">=3.8,<4.0" files = [ - {file = "asyncer-0.0.2-py3-none-any.whl", hash = "sha256:46e0e1423ce21588350ad425875e81795280b9e1f517e8a389de940b86c348bd"}, - {file = "asyncer-0.0.2.tar.gz", hash = "sha256:d546c85f3626ebbaf06bb4395db49761c902a61a6ac802b1a74133cab4f7f433"}, + {file = "asyncer-0.0.3-py3-none-any.whl", hash = "sha256:803398c31c23337285b97aa50e7594ac92fa99672a9e26e596e8dd92ed03fe1e"}, + {file = "asyncer-0.0.3.tar.gz", hash = "sha256:4734ede2bbe9c45caf8d81573af7a98d1bf866e3b92aa174b3f17b191cc51726"}, ] [package.dependencies] -anyio = ">=3.4.0,<4.0.0" +anyio = ">=3.4.0,<5.0" [[package]] name = "attrs" @@ -359,6 +378,77 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "h11" version = "0.14.0" @@ -457,6 +547,25 @@ dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] doc = ["myst-parser", "sphinx", "sphinx-book-theme"] test = ["coverage", "pytest", "pytest-cov"] +[[package]] +name = "mako" +version = "1.3.1" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.1-py3-none-any.whl", hash = "sha256:463f03e04559689adaee25e0967778d6ad41285ed607dc1e7df0dd4e4df81f9e"}, + {file = "Mako-1.3.1.tar.gz", hash = "sha256:baee30b9c61718e093130298e678abed0dbfa1b411fcc4c1ab4df87cd631a0f2"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -483,6 +592,75 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "markupsafe" +version = "2.1.4" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, +] + [[package]] name = "mdit-py-plugins" version = "0.4.0" @@ -978,6 +1156,93 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.25" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"}, + {file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"}, + {file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "tap-py" version = "3.1" @@ -1029,20 +1294,6 @@ msgpack = ">=1.0.3" textual = ">=0.36.0" typing-extensions = ">=4.4.0,<5.0.0" -[[package]] -name = "textual-select" -version = "0.3.4" -description = "A select widget (aka dropdown) for Textual." -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "textual_select-0.3.4-py3-none-any.whl", hash = "sha256:8b315dd041178f95c20000bab4358d49abae4c46fb222c9ec71eebe11d9e3a88"}, - {file = "textual_select-0.3.4.tar.gz", hash = "sha256:ae6767bb9078d2e451caf8f1606785cc9e99037f509a714942fe15660aeaa3e3"}, -] - -[package.dependencies] -textual = ">=0.14.0" - [[package]] name = "typer" version = "0.9.0" @@ -1199,4 +1450,4 @@ console = [] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "737e15ffae2c8b60f43e1440d1c7b1e36478248be81093c9dac2d1d79900ae22" +content-hash = "790f09538359e54517d0a02a96e152d5f244ddfc6da5376902cd4e7b45d0d765" diff --git a/pyproject.toml b/pyproject.toml index 1929b98f..f63e71dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,14 +47,18 @@ textual-dev = "^1.4.0" [tool.poetry.group.cli.dependencies] -asyncer = "^0.0.2" +asyncer = "^0.0.3" typer = "^0.9.0" rich = "^13.7.0" [tool.poetry.group.console.dependencies] textual = "^0.47.1" -textual-select = "^0.3.4" + + +[tool.poetry.group.sync.dependencies] +sqlalchemy = "^2.0.25" +alembic = "^1.13.1" [tool.poetry.scripts] gal = "gallagher.cli:app" From 5802e100a05b805105cc3a03c5ba550d51f6c5fa Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 5 Feb 2024 07:29:55 +1100 Subject: [PATCH 113/118] refactor: moves dashboard code out of app --- gallagher/tui/__init__.py | 11 +++++------ gallagher/tui/dashboard.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 gallagher/tui/dashboard.py diff --git a/gallagher/tui/__init__.py b/gallagher/tui/__init__.py index c72dab19..e650b746 100644 --- a/gallagher/tui/__init__.py +++ b/gallagher/tui/__init__.py @@ -1,4 +1,4 @@ -""" Console +""" Console """ from textual.app import ( @@ -18,6 +18,8 @@ Placeholder, ) +from .dashboard import Dashboard + class GallagherConsole(App): """ A console interface for the Gallagher Command Centre. @@ -32,7 +34,7 @@ class GallagherConsole(App): # Decorative constants TITLE = "Gallagher" - SUB_TITLE = "Super charged textual console" + SUB_TITLE = "power tools for the console" # def on_mount(self) -> None: @@ -40,10 +42,7 @@ def compose(self) -> ComposeResult: """ Create child widgets for the app. """ yield Header() - with Grid(): - yield Placeholder(id="a") - yield Placeholder(id="b") - yield Placeholder(id="c") + yield Dashboard() yield Footer() def action_toggle_dark(self) -> None: diff --git a/gallagher/tui/dashboard.py b/gallagher/tui/dashboard.py new file mode 100644 index 00000000..bde4eab4 --- /dev/null +++ b/gallagher/tui/dashboard.py @@ -0,0 +1,29 @@ +""" Dashboard + +Where you land up once the tui is started via the command line +this shows you some statistics, and health information. +""" + +from textual.app import ( + ComposeResult +) + +from textual.containers import ( + Container, + Grid, +) + +from textual.widgets import ( + Digits, + Placeholder, +) + + +class Dashboard(Container): + + def compose(self) -> ComposeResult: + """ Create child widgets for the app. + """ + with Grid(): + yield Placeholder("Dashboard") + yield Digits("3.141,592,653,5897", id="pi") From aa087648300d6e89e5982746adba72f99d6cc417 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 5 Feb 2024 07:30:03 +1100 Subject: [PATCH 114/118] docs: update todo --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 99a88668..6bb851c5 100644 --- a/TODO.md +++ b/TODO.md @@ -69,7 +69,7 @@ The following is tracking which endpoints are available along with their test ca - [ ] Operator group detail - [ ] Operator group membership - [ ] PDF definition search -- [ ] PDF definition +- [x] PDF definition - [ ] Reception search - [ ] Reception - [ ] Redaction From 74ee4748670a5700e901e36f98fe51305c499f29 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 5 Feb 2024 07:30:17 +1100 Subject: [PATCH 115/118] refactor: python scripts --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f63e71dc..634c6765 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ alembic = "^1.13.1" [tool.poetry.scripts] gal = "gallagher.cli:app" -gcon = "gallagher.console:main" +gcon = "gallagher.tui:main" [build-system] requires = ["poetry-core"] From 2bd0013a080b6b9f7cd6dffc94e66e509381de3a Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Mon, 4 Mar 2024 12:18:42 +1100 Subject: [PATCH 116/118] chore: update packages --- poetry.lock | 638 ++++++++++++++++++++++++------------------------- pyproject.toml | 25 +- 2 files changed, 322 insertions(+), 341 deletions(-) diff --git a/poetry.lock b/poetry.lock index a887d563..a676d868 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,88 +1,88 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] name = "aiohttp" -version = "3.9.1" +version = "3.9.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, - {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, - {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, - {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, - {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, - {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, - {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, - {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, - {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, - {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, - {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, - {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, ] [package.dependencies] @@ -141,13 +141,13 @@ files = [ [[package]] name = "anyio" -version = "3.7.1" +version = "4.3.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, ] [package.dependencies] @@ -155,9 +155,9 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "asyncer" @@ -194,13 +194,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -230,63 +230,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.extras] @@ -483,13 +483,13 @@ trio = ["trio (>=0.22.0,<0.23.0)"] [[package]] name = "httpx" -version = "0.26.0" +version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, - {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] @@ -852,13 +852,13 @@ files = [ [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -878,18 +878,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.3" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, + {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.3" typing-extensions = ">=4.6.1" [package.extras] @@ -897,116 +897,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.3" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, ] [package.dependencies] @@ -1029,37 +1003,37 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.1.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.1.0-py3-none-any.whl", hash = "sha256:ee32db7af8de4629a455806befa90559f307424c07b8413ccfc30bf5b221dd7e"}, + {file = "pytest-8.1.0.tar.gz", hash = "sha256:f8fa04ab8f98d185113ae60ea6d79c22f8143b14bc1caeced44a0ab844928323"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" +pluggy = ">=1.4,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.3" +version = "0.23.5" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, - {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, + {file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"}, + {file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"}, ] [package.dependencies] -pytest = ">=7.0.0" +pytest = ">=7.0.0,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] @@ -1129,13 +1103,13 @@ pytest = ">=3.0" [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -1158,60 +1132,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.25" +version = "2.0.27" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"}, - {file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"}, - {file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"}, - {file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"}, - {file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"}, - {file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"}, - {file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"}, - {file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"}, - {file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, + {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, + {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, ] [package.dependencies] @@ -1259,13 +1233,13 @@ yaml = ["PyYAML (>=5.1)", "more-itertools"] [[package]] name = "textual" -version = "0.47.1" +version = "0.52.1" description = "Modern Text User Interface framework" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "textual-0.47.1-py3-none-any.whl", hash = "sha256:da79df2e138f6de51bda84a1ee1460936bb2ecf5527ca2d47b9b59c584323327"}, - {file = "textual-0.47.1.tar.gz", hash = "sha256:4b82e317884bb1092f693f474c319ceb068b5a0b128b121f1aa53a2d48b4b80c"}, + {file = "textual-0.52.1-py3-none-any.whl", hash = "sha256:960a19df2319482918b4a58736d9552cdc1ab65d170ba0bc15273ce0e1922b7a"}, + {file = "textual-0.52.1.tar.gz", hash = "sha256:4232e5c2b423ed7c63baaeb6030355e14e1de1b9df096c9655b68a1e60e4de5f"}, ] [package.dependencies] @@ -1278,13 +1252,13 @@ syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] [[package]] name = "textual-dev" -version = "1.4.0" +version = "1.5.1" description = "Development tools for working with Textual" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "textual_dev-1.4.0-py3-none-any.whl", hash = "sha256:330beec18b8f469adf7cdf9d69cc94965bf3e69d9aec23625d62cdedcadab044"}, - {file = "textual_dev-1.4.0.tar.gz", hash = "sha256:a20ea746a93e66978e9dfe71a7e5409854c96cc3e46550cc40760b199d7a5d3b"}, + {file = "textual_dev-1.5.1-py3-none-any.whl", hash = "sha256:bb37dd769ae6b67e1422aa97f6d6ef952e0a6d2aafe08327449e8bdd70474776"}, + {file = "textual_dev-1.5.1.tar.gz", hash = "sha256:e0366ab6f42c128d7daa37a7c418e61fe7aa83731983da990808e4bf2de922a1"}, ] [package.dependencies] @@ -1317,13 +1291,13 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -1450,4 +1424,4 @@ console = [] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "790f09538359e54517d0a02a96e152d5f244ddfc6da5376902cd4e7b45d0d765" +content-hash = "6f76fbceb1123a73ecd240fc010b9c53fe97ad1831e1200d69bd8c035cb1f82a" diff --git a/pyproject.toml b/pyproject.toml index 634c6765..cf61902c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,22 +26,24 @@ console = ["textual"] [tool.poetry.dependencies] python = "^3.11" -httpx = "^0.26.0" -pydantic = "^2.5.3" -typing-extensions = "^4.9.0" +httpx = "^0.27.0" +pydantic = "^2.6.3" +typing-extensions = "^4.10.0" annotated-types = "^0.6.0" -certifi = "^2023.11.17" +certifi = "^2024.2.2" idna = "^3.6" packaging = "^23.2" pluggy = "^1.3.0" +anyio = "^4.3.0" +aiohttp = "^3.9.3" [tool.poetry.group.dev.dependencies] pytest-cov = "^4.1.0" coverage = "^7.4.0" -pytest = "^7.4.4" +pytest = "^8.1.0" pytest-order = "^1.2.0" pytest-tap = "^3.4" -pytest-asyncio = "^0.23.3" +pytest-asyncio = "^0.23.5" pytest-clarity = "^1.0.1" textual-dev = "^1.4.0" @@ -49,17 +51,22 @@ textual-dev = "^1.4.0" [tool.poetry.group.cli.dependencies] asyncer = "^0.0.3" typer = "^0.9.0" -rich = "^13.7.0" +rich = "^13.7.1" [tool.poetry.group.console.dependencies] -textual = "^0.47.1" +textual = "^0.52.1" +textual-dev = "^1.5.1" [tool.poetry.group.sync.dependencies] -sqlalchemy = "^2.0.25" +sqlalchemy = "^2.0.27" alembic = "^1.13.1" + +[tool.poetry.group.test.dependencies] +coverage = "^7.4.3" + [tool.poetry.scripts] gal = "gallagher.cli:app" gcon = "gallagher.tui:main" From 8aa727fb05f691968917193f5bbd5e5c6d3be49d Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Sat, 9 Mar 2024 17:37:31 +1100 Subject: [PATCH 117/118] refactor: update packages --- poetry.lock | 108 ++++++++++++++++++++++++------------------------- pyproject.toml | 5 ++- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/poetry.lock b/poetry.lock index a676d868..fd824509 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1023,13 +1023,13 @@ testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygm [[package]] name = "pytest-asyncio" -version = "0.23.5" +version = "0.23.5.post1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"}, - {file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"}, + {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, + {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, ] [package.dependencies] @@ -1132,60 +1132,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.27" +version = "2.0.28" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, ] [package.dependencies] @@ -1424,4 +1424,4 @@ console = [] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "6f76fbceb1123a73ecd240fc010b9c53fe97ad1831e1200d69bd8c035cb1f82a" +content-hash = "e2d708fb48e9a54e84c3ca113ed96046d9aef41d01c9369360836b4f8cc8d600" diff --git a/pyproject.toml b/pyproject.toml index cf61902c..787d8551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,18 +54,19 @@ typer = "^0.9.0" rich = "^13.7.1" -[tool.poetry.group.console.dependencies] +[tool.poetry.group.tui.dependencies] textual = "^0.52.1" textual-dev = "^1.5.1" [tool.poetry.group.sync.dependencies] -sqlalchemy = "^2.0.27" +sqlalchemy = "^2.0.28" alembic = "^1.13.1" [tool.poetry.group.test.dependencies] coverage = "^7.4.3" +pytest-asyncio = "^0.23.5.post1" [tool.poetry.scripts] gal = "gallagher.cli:app" From de4cc97c226b79834ed38d201a6f22670eed6295 Mon Sep 17 00:00:00 2001 From: Dev Mukherjee Date: Thu, 28 Mar 2024 08:43:43 +1100 Subject: [PATCH 118/118] chore: updates packages --- poetry.lock | 26 +++++++++++++------------- pyproject.toml | 8 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index fd824509..159a8278 100644 --- a/poetry.lock +++ b/poetry.lock @@ -878,13 +878,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] @@ -1003,13 +1003,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.1.0" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.0-py3-none-any.whl", hash = "sha256:ee32db7af8de4629a455806befa90559f307424c07b8413ccfc30bf5b221dd7e"}, - {file = "pytest-8.1.0.tar.gz", hash = "sha256:f8fa04ab8f98d185113ae60ea6d79c22f8143b14bc1caeced44a0ab844928323"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -1023,13 +1023,13 @@ testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygm [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.6" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] @@ -1233,13 +1233,13 @@ yaml = ["PyYAML (>=5.1)", "more-itertools"] [[package]] name = "textual" -version = "0.52.1" +version = "0.53.1" description = "Modern Text User Interface framework" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "textual-0.52.1-py3-none-any.whl", hash = "sha256:960a19df2319482918b4a58736d9552cdc1ab65d170ba0bc15273ce0e1922b7a"}, - {file = "textual-0.52.1.tar.gz", hash = "sha256:4232e5c2b423ed7c63baaeb6030355e14e1de1b9df096c9655b68a1e60e4de5f"}, + {file = "textual-0.53.1-py3-none-any.whl", hash = "sha256:32201aa9d334ed064d5e670f15fe3d7f19c736ca54cecb054a5b995691104434"}, + {file = "textual-0.53.1.tar.gz", hash = "sha256:23ba673be7974819ded35ea88d28df7117987e53d58f15b2cc890ac2ecf56401"}, ] [package.dependencies] @@ -1424,4 +1424,4 @@ console = [] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "e2d708fb48e9a54e84c3ca113ed96046d9aef41d01c9369360836b4f8cc8d600" +content-hash = "8db25ce7034277e23607e1066781044f94157396150fd24bfa30793ab74a3c89" diff --git a/pyproject.toml b/pyproject.toml index 787d8551..81e67675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ console = ["textual"] [tool.poetry.dependencies] python = "^3.11" httpx = "^0.27.0" -pydantic = "^2.6.3" +pydantic = "^2.6.4" typing-extensions = "^4.10.0" annotated-types = "^0.6.0" certifi = "^2024.2.2" @@ -40,10 +40,10 @@ aiohttp = "^3.9.3" [tool.poetry.group.dev.dependencies] pytest-cov = "^4.1.0" coverage = "^7.4.0" -pytest = "^8.1.0" +pytest = "^8.1.1" pytest-order = "^1.2.0" pytest-tap = "^3.4" -pytest-asyncio = "^0.23.5" +pytest-asyncio = "^0.23.6" pytest-clarity = "^1.0.1" textual-dev = "^1.4.0" @@ -55,7 +55,7 @@ rich = "^13.7.1" [tool.poetry.group.tui.dependencies] -textual = "^0.52.1" +textual = "^0.53.1" textual-dev = "^1.5.1"