permitstack 1.0.0__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.
Files changed (68) hide show
  1. permitstack/__init__.py +17 -0
  2. permitstack/_hooks/__init__.py +4 -0
  3. permitstack/_hooks/sdkhooks.py +74 -0
  4. permitstack/_hooks/types.py +112 -0
  5. permitstack/_version.py +15 -0
  6. permitstack/basesdk.py +396 -0
  7. permitstack/bulk_export.py +241 -0
  8. permitstack/contractors.py +625 -0
  9. permitstack/errors/__init__.py +39 -0
  10. permitstack/errors/httpvalidationerror.py +28 -0
  11. permitstack/errors/no_response_error.py +17 -0
  12. permitstack/errors/permitstackdefaulterror.py +40 -0
  13. permitstack/errors/permitstackerror.py +30 -0
  14. permitstack/errors/responsevalidationerror.py +27 -0
  15. permitstack/health.py +171 -0
  16. permitstack/httpclient.py +125 -0
  17. permitstack/models/__init__.py +158 -0
  18. permitstack/models/contractorprofile.py +108 -0
  19. permitstack/models/contractorsearchresponse.py +24 -0
  20. permitstack/models/contractorsummary.py +57 -0
  21. permitstack/models/delete_webhookop.py +16 -0
  22. permitstack/models/export_permits_csvop.py +98 -0
  23. permitstack/models/get_contractor_permitsop.py +46 -0
  24. permitstack/models/get_contractorop.py +16 -0
  25. permitstack/models/get_permitop.py +16 -0
  26. permitstack/models/get_permits_by_addressop.py +46 -0
  27. permitstack/models/get_property_historyop.py +18 -0
  28. permitstack/models/permitcategory.py +27 -0
  29. permitstack/models/permitdetail.py +164 -0
  30. permitstack/models/permitsearchresponse.py +24 -0
  31. permitstack/models/permitstatus.py +16 -0
  32. permitstack/models/permitsummary.py +121 -0
  33. permitstack/models/propertytype.py +14 -0
  34. permitstack/models/search_contractorsop.py +98 -0
  35. permitstack/models/search_permitsop.py +247 -0
  36. permitstack/models/security.py +42 -0
  37. permitstack/models/validationerror.py +57 -0
  38. permitstack/models/webhookcreate.py +60 -0
  39. permitstack/permits.py +866 -0
  40. permitstack/property_history.py +207 -0
  41. permitstack/py.typed +1 -0
  42. permitstack/sdk.py +218 -0
  43. permitstack/sdkconfiguration.py +49 -0
  44. permitstack/types/__init__.py +21 -0
  45. permitstack/types/basemodel.py +77 -0
  46. permitstack/utils/__init__.py +178 -0
  47. permitstack/utils/annotations.py +79 -0
  48. permitstack/utils/datetimes.py +23 -0
  49. permitstack/utils/dynamic_imports.py +54 -0
  50. permitstack/utils/enums.py +134 -0
  51. permitstack/utils/eventstreaming.py +309 -0
  52. permitstack/utils/forms.py +234 -0
  53. permitstack/utils/headers.py +136 -0
  54. permitstack/utils/logger.py +27 -0
  55. permitstack/utils/metadata.py +119 -0
  56. permitstack/utils/queryparams.py +217 -0
  57. permitstack/utils/requestbodies.py +66 -0
  58. permitstack/utils/retries.py +271 -0
  59. permitstack/utils/security.py +215 -0
  60. permitstack/utils/serializers.py +225 -0
  61. permitstack/utils/unmarshal_json_response.py +38 -0
  62. permitstack/utils/url.py +155 -0
  63. permitstack/utils/values.py +137 -0
  64. permitstack/webhooks.py +593 -0
  65. permitstack-1.0.0.dist-info/METADATA +541 -0
  66. permitstack-1.0.0.dist-info/RECORD +68 -0
  67. permitstack-1.0.0.dist-info/WHEEL +5 -0
  68. permitstack-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,24 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from .contractorsummary import ContractorSummary, ContractorSummaryTypedDict
