masstack-python-client 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,267 @@
1
+ import os
2
+ from typing import Optional, List
3
+ from masstack_python_client.client import MasstackClient
4
+ from masstack_python_client.resources.models.address import (
5
+ BuildingData,
6
+ ClarifierData,
7
+ CoverageData,
8
+ StreetData,
9
+ UnitData,
10
+ )
11
+
12
+
13
+ class FeasibilityResource:
14
+ """Resource for feasibility operations
15
+ https://developers.masstack.com/en/docs/apis_feasibility_doc_swagger/1/apioverview
16
+ """
17
+
18
+ _api_domain = "feasibility"
19
+
20
+
21
+ class Coverage(FeasibilityResource):
22
+
23
+ _api_path = "coverage"
24
+ _version = "v1"
25
+
26
+ @staticmethod
27
+ def _sf_segment() -> str:
28
+ return os.environ.get("MASSTACK_COVERAGE_SEGMENT", "")
29
+
30
+ @staticmethod
31
+ def _sf_channel() -> str:
32
+ return os.environ.get("MASSTACK_COVERAGE_CHANNEL", "")
33
+
34
+ @classmethod
35
+ def _get_endpoint(cls, subpath: str) -> str:
36
+ return f"{cls._api_path}/{subpath}"
37
+
38
+ @classmethod
39
+ def _get_header(cls, uuid: str) -> dict:
40
+ return {
41
+ "sf-channel": cls._sf_channel(),
42
+ "sf-segment": cls._sf_segment(),
43
+ "sf-check-id": uuid,
44
+ }
45
+
46
+ @classmethod
47
+ def _request(
48
+ cls,
49
+ method: str,
50
+ subpath: str,
51
+ uuid: str,
52
+ data: Optional[dict] = None,
53
+ params: Optional[dict] = None,
54
+ ) -> dict:
55
+ client = MasstackClient()
56
+ func = getattr(client, method)
57
+
58
+ return func(
59
+ api=cls._api_domain,
60
+ version=cls._version,
61
+ endpoint=cls._get_endpoint(subpath),
62
+ params=params,
63
+ data=data,
64
+ header=cls._get_header(uuid),
65
+ )
66
+
67
+
68
+ class Streets(Coverage):
69
+
70
+ _api_subpath = "streets"
71
+
72
+ @classmethod
73
+ def search(cls, text: str, lang: str, uuid: str) -> List[StreetData]:
74
+ """Search streets by text
75
+
76
+ Args:
77
+ text (str): Text to search
78
+ lang (str): Language code
79
+ uuid (str): Unique identifier for the request
80
+
81
+ Returns:
82
+ StreetData
83
+
84
+ """
85
+ streets = cls._request(
86
+ "get",
87
+ cls._api_subpath,
88
+ uuid,
89
+ params={"search": text},
90
+ )
91
+ return [StreetData.from_dict(uuid, lang, **street) for street in streets]
92
+
93
+ @classmethod
94
+ def get_building_info_by_street(
95
+ cls, street_id: str, uuid: str
96
+ ) -> List[ClarifierData]:
97
+ """Provides information to identify a building from a street.
98
+
99
+ Args:
100
+ street_id (str): Street identifier (Gescal 12)
101
+ uuid (str): Unique identifier for the request
102
+
103
+ Returns:
104
+ ClarifierData
105
+ """
106
+ clarifiers = cls._request(
107
+ "get",
108
+ f"{cls._api_subpath}/clarifiers/{street_id}",
109
+ uuid,
110
+ )
111
+
112
+ return [ClarifierData(uuid, **clarifier) for clarifier in clarifiers]
113
+
114
+
115
+ class Buildings(Coverage):
116
+
117
+ _api_subpath = "buildings"
118
+
119
+ @classmethod
120
+ def search(cls, text: str, lang: str, uuid: str) -> List[BuildingData]:
121
+ """Search buildings by text
122
+
123
+ Args:
124
+ text (str): Text to search
125
+ lang (str): Language code
126
+ uuid (str): Unique identifier for the request
127
+
128
+ Returns:
129
+ BuildingData
130
+ """
131
+ buildings = cls._request(
132
+ "get",
133
+ cls._api_subpath,
134
+ uuid,
135
+ params={"search": text},
136
+ )
137
+ return [
138
+ BuildingData.from_dict(uuid, lang, **building) for building in buildings
139
+ ]
140
+
141
+ @classmethod
142
+ def get(
143
+ cls, id: str, lang: str, uuid: str, unit_to_data: Optional[bool] = False
144
+ ) -> BuildingData:
145
+ """Returns a single building object
146
+
147
+ Args:
148
+ id (str): Building identifier (Gescal 17)
149
+ lang (str): Language code
150
+ uuid (str): Unique identifier for the request
151
+ unit_to_data (bool): Territory_owner (TO) for each unit
152
+
153
+ Returns:
154
+ BuildingData
155
+ """
156
+ building = cls._request(
157
+ "get",
158
+ f"{cls._api_subpath}/{id}",
159
+ uuid,
160
+ params={"unit_to_data": unit_to_data},
161
+ )
162
+ return BuildingData.from_dict(
163
+ uuid,
164
+ lang,
165
+ **building,
166
+ )
167
+
168
+ @classmethod
169
+ def multi_search(
170
+ cls,
171
+ way_name: str,
172
+ number: str,
173
+ lang: str,
174
+ uuid: str,
175
+ postal_code: Optional[str] = "",
176
+ way_type: Optional[str] = "",
177
+ town: Optional[str] = "",
178
+ ) -> List[BuildingData]:
179
+ """Search buildings by required way_name and number
180
+ optionaly with postal_code, way_type, town, postal_code
181
+
182
+ Args:
183
+ way_name (str): Street name
184
+ number (str): Building number
185
+ lang (str): Language code
186
+ uuid (str): Unique identifier for the request
187
+ postal_code (str, optional): Postal code. Defaults to "".
188
+ way_type (str, optional): Street type. Defaults to "".
189
+ town (str, optional): Town name. Defaults to "".
190
+
191
+ Returns:
192
+ BuildingData
193
+ """
194
+
195
+ buildings = cls._request(
196
+ "post",
197
+ f"{cls._api_subpath}/search",
198
+ uuid,
199
+ data={
200
+ "way_name": way_name,
201
+ "number": number,
202
+ "way_type": way_type,
203
+ "town": town,
204
+ "postal_code": postal_code,
205
+ },
206
+ )
207
+ return [
208
+ BuildingData.from_dict(uuid, lang, **building) for building in buildings
209
+ ]
210
+
211
+
212
+ class Units(Coverage):
213
+
214
+ _api_subpath = "units"
215
+
216
+ @classmethod
217
+ def get(
218
+ cls, unit_id: str, lang: str, uuid: str, parent_coverage: Optional[bool] = False
219
+ ) -> UnitData:
220
+ """Returns a single unit object
221
+
222
+ Args:
223
+ unit_id (str): Unit identifier (Gescal 37)
224
+ lang (str): Language code
225
+ uuid (str): Unique identifier for the request
226
+ parent_coverage (bool): return the parent coverage
227
+
228
+ Returns:
229
+ UnitData
230
+ """
231
+ unit = cls._request(
232
+ "get",
233
+ f"{cls._api_subpath}/{unit_id}",
234
+ uuid,
235
+ params={"parent_coverage": parent_coverage},
236
+ )
237
+ return UnitData.from_dict(
238
+ lang,
239
+ uuid=uuid,
240
+ include_coverage=True,
241
+ include_parent=parent_coverage,
242
+ **unit,
243
+ )
244
+
245
+ @classmethod
246
+ def get_coverage(
247
+ cls, unit_id: str, territory_owner: str, uuid: str
248
+ ) -> CoverageData:
249
+ """Returns an coverage info for a unit 'id' and 'territory_owner'.
250
+
251
+ Args:
252
+ unit_id (str): Unit identifier (Gescal 37)
253
+ territory_owner (str): territory_owner parameter to match with a unit
254
+ uuid (str): Unique identifier for the request
255
+
256
+ Returns:
257
+ CoverageData
258
+ """
259
+ unit = cls._request(
260
+ "get",
261
+ f"{cls._api_subpath}/extra",
262
+ uuid,
263
+ params={"id": unit_id, "territory_owner": territory_owner},
264
+ )
265
+ return CoverageData.from_dict(
266
+ **unit,
267
+ )
@@ -0,0 +1,197 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List
3
+
4
+ from masstack_python_client.data.data_utils import (
5
+ get_province_by_id,
6
+ get_street_type,
7
+ get_unit_translation,
8
+ )
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class CoverageData:
13
+ territory_owner: str
14
+ operator: str
15
+ territory_owner_type: str
16
+ address_id: str
17
+ technology: str
18
+ availability: bool
19
+ priority: str
20
+
21
+ @classmethod
22
+ def from_dict(
23
+ cls,
24
+ territory_owner: str,
25
+ operator: str,
26
+ territory_owner_type: str,
27
+ address_id: str,
28
+ priority: str = "1",
29
+ availability: bool = True,
30
+ technology: str = "FTTH",
31
+ **kwargs,
32
+ ):
33
+ return cls(
34
+ **{
35
+ "territory_owner": territory_owner,
36
+ "operator": operator,
37
+ "territory_owner_type": territory_owner_type,
38
+ "address_id": address_id,
39
+ "technology": technology,
40
+ "availability": availability,
41
+ "priority": priority,
42
+ }
43
+ )
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class UnitData:
48
+ uuid: str
49
+ id: str
50
+ technical_id: str
51
+ floor: str = ""
52
+ hand1: str = ""
53
+ summary: str = ""
54
+ hand2: str = ""
55
+ block: str = ""
56
+ door: str = ""
57
+ letter: str = ""
58
+ stair: str = ""
59
+ translated_floor: str = ""
60
+ translated_hand1: str = ""
61
+ translated_hand2: str = ""
62
+ coverages: List[CoverageData] = field(default_factory=list)
63
+ parent_id: str = ""
64
+
65
+ @classmethod
66
+ def from_dict(
67
+ cls,
68
+ lang: str,
69
+ id: str,
70
+ technical_id: str,
71
+ address_description: dict,
72
+ uuid: str = "",
73
+ coverage: list = [],
74
+ parent: dict = {},
75
+ include_coverage: bool = False,
76
+ include_parent: bool = False,
77
+ **kwargs,
78
+ ):
79
+ fields_dict = {
80
+ "id": id,
81
+ "uuid": uuid,
82
+ "technical_id": technical_id,
83
+ "floor": address_description.get("floor"),
84
+ "hand1": address_description.get("hand1"),
85
+ "hand2": address_description.get("hand2"),
86
+ "summary": address_description.get("summary"),
87
+ "block": address_description.get("block"),
88
+ "door": address_description.get("door"),
89
+ "letter": address_description.get("letter"),
90
+ "stair": address_description.get("stair"),
91
+ "translated_floor": get_unit_translation(
92
+ address_description.get("floor"), lang
93
+ ),
94
+ "translated_hand1": get_unit_translation(
95
+ address_description.get("hand1"), lang
96
+ ),
97
+ "translated_hand2": get_unit_translation(
98
+ address_description.get("hand2"), lang
99
+ ),
100
+ }
101
+ if include_coverage:
102
+ fields_dict = {
103
+ "coverages": [CoverageData.from_dict(**cov) for cov in coverage],
104
+ **fields_dict,
105
+ }
106
+ if include_parent:
107
+ fields_dict = {
108
+ "parent_id": parent["id"],
109
+ **fields_dict,
110
+ }
111
+ return cls(**fields_dict)
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class AddressData:
116
+ uuid: str
117
+ id: str
118
+ technical_id: str
119
+ summary: str
120
+ street_type: str
121
+ street_name: str
122
+ town: str
123
+ province: str
124
+ province_id: str
125
+ translated_street_type: str
126
+ translated_province: str
127
+
128
+ @classmethod
129
+ def base_fields_dict(
130
+ cls, uuid: str, lang: str, id: str, technical_id: str, address: dict
131
+ ) -> dict:
132
+ return {
133
+ "uuid": uuid,
134
+ "id": id,
135
+ "technical_id": technical_id,
136
+ "summary": address["summary"],
137
+ "street_type": address["street_type"],
138
+ "street_name": address["street_name"],
139
+ "town": address["town"],
140
+ "province": address["province"],
141
+ "province_id": address["province_id"],
142
+ "translated_street_type": get_street_type(address["street_type"], lang),
143
+ "translated_province": get_province_by_id(
144
+ address["province_id"], address["province"], lang
145
+ ),
146
+ }
147
+
148
+
149
+ @dataclass(frozen=True)
150
+ class StreetData(AddressData):
151
+ postal_codes: list
152
+
153
+ @classmethod
154
+ def from_dict(
155
+ cls, uuid: str, lang: str, id: str, technical_id: str, address: dict, **kwargs
156
+ ) -> "StreetData":
157
+ base_fields = super().base_fields_dict(uuid, lang, id, technical_id, address)
158
+ return cls(**{**base_fields, "postal_codes": address["postal_code"]})
159
+
160
+
161
+ @dataclass(frozen=True)
162
+ class BuildingData(AddressData):
163
+ number: str
164
+ postal_code: str
165
+ units: List[UnitData] = field(default_factory=list)
166
+
167
+ @classmethod
168
+ def from_dict(
169
+ cls,
170
+ uuid: str,
171
+ lang: str,
172
+ id: str,
173
+ technical_id: str,
174
+ address: dict,
175
+ units: list = [],
176
+ **kwargs,
177
+ ) -> "BuildingData":
178
+ base_fields = super().base_fields_dict(uuid, lang, id, technical_id, address)
179
+ return cls(
180
+ **{
181
+ **base_fields,
182
+ "number": address["number"],
183
+ "postal_code": address["postal_code"],
184
+ "units": [
185
+ UnitData.from_dict(lang, uuid=uuid, **unit) for unit in units
186
+ ],
187
+ }
188
+ )
189
+
190
+
191
+ @dataclass(frozen=True)
192
+ class ClarifierData:
193
+ uuid: str
194
+ number: str
195
+ description: str
196
+ building_id: str
197
+ street_id: str
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: masstack-python-client
3
+ Version: 0.0.1
4
+ Summary: This Python API client provides access to masmovil core API domains
5
+ Author-email: Borja Gimeno <borja.gimeno@somconnexio.coop>
6
+ License: GPL-3.0-only
7
+ Project-URL: Homepage, https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: requests
15
+ Requires-Dist: joserfc
16
+ Dynamic: license-file
17
+
18
+ [![Latest Release](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/-/badges/release.svg)](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/-/releases)
19
+ [![pipeline status](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/badges/main/pipeline.svg)](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/-/commits/main)
20
+ [![coverage report](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/badges/main/coverage.svg)](https://git.coopdevs.org/coopdevs/som-connexio/masmovil/masstack-python-client/-/commits/main)
21
+
22
+ :warning: WORK IN PROGRESS :warning:
23
+
24
+ This Python API client provides access to masmovil core API domains.
25
+
26
+ ## Resources
27
+
28
+ * Feasibility
29
+ - Coverage - find an address
30
+ - Streets:
31
+ - search: returns a list of street by text
32
+ - get_building_info_by_street: returns a list of building info by street id
33
+ - Buildings:
34
+ - search: returns a list of buildings by text
35
+ - multi_search: returns a list of buildings by request body
36
+ - get: returns a building by id with a list of units
37
+ - Units:
38
+ - get: returns a unit by id with a list of coverages
39
+ - get_coverage: returns coverage info for a unit id and territory_owner
40
+
41
+ ## Installation
42
+
43
+ ```commandline
44
+ $ pip install masstack-python-client
45
+ ```
46
+
47
+ ## Configuration Environment
48
+
49
+ You need define the Masstack CLIENT_EMAIL, PRIVATE_KEY_ID TOKEN_URI, MASSTACK_DOMAIN and PRIVATE_KEY in BASE64 as environment variables to request access toekn. You need define:
50
+
51
+ ```
52
+ MASSTACK_CLIENT_EMAIL=<YOUR CLIENT EMAIL>
53
+ MASSTACK_PRIVATE_KEY_ID=<YOUR PRIVATE KEY ID>
54
+ MASSTACK_TOKEN_URI=<MASSTACK TOKEN URI>
55
+ MASSTACK_PRIVATE_KEY_BASE64=<YOUR PRIVATE KEY in BASE64>
56
+ MASSTACK_DOMAIN=<MASSTACK DOMAIN>
57
+
58
+ ```
59
+
60
+ If this envvars are not defined, a exception will be raised with the name of the envvar not defined.
61
+
62
+ Optional TOKEN_STORE_PATH environment variable to store token, if is not defined by defect is /tmp/masstack.token
63
+
64
+ ```
65
+ MASSTACK_TOKEN_STORE_PATH=<YOUR PATH>
66
+
67
+ ```
68
+
69
+ To all resources need define ORG_ID
70
+
71
+ ```
72
+ MASSTACK_ORG_ID=<MASSTACK ORG ID>
73
+
74
+ ```
75
+
76
+ For coverage resources need define COVERAGE_SEGMENT and COVERAGE_CHANNEL
77
+
78
+ ```
79
+ MASSTACK_COVERAGE_SEGMENT=<SF SEGMENT>
80
+ MASSTACK_COVERAGE_CHANNEL=<SF CHANNEL>
81
+
82
+ ```
83
+
84
+ ## Development
85
+
86
+ ### Setup environment
87
+
88
+ 1. Install `pyenv`
89
+ ```sh
90
+ curl https://pyenv.run | bash
91
+ ```
92
+ 2. Build the Python version
93
+ ```sh
94
+ pyenv install 3.11.9
95
+ ```
96
+ 3. Create a virtualenv
97
+ ```sh
98
+ pyenv virtualenv 3.11.9 masstack-python-client
99
+ ```
100
+ 4. Activate virtualenv
101
+ ```sh
102
+ pyenv activate masstack-python-client
103
+ ```
104
+ 5. Install dependencies
105
+ ```sh
106
+ pyenv exec pip install -r requirements-dev.txt
107
+ ```
108
+ 6. Install pre-commit hooks
109
+ ```sh
110
+ pyenv exec pre-commit install
@@ -0,0 +1,19 @@
1
+ masstack_python_client/__init__.py,sha256=_LdSbviglyB3x3fpuIQ9EUcSCj35jDkPRsaneNm-d6Y,154
2
+ masstack_python_client/client.py,sha256=5b52Q2ekABi7DlZPsNobcmaJXOKzZfhKE_kHvNEqE9Q,2496
3
+ masstack_python_client/exceptions.py,sha256=TszLAHlTurfnzlyf_OWvP9qACx1Y565TyIl4j75w-EM,1941
4
+ masstack_python_client/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ masstack_python_client/auth/token.py,sha256=cNpV8lwfme1E9XBpGAtMj2ZFh2C_NgGVlIuXswE-eBE,306
6
+ masstack_python_client/auth/token_manager.py,sha256=zEtLBUgmJh6kfN9w2csBzsCRwZevZwgWsjZkjE7AkBE,2738
7
+ masstack_python_client/auth/token_store.py,sha256=SYj8oxkyELVV_AxtHSE1dpURRIqP6Gbj87MPQdhmXj8,910
8
+ masstack_python_client/data/data_utils.py,sha256=TKybTybMXPqJdN6s-VTFur2jWHwfpzicQ-HQQZhm1_o,1476
9
+ masstack_python_client/data/province_translation.csv,sha256=Jpd6jbjYaqeNOk9lUA3W_ftd1dPP3jFrlfxoTuuE3W0,1063
10
+ masstack_python_client/data/street_type_translation.csv,sha256=2MGeTs0v6wVz2yRMyZUuoNOYA4NSqKGJCGYIRi9X8Ic,1791
11
+ masstack_python_client/data/unit_translation.csv,sha256=34FPfaCC1eSmBfV0BbHSHJ-tktQk7YLlfT8tMxnfnys,194
12
+ masstack_python_client/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ masstack_python_client/resources/feasibility.py,sha256=DbiPwWa9orSmITgcID7RVPKf7b50Pk8h1XQh0xxGYhw,7119
14
+ masstack_python_client/resources/models/address.py,sha256=hKnEUVVz8AfGjhaDngzQ0HuR4_Z6zhK6Xe8Aj6xJR7I,5451
15
+ masstack_python_client-0.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
16
+ masstack_python_client-0.0.1.dist-info/METADATA,sha256=71BLaspeDsRB3_jss4r86h8LMokS2kMOXPV_3uk7Nn0,3462
17
+ masstack_python_client-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
18
+ masstack_python_client-0.0.1.dist-info/top_level.txt,sha256=q_iYlqJq22lhg_2O2iktARr7Mzz3QfjaW4l6J4vwyE8,23
19
+ masstack_python_client-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+