5
+ from permitstack.types import BaseModel
6
+ from typing import List
7
+ from typing_extensions import TypedDict
8
+
9
+
10
+ class ContractorSearchResponseTypedDict(TypedDict):
11
+ total: int
12
+ page: int
13
+ per_page: int
14
+ results: List[ContractorSummaryTypedDict]
15
+
16
+
17
+ class ContractorSearchResponse(BaseModel):
18
+ total: int
19
+
20
+ page: int
21
+
22
+ per_page: int
23
+
24
+ results: List[ContractorSummary]
@@ -0,0 +1,57 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from datetime import date
5
+ from permitstack.types import BaseModel, Nullable, UNSET_SENTINEL
6
+ from pydantic import model_serializer
7
+ from typing import List
8
+ from typing_extensions import TypedDict
9
+
10
+
11
+ class ContractorSummaryTypedDict(TypedDict):
12
+ id: str
13
+ name: str
14
+ license_number: Nullable[str]
15
+ license_state: Nullable[str]
16
+ city: Nullable[str]
17
+ state: Nullable[str]
18
+ total_permits: int
19
+ first_permit_date: Nullable[date]
20
+ last_permit_date: Nullable[date]
21
+ specialties: Nullable[List[str]]
22
+
23
+
24
+ class ContractorSummary(BaseModel):
25
+ id: str
26
+
27
+ name: str
28
+
29
+ license_number: Nullable[str]
30
+
31
+ license_state: Nullable[str]
32
+
33
+ city: Nullable[str]
34
+
35
+ state: Nullable[str]
36
+
37
+ total_permits: int
38
+
39
+ first_permit_date: Nullable[date]
40
+
41
+ last_permit_date: Nullable[date]
42
+
43
+ specialties: Nullable[List[str]]
44
+
45
+ @model_serializer(mode="wrap")
46
+ def serialize_model(self, handler):
47
+ serialized = handler(self)
48
+ m = {}
49
+
50
+ for n, f in type(self).model_fields.items():
51
+ k = f.alias or n
52
+ val = serialized.get(k, serialized.get(n))
53
+
54
+ if val != UNSET_SENTINEL:
55
+ m[k] = val
56
+
57
+ return m
@@ -0,0 +1,16 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel
5
+ from permitstack.utils import FieldMetadata, PathParamMetadata
6
+ from typing_extensions import Annotated, TypedDict
7
+
8
+
9
+ class DeleteWebhookRequestTypedDict(TypedDict):
10
+ webhook_id: str
11
+
12
+
13
+ class DeleteWebhookRequest(BaseModel):
14
+ webhook_id: Annotated[
15
+ str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
16
+ ]
@@ -0,0 +1,98 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import (
5
+ BaseModel,
6
+ Nullable,
7
+ OptionalNullable,
8
+ UNSET,
9
+ UNSET_SENTINEL,
10
+ )
11
+ from permitstack.utils import FieldMetadata, QueryParamMetadata
12
+ from pydantic import model_serializer
13
+ from typing import Optional
14
+ from typing_extensions import Annotated, NotRequired, TypedDict
15
+
16
+
17
+ class ExportPermitsCsvRequestTypedDict(TypedDict):
18
+ city: NotRequired[Nullable[str]]
19
+ state: NotRequired[Nullable[str]]
20
+ category: NotRequired[Nullable[str]]
21
+ zip_code: NotRequired[Nullable[str]]
22
+ filed_after: NotRequired[Nullable[str]]
23
+ filed_before: NotRequired[Nullable[str]]
24
+ limit: NotRequired[int]
25
+
26
+
27
+ class ExportPermitsCsvRequest(BaseModel):
28
+ city: Annotated[
29
+ OptionalNullable[str],
30
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
31
+ ] = UNSET
32
+
33
+ state: Annotated[
34
+ OptionalNullable[str],
35
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
36
+ ] = UNSET
37
+
38
+ category: Annotated[
39
+ OptionalNullable[str],
40
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
41
+ ] = UNSET
42
+
43
+ zip_code: Annotated[
44
+ OptionalNullable[str],
45
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
46
+ ] = UNSET
47
+
48
+ filed_after: Annotated[
49
+ OptionalNullable[str],
50
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
51
+ ] = UNSET
52
+
53
+ filed_before: Annotated[
54
+ OptionalNullable[str],
55
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
56
+ ] = UNSET
57
+
58
+ limit: Annotated[
59
+ Optional[int],
60
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
61
+ ] = 10000
62
+
63
+ @model_serializer(mode="wrap")
64
+ def serialize_model(self, handler):
65
+ optional_fields = set(
66
+ [
67
+ "city",
68
+ "state",
69
+ "category",
70
+ "zip_code",
71
+ "filed_after",
72
+ "filed_before",
73
+ "limit",
74
+ ]
75
+ )
76
+ nullable_fields = set(
77
+ ["city", "state", "category", "zip_code", "filed_after", "filed_before"]
78
+ )
79
+ serialized = handler(self)
80
+ m = {}
81
+
82
+ for n, f in type(self).model_fields.items():
83
+ k = f.alias or n
84
+ val = serialized.get(k, serialized.get(n))
85
+ is_nullable_and_explicitly_set = (
86
+ k in nullable_fields
87
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
88
+ )
89
+
90
+ if val != UNSET_SENTINEL:
91
+ if (
92
+ val is not None
93
+ or k not in optional_fields
94
+ or is_nullable_and_explicitly_set
95
+ ):
96
+ m[k] = val
97
+
98
+ return m
@@ -0,0 +1,46 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel, UNSET_SENTINEL
5
+ from permitstack.utils import FieldMetadata, PathParamMetadata, QueryParamMetadata
6
+ from pydantic import model_serializer
7
+ from typing import Optional
8
+ from typing_extensions import Annotated, NotRequired, TypedDict
9
+
10
+
11
+ class GetContractorPermitsRequestTypedDict(TypedDict):
12
+ contractor_id: str
13
+ page: NotRequired[int]
14
+ per_page: NotRequired[int]
15
+
16
+
17
+ class GetContractorPermitsRequest(BaseModel):
18
+ contractor_id: Annotated[
19
+ str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
20
+ ]
21
+
22
+ page: Annotated[
23
+ Optional[int],
24
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
25
+ ] = 1
26
+
27
+ per_page: Annotated[
28
+ Optional[int],
29
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
30
+ ] = 25
31
+
32
+ @model_serializer(mode="wrap")
33
+ def serialize_model(self, handler):
34
+ optional_fields = set(["page", "per_page"])
35
+ serialized = handler(self)
36
+ m = {}
37
+
38
+ for n, f in type(self).model_fields.items():
39
+ k = f.alias or n
40
+ val = serialized.get(k, serialized.get(n))
41
+
42
+ if val != UNSET_SENTINEL:
43
+ if val is not None or k not in optional_fields:
44
+ m[k] = val
45
+
46
+ return m
@@ -0,0 +1,16 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel
5
+ from permitstack.utils import FieldMetadata, PathParamMetadata
6
+ from typing_extensions import Annotated, TypedDict
7
+
8
+
9
+ class GetContractorRequestTypedDict(TypedDict):
10
+ contractor_id: str
11
+
12
+
13
+ class GetContractorRequest(BaseModel):
14
+ contractor_id: Annotated[
15
+ str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
16
+ ]
@@ -0,0 +1,16 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel
5
+ from permitstack.utils import FieldMetadata, PathParamMetadata
6
+ from typing_extensions import Annotated, TypedDict
7
+
8
+
9
+ class GetPermitRequestTypedDict(TypedDict):
10
+ permit_id: str
11
+
12
+
13
+ class GetPermitRequest(BaseModel):
14
+ permit_id: Annotated[
15
+ str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
16
+ ]
@@ -0,0 +1,46 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel, UNSET_SENTINEL
5
+ from permitstack.utils import FieldMetadata, PathParamMetadata, QueryParamMetadata
6
+ from pydantic import model_serializer
7
+ from typing import Optional
8
+ from typing_extensions import Annotated, NotRequired, TypedDict
9
+
10
+
11
+ class GetPermitsByAddressRequestTypedDict(TypedDict):
12
+ address: str
13
+ page: NotRequired[int]
14
+ per_page: NotRequired[int]
15
+
16
+
17
+ class GetPermitsByAddressRequest(BaseModel):
18
+ address: Annotated[
19
+ str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
20
+ ]
21
+
22
+ page: Annotated[
23
+ Optional[int],
24
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
25
+ ] = 1
26
+
27
+ per_page: Annotated[
28
+ Optional[int],
29
+ FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
30
+ ] = 25
31
+
32
+ @model_serializer(mode="wrap")
33
+ def serialize_model(self, handler):
34
+ optional_fields = set(["page", "per_page"])
35
+ serialized = handler(self)
36
+ m = {}
37
+
38
+ for n, f in type(self).model_fields.items():
39
+ k = f.alias or n
40
+ val = serialized.get(k, serialized.get(n))
41
+
42
+ if val != UNSET_SENTINEL:
43
+ if val is not None or k not in optional_fields:
44
+ m[k] = val
45
+
46
+ return m
@@ -0,0 +1,18 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from permitstack.types import BaseModel
5
+ from permitstack.utils import FieldMetadata, QueryParamMetadata
6
+ from typing_extensions import Annotated, TypedDict
7
+
8
+
9
+ class GetPropertyHistoryRequestTypedDict(TypedDict):
10
+ address: str
11
+ r"""Street address to look up"""
12
+
13
+
14
+ class GetPropertyHistoryRequest(BaseModel):
15
+ address: Annotated[
16
+ str, FieldMetadata(query=QueryParamMetadata(style="form", explode=True))
17
+ ]
18
+ r"""Street address to look up"""
@@ -0,0 +1,27 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Literal
5
+
6
+
7
+ PermitCategory = Literal[
8
+ "new_construction",
9
+ "renovation",
10
+ "demolition",
11
+ "electrical",
12
+ "plumbing",
13
+ "mechanical",
14
+ "roofing",
15
+ "solar",
16
+ "ev_charger",
17
+ "hvac",
18
+ "fire_alarm",
19
+ "sign",
20
+ "fence",
21
+ "pool",
22
+ "foundation",
23
+ "addition",
24
+ "interior_remodel",
25
+ "grading",
26
+ "other",
27
+ ]
@@ -0,0 +1,164 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from datetime import date
5
+ from permitstack.types import (
6
+ BaseModel,
7
+ Nullable,
8
+ OptionalNullable,
9
+ UNSET,
10
+ UNSET_SENTINEL,
11
+ )
12
+ from pydantic import model_serializer
13
+ from typing import List
14
+ from typing_extensions import NotRequired, TypedDict
15
+
16
+
17
+ class PermitDetailTypedDict(TypedDict):
18
+ id: str
19
+ permit_number: Nullable[str]
20
+ status: str
21
+ category: str
22
+ tags: Nullable[List[str]]
23
+ property_type: str
24
+ address_street: Nullable[str]
25
+ address_city: Nullable[str]
26
+ address_state: Nullable[str]
27
+ address_zip: Nullable[str]
28
+ description_raw: Nullable[str]
29
+ estimated_value: Nullable[float]
30
+ date_filed: Nullable[date]
31
+ date_issued: Nullable[date]
32
+ date_completed: Nullable[date]
33
+ date_expired: Nullable[date]
34
+ fee_amount: Nullable[float]
35
+ stories: Nullable[int]
36
+ units: Nullable[int]
37
+ square_footage: Nullable[float]
38
+ applicant_name: Nullable[str]
39
+ owner_name: Nullable[str]
40
+ contractor_name: NotRequired[Nullable[str]]
41
+ jurisdiction_name: NotRequired[Nullable[str]]
42
+ latitude: NotRequired[Nullable[float]]
43
+ longitude: NotRequired[Nullable[float]]
44
+ contractor_license: NotRequired[Nullable[str]]
45
+ created_at: NotRequired[Nullable[str]]
46
+
47
+
48
+ class PermitDetail(BaseModel):
49
+ id: str
50
+
51
+ permit_number: Nullable[str]
52
+
53
+ status: str
54
+
55
+ category: str
56
+
57
+ tags: Nullable[List[str]]
58
+
59
+ property_type: str
60
+
61
+ address_street: Nullable[str]
62
+
63
+ address_city: Nullable[str]
64
+
65
+ address_state: Nullable[str]
66
+
67
+ address_zip: Nullable[str]
68
+
69
+ description_raw: Nullable[str]
70
+
71
+ estimated_value: Nullable[float]
72
+
73
+ date_filed: Nullable[date]
74
+
75
+ date_issued: Nullable[date]
76
+
77
+ date_completed: Nullable[date]
78
+
79
+ date_expired: Nullable[date]
80
+
81
+ fee_amount: Nullable[float]
82
+
83
+ stories: Nullable[int]
84
+
85
+ units: Nullable[int]
86
+
87
+ square_footage: Nullable[float]
88
+
89
+ applicant_name: Nullable[str]
90
+
91
+ owner_name: Nullable[str]
92
+
93
+ contractor_name: OptionalNullable[str] = UNSET
94
+
95
+ jurisdiction_name: OptionalNullable[str] = UNSET
96
+
97
+ latitude: OptionalNullable[float] = UNSET
98
+
99
+ longitude: OptionalNullable[float] = UNSET
100
+
101
+ contractor_license: OptionalNullable[str] = UNSET
102
+
103
+ created_at: OptionalNullable[str] = UNSET
104
+
105
+ @model_serializer(mode="wrap")
106
+ def serialize_model(self, handler):
107
+ optional_fields = set(
108
+ [
109
+ "contractor_name",
110
+ "jurisdiction_name",
111
+ "latitude",
112
+ "longitude",
113
+ "contractor_license",
114
+ "created_at",
115
+ ]
116
+ )
117
+ nullable_fields = set(
118
+ [
119
+ "permit_number",
120
+ "tags",
121
+ "address_street",
122
+ "address_city",
123
+ "address_state",
124
+ "address_zip",
125
+ "description_raw",
126
+ "estimated_value",
127
+ "date_filed",
128
+ "date_issued",
129
+ "date_completed",
130
+ "contractor_name",
131
+ "jurisdiction_name",
132
+ "latitude",
133
+ "longitude",
134
+ "date_expired",
135
+ "fee_amount",
136
+ "stories",
137
+ "units",
138
+ "square_footage",
139
+ "applicant_name",
140
+ "owner_name",
141
+ "contractor_license",
142
+ "created_at",
143
+ ]
144
+ )
145
+ serialized = handler(self)
146
+ m = {}
147
+
148
+ for n, f in type(self).model_fields.items():
149
+ k = f.alias or n
150
+ val = serialized.get(k, serialized.get(n))
151
+ is_nullable_and_explicitly_set = (
152
+ k in nullable_fields
153
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
154
+ )
155
+
156
+ if val != UNSET_SENTINEL:
157
+ if (
158
+ val is not None
159
+ or k not in optional_fields
160
+ or is_nullable_and_explicitly_set
161
+ ):
162
+ m[k] = val
163
+
164
+ return m
@@ -0,0 +1,24 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from .permitsummary import PermitSummary, PermitSummaryTypedDict
5
+ from permitstack.types import BaseModel
6
+ from typing import List
7
+ from typing_extensions import TypedDict
8
+
9
+
10
+ class PermitSearchResponseTypedDict(TypedDict):
11
+ total: int
12
+ page: int
13
+ per_page: int
14
+ results: List[PermitSummaryTypedDict]
15
+
16
+
17
+ class PermitSearchResponse(BaseModel):
18
+ total: int
19
+
20
+ page: int
21
+
22
+ per_page: int
23
+
24
+ results: List[PermitSummary]
@@ -0,0 +1,16 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Literal
5
+
6
+
7
+ PermitStatus = Literal[
8
+ "filed",
9
+ "issued",
10
+ "in_progress",
11
+ "final",
12
+ "expired",
13
+ "cancelled",
14
+ "revoked",
15
+ "unknown",
16
+ ]
@@ -0,0 +1,121 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from datetime import date
5
+ from permitstack.types import (
6
+ BaseModel,
7
+ Nullable,
8
+ OptionalNullable,
9
+ UNSET,
10
+ UNSET_SENTINEL,
11
+ )
12
+ from pydantic import model_serializer
13
+ from typing import List
14
+ from typing_extensions import NotRequired, TypedDict
15
+
16
+
17
+ class PermitSummaryTypedDict(TypedDict):
18
+ id: str
19
+ permit_number: Nullable[str]
20
+ status: str
21
+ category: str
22
+ tags: Nullable[List[str]]
23
+ property_type: str
24
+ address_street: Nullable[str]
25
+ address_city: Nullable[str]
26
+ address_state: Nullable[str]
27
+ address_zip: Nullable[str]
28
+ description_raw: Nullable[str]
29
+ estimated_value: Nullable[float]
30
+ date_filed: Nullable[date]
31
+ date_issued: Nullable[date]
32
+ date_completed: Nullable[date]
33
+ contractor_name: NotRequired[Nullable[str]]
34
+ jurisdiction_name: NotRequired[Nullable[str]]
35
+ latitude: NotRequired[Nullable[float]]
36
+ longitude: NotRequired[Nullable[float]]
37
+
38
+
39
+ class PermitSummary(BaseModel):
40
+ id: str
41
+
42
+ permit_number: Nullable[str]
43
+
44
+ status: str
45
+
46
+ category: str
47
+
48
+ tags: Nullable[List[str]]
49
+
50
+ property_type: str
51
+
52
+ address_street: Nullable[str]
53
+
54
+ address_city: Nullable[str]
55
+
56
+ address_state: Nullable[str]
57
+
58
+ address_zip: Nullable[str]
59
+
60
+ description_raw: Nullable[str]
61
+
62
+ estimated_value: Nullable[float]
63
+
64
+ date_filed: Nullable[date]
65
+
66
+ date_issued: Nullable[date]
67
+
68
+ date_completed: Nullable[date]
69
+
70
+ contractor_name: OptionalNullable[str] = UNSET
71
+
72
+ jurisdiction_name: OptionalNullable[str] = UNSET
73
+
74
+ latitude: OptionalNullable[float] = UNSET
75
+
76
+ longitude: OptionalNullable[float] = UNSET
77
+
78
+ @model_serializer(mode="wrap")
79
+ def serialize_model(self, handler):
80
+ optional_fields = set(
81
+ ["contractor_name", "jurisdiction_name", "latitude", "longitude"]
82
+ )
83
+ nullable_fields = set(
84
+ [
85
+ "permit_number",
86
+ "tags",
87
+ "address_street",
88
+ "address_city",
89
+ "address_state",
90
+ "address_zip",
91
+ "description_raw",
92
+ "estimated_value",
93
+ "date_filed",
94
+ "date_issued",
95
+ "date_completed",
96
+ "contractor_name",
97
+ "jurisdiction_name",
98
+ "latitude",
99
+ "longitude",
100
+ ]
101
+ )
102
+ serialized = handler(self)
103
+ m = {}
104
+
105
+ for n, f in type(self).model_fields.items():
106
+ k = f.alias or n
107
+ val = serialized.get(k, serialized.get(n))
108
+ is_nullable_and_explicitly_set = (
109
+ k in nullable_fields
110
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
111
+ )
112
+
113
+ if val != UNSET_SENTINEL:
114
+ if (
115
+ val is not None
116
+ or k not in optional_fields
117
+ or is_nullable_and_explicitly_set
118
+ ):
119
+ m[k] = val
120
+
121
+ return m