karrio-cli 2025.5rc3__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.
- karrio_cli/__init__.py +0 -0
- karrio_cli/__main__.py +105 -0
- karrio_cli/ai/README.md +335 -0
- karrio_cli/ai/__init__.py +0 -0
- karrio_cli/ai/commands.py +102 -0
- karrio_cli/ai/karrio_ai/__init__.py +1 -0
- karrio_cli/ai/karrio_ai/agent.py +972 -0
- karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
- karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
- karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
- karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
- karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
- karrio_cli/ai/karrio_ai/rag_system.py +503 -0
- karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
- karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
- karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
- karrio_cli/commands/__init__.py +0 -0
- karrio_cli/commands/codegen.py +336 -0
- karrio_cli/commands/login.py +139 -0
- karrio_cli/commands/plugins.py +168 -0
- karrio_cli/commands/sdk.py +870 -0
- karrio_cli/common/queries.py +101 -0
- karrio_cli/common/utils.py +368 -0
- karrio_cli/resources/__init__.py +0 -0
- karrio_cli/resources/carriers.py +91 -0
- karrio_cli/resources/connections.py +207 -0
- karrio_cli/resources/events.py +151 -0
- karrio_cli/resources/logs.py +151 -0
- karrio_cli/resources/orders.py +144 -0
- karrio_cli/resources/shipments.py +210 -0
- karrio_cli/resources/trackers.py +287 -0
- karrio_cli/templates/__init__.py +9 -0
- karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
- karrio_cli/templates/address.py +308 -0
- karrio_cli/templates/docs.py +150 -0
- karrio_cli/templates/documents.py +428 -0
- karrio_cli/templates/manifest.py +396 -0
- karrio_cli/templates/pickup.py +839 -0
- karrio_cli/templates/rates.py +638 -0
- karrio_cli/templates/sdk.py +947 -0
- karrio_cli/templates/shipments.py +892 -0
- karrio_cli/templates/tracking.py +437 -0
- karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
- karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
- karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
- karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
- karrio_cli-2025.5rc3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,308 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
PROVIDER_ADDRESS_TEMPLATE = Template(
|
4
|
+
'''"""Karrio {{name}} address validation API implementation."""
|
5
|
+
|
6
|
+
# IMPLEMENTATION INSTRUCTIONS:
|
7
|
+
# 1. Uncomment the imports when the schema types are generated
|
8
|
+
# 2. Import the specific request and response types you need
|
9
|
+
# 3. Create a request instance with the appropriate request type
|
10
|
+
# 4. Extract address validation details from the response
|
11
|
+
#
|
12
|
+
# NOTE: JSON schema types are generated with "Type" suffix (e.g., AddressValidationRequestType),
|
13
|
+
# while XML schema types don't have this suffix (e.g., AddressValidationRequest).
|
14
|
+
|
15
|
+
import karrio.schemas.{{id}}.address_validation_request as {{id}}_req
|
16
|
+
import karrio.schemas.{{id}}.address_validation_response as {{id}}_res
|
17
|
+
|
18
|
+
import typing
|
19
|
+
import karrio.lib as lib
|
20
|
+
import karrio.core.units as units
|
21
|
+
import karrio.core.models as models
|
22
|
+
import karrio.providers.{{id}}.error as error
|
23
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
24
|
+
import karrio.providers.{{id}}.units as provider_units
|
25
|
+
|
26
|
+
|
27
|
+
def parse_address_validation_response(
|
28
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
29
|
+
settings: provider_utils.Settings,
|
30
|
+
) -> typing.Tuple[models.AddressValidationDetails, typing.List[models.Message]]:
|
31
|
+
response = _response.deserialize()
|
32
|
+
messages = error.parse_error_response(response, settings)
|
33
|
+
|
34
|
+
validation_details = models.AddressValidationDetails(
|
35
|
+
carrier_id=settings.carrier_id,
|
36
|
+
carrier_name=settings.carrier_name,
|
37
|
+
success=True,
|
38
|
+
)
|
39
|
+
|
40
|
+
return validation_details, messages
|
41
|
+
|
42
|
+
|
43
|
+
def address_validation_request(
|
44
|
+
payload: models.AddressValidationRequest,
|
45
|
+
settings: provider_utils.Settings,
|
46
|
+
) -> lib.Serializable:
|
47
|
+
"""
|
48
|
+
Create an address validation request for the carrier API
|
49
|
+
|
50
|
+
payload: The standardized AddressValidationRequest from karrio
|
51
|
+
settings: The carrier connection settings
|
52
|
+
|
53
|
+
Returns a Serializable object that can be sent to the carrier API
|
54
|
+
"""
|
55
|
+
# Extract the address from payload
|
56
|
+
address = lib.to_address(payload.address)
|
57
|
+
|
58
|
+
{% if is_xml_api %}
|
59
|
+
# For XML API request
|
60
|
+
request = {{id}}_req.AddressValidationRequest(
|
61
|
+
street=address.address_line1,
|
62
|
+
city_name=address.city,
|
63
|
+
postcode=address.postal_code,
|
64
|
+
country_code=address.country_code,
|
65
|
+
province_code=address.state_code,
|
66
|
+
)
|
67
|
+
{% else %}
|
68
|
+
# For JSON API request - Using camelCase attribute names to match schema definition
|
69
|
+
request = {{id}}_req.AddressValidationRequestType(
|
70
|
+
streetAddress=address.address_line1,
|
71
|
+
cityLocality=address.city,
|
72
|
+
postalCode=address.postal_code,
|
73
|
+
countryCode=address.country_code,
|
74
|
+
stateProvince=address.state_code,
|
75
|
+
)
|
76
|
+
{% endif %}
|
77
|
+
|
78
|
+
return lib.Serializable(request, {% if is_xml_api %}lib.to_xml{% else %}lib.to_dict{% endif %})
|
79
|
+
'''
|
80
|
+
)
|
81
|
+
|
82
|
+
JSON_SCHEMA_ADDRESS_VALIDATION_REQUEST_TEMPLATE = Template('''
|
83
|
+
{
|
84
|
+
"streetAddress": "123 Main St",
|
85
|
+
"cityLocality": "City Name",
|
86
|
+
"postalCode": "12345",
|
87
|
+
"countryCode": "US",
|
88
|
+
"stateProvince": "CA"
|
89
|
+
}
|
90
|
+
''')
|
91
|
+
|
92
|
+
JSON_SCHEMA_ADDRESS_VALIDATION_RESPONSE_TEMPLATE = Template('''
|
93
|
+
{
|
94
|
+
"isValid": true,
|
95
|
+
"normalizedAddress": {
|
96
|
+
"streetAddress": "123 Main St",
|
97
|
+
"cityLocality": "City Name",
|
98
|
+
"postalCode": "12345",
|
99
|
+
"countryCode": "US",
|
100
|
+
"stateProvince": "CA"
|
101
|
+
},
|
102
|
+
"validationMessages": [
|
103
|
+
{
|
104
|
+
"message": "Address is valid",
|
105
|
+
"code": "SUCCESS"
|
106
|
+
}
|
107
|
+
]
|
108
|
+
}
|
109
|
+
''')
|
110
|
+
|
111
|
+
XML_SCHEMA_ADDRESS_VALIDATION_REQUEST_TEMPLATE = Template('''<?xml version="1.0"?>
|
112
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/address" xmlns="http://{{id}}.com/ws/address" elementFormDefault="qualified">
|
113
|
+
<xsd:element name="address-validation-request">
|
114
|
+
<xsd:complexType>
|
115
|
+
<xsd:sequence>
|
116
|
+
<xsd:element name="street" type="xsd:string" />
|
117
|
+
<xsd:element name="city_name" type="xsd:string" />
|
118
|
+
<xsd:element name="postcode" type="xsd:string" />
|
119
|
+
<xsd:element name="country_code" type="xsd:string" />
|
120
|
+
<xsd:element name="province_code" type="xsd:string" minOccurs="0" />
|
121
|
+
</xsd:sequence>
|
122
|
+
</xsd:complexType>
|
123
|
+
</xsd:element>
|
124
|
+
</xsd:schema>
|
125
|
+
''')
|
126
|
+
|
127
|
+
XML_SCHEMA_ADDRESS_VALIDATION_RESPONSE_TEMPLATE = Template('''<?xml version="1.0"?>
|
128
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/address" xmlns="http://{{id}}.com/ws/address" elementFormDefault="qualified">
|
129
|
+
<xsd:element name="address-validation-response">
|
130
|
+
<xsd:complexType>
|
131
|
+
<xsd:sequence>
|
132
|
+
<xsd:element name="valid_address" type="xsd:string" />
|
133
|
+
<xsd:element name="normalized_address" minOccurs="0">
|
134
|
+
<xsd:complexType>
|
135
|
+
<xsd:sequence>
|
136
|
+
<xsd:element name="street" type="xsd:string" />
|
137
|
+
<xsd:element name="city_name" type="xsd:string" />
|
138
|
+
<xsd:element name="postcode" type="xsd:string" />
|
139
|
+
<xsd:element name="country_code" type="xsd:string" />
|
140
|
+
<xsd:element name="province_code" type="xsd:string" minOccurs="0" />
|
141
|
+
</xsd:sequence>
|
142
|
+
</xsd:complexType>
|
143
|
+
</xsd:element>
|
144
|
+
<xsd:element name="validation_messages" minOccurs="0">
|
145
|
+
<xsd:complexType>
|
146
|
+
<xsd:sequence>
|
147
|
+
<xsd:element name="message" maxOccurs="unbounded">
|
148
|
+
<xsd:complexType>
|
149
|
+
<xsd:sequence>
|
150
|
+
<xsd:element name="text" type="xsd:string" />
|
151
|
+
<xsd:element name="code" type="xsd:string" />
|
152
|
+
</xsd:sequence>
|
153
|
+
</xsd:complexType>
|
154
|
+
</xsd:element>
|
155
|
+
</xsd:sequence>
|
156
|
+
</xsd:complexType>
|
157
|
+
</xsd:element>
|
158
|
+
</xsd:sequence>
|
159
|
+
</xsd:complexType>
|
160
|
+
</xsd:element>
|
161
|
+
</xsd:schema>
|
162
|
+
''')
|
163
|
+
|
164
|
+
TEST_ADDRESS_TEMPLATE = Template('''"""{{name}} carrier address validation tests."""
|
165
|
+
|
166
|
+
import unittest
|
167
|
+
from unittest.mock import patch, ANY
|
168
|
+
from .fixture import gateway
|
169
|
+
import logging
|
170
|
+
import karrio.sdk as karrio
|
171
|
+
import karrio.lib as lib
|
172
|
+
import karrio.core.models as models
|
173
|
+
|
174
|
+
logger = logging.getLogger(__name__)
|
175
|
+
|
176
|
+
class Test{{compact_name}}Address(unittest.TestCase):
|
177
|
+
def setUp(self):
|
178
|
+
self.maxDiff = None
|
179
|
+
self.AddressValidationRequest = models.AddressValidationRequest(**AddressValidationPayload)
|
180
|
+
|
181
|
+
def test_create_address_validation_request(self):
|
182
|
+
request = gateway.mapper.create_address_validation_request(self.AddressValidationRequest)
|
183
|
+
self.assertEqual(lib.to_dict(request.serialize()), AddressValidationRequest)
|
184
|
+
|
185
|
+
def test_validate_address(self):
|
186
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
187
|
+
mock.return_value = {% if is_xml_api %}"<r></r>"{% else %}"{}"{% endif %}
|
188
|
+
karrio.Address.validate(self.AddressValidationRequest).from_(gateway)
|
189
|
+
self.assertEqual(
|
190
|
+
mock.call_args[1]["url"],
|
191
|
+
f"{gateway.settings.server_url}/address/validate"
|
192
|
+
)
|
193
|
+
|
194
|
+
def test_parse_address_validation_response(self):
|
195
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
196
|
+
mock.return_value = AddressValidationResponse
|
197
|
+
parsed_response = (
|
198
|
+
karrio.Address.validate(self.AddressValidationRequest)
|
199
|
+
.from_(gateway)
|
200
|
+
.parse()
|
201
|
+
)
|
202
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedAddressValidationResponse)
|
203
|
+
|
204
|
+
def test_parse_error_response(self):
|
205
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
206
|
+
mock.return_value = ErrorResponse
|
207
|
+
parsed_response = (
|
208
|
+
karrio.Address.validate(self.AddressValidationRequest)
|
209
|
+
.from_(gateway)
|
210
|
+
.parse()
|
211
|
+
)
|
212
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedErrorResponse)
|
213
|
+
|
214
|
+
|
215
|
+
if __name__ == "__main__":
|
216
|
+
unittest.main()
|
217
|
+
|
218
|
+
|
219
|
+
AddressValidationPayload = {
|
220
|
+
"address": {
|
221
|
+
"address_line1": "123 Main St",
|
222
|
+
"city": "City Name",
|
223
|
+
"postal_code": "12345",
|
224
|
+
"country_code": "US",
|
225
|
+
"state_code": "CA",
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
AddressValidationRequest = {% if is_xml_api %}"""<?xml version="1.0"?>
|
230
|
+
<address-validation-request>
|
231
|
+
<street>123 Main St</street>
|
232
|
+
<city_name>City Name</city_name>
|
233
|
+
<postcode>12345</postcode>
|
234
|
+
<country_code>US</country_code>
|
235
|
+
<province_code>CA</province_code>
|
236
|
+
</address-validation-request>"""{% else %}{
|
237
|
+
"streetAddress": "123 Main St",
|
238
|
+
"cityLocality": "City Name",
|
239
|
+
"postalCode": "12345",
|
240
|
+
"countryCode": "US",
|
241
|
+
"stateProvince": "CA"
|
242
|
+
}{% endif %}
|
243
|
+
|
244
|
+
AddressValidationResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
245
|
+
<address-validation-response>
|
246
|
+
<valid_address>true</valid_address>
|
247
|
+
<normalized_address>
|
248
|
+
<street>123 MAIN ST</street>
|
249
|
+
<city_name>CITY NAME</city_name>
|
250
|
+
<postcode>12345</postcode>
|
251
|
+
<country_code>US</country_code>
|
252
|
+
<province_code>CA</province_code>
|
253
|
+
</normalized_address>
|
254
|
+
</address-validation-response>"""{% else %}"""{
|
255
|
+
"isValid": true,
|
256
|
+
"normalizedAddress": {
|
257
|
+
"streetAddress": "123 MAIN ST",
|
258
|
+
"cityLocality": "CITY NAME",
|
259
|
+
"postalCode": "12345",
|
260
|
+
"countryCode": "US",
|
261
|
+
"stateProvince": "CA"
|
262
|
+
},
|
263
|
+
"validationMessages": [
|
264
|
+
{
|
265
|
+
"message": "Address is valid",
|
266
|
+
"code": "SUCCESS"
|
267
|
+
}
|
268
|
+
]
|
269
|
+
}"""{% endif %}
|
270
|
+
|
271
|
+
ErrorResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
272
|
+
<error-response>
|
273
|
+
<e>
|
274
|
+
<code>address_error</code>
|
275
|
+
<message>Unable to validate address</message>
|
276
|
+
<details>Invalid address information provided</details>
|
277
|
+
</e>
|
278
|
+
</error-response>"""{% else %}"""{
|
279
|
+
"error": {
|
280
|
+
"code": "address_error",
|
281
|
+
"message": "Unable to validate address",
|
282
|
+
"details": "Invalid address information provided"
|
283
|
+
}
|
284
|
+
}"""{% endif %}
|
285
|
+
|
286
|
+
ParsedAddressValidationResponse = [
|
287
|
+
{
|
288
|
+
"carrier_id": "{{id}}",
|
289
|
+
"carrier_name": "{{id}}",
|
290
|
+
"success": True
|
291
|
+
},
|
292
|
+
[]
|
293
|
+
]
|
294
|
+
|
295
|
+
ParsedErrorResponse = [
|
296
|
+
None,
|
297
|
+
[
|
298
|
+
{
|
299
|
+
"carrier_id": "{{id}}",
|
300
|
+
"carrier_name": "{{id}}",
|
301
|
+
"code": "address_error",
|
302
|
+
"message": "Unable to validate address",
|
303
|
+
"details": {
|
304
|
+
"details": "Invalid address information provided"
|
305
|
+
}
|
306
|
+
}
|
307
|
+
]
|
308
|
+
]''')
|
@@ -0,0 +1,150 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
|
4
|
+
EMPTY_FILE_TEMPLATE = Template("")
|
5
|
+
|
6
|
+
|
7
|
+
MODELS_TEMPLATE = Template(
|
8
|
+
"""
|
9
|
+
{% for name, cls in classes.items() %}
|
10
|
+
#### {{ name }}
|
11
|
+
|
12
|
+
| Name | Type | Description
|
13
|
+
| --- | --- | --- |
|
14
|
+
{% for prop in cls.__attrs_attrs__ %}| `{{ prop.name }}` | {{ prop.type }} | {{ '**required**' if str(prop.default) == 'NOTHING' else '' }}
|
15
|
+
{% endfor %}
|
16
|
+
{% endfor %}
|
17
|
+
"""
|
18
|
+
)
|
19
|
+
|
20
|
+
SETTINGS_TEMPLATE = Template(
|
21
|
+
"""
|
22
|
+
{% for k, val in settings.items() %}
|
23
|
+
#### {{ val['label'] }} Settings `[carrier_name = {{k}}]`
|
24
|
+
|
25
|
+
| Name | Type | Description
|
26
|
+
| --- | --- | --- |
|
27
|
+
{% for prop in val['Settings'].__attrs_attrs__ %}| `{{ prop.name }}` | {{ prop.type }} | {{ '**required**' if str(prop.default) == 'NOTHING' else '' }}
|
28
|
+
{% endfor %}
|
29
|
+
{% endfor %}
|
30
|
+
"""
|
31
|
+
)
|
32
|
+
|
33
|
+
SERVICES_TEMPLATE = Template(
|
34
|
+
"""
|
35
|
+
{% for key, value in service_mappers.items() %}
|
36
|
+
#### {{ mappers[key]["label"] }}
|
37
|
+
|
38
|
+
| Code | Identifier
|
39
|
+
| --- | ---
|
40
|
+
{% for code, name in value.items() %}| `{{ code }}` | {{ name }}
|
41
|
+
{% endfor %}
|
42
|
+
{% endfor %}
|
43
|
+
"""
|
44
|
+
)
|
45
|
+
|
46
|
+
SHIPMENT_OPTIONS_TEMPLATE = Template(
|
47
|
+
"""
|
48
|
+
{% for key, value in option_mappers.items() %}
|
49
|
+
#### {{ mappers[key]["label"] }}
|
50
|
+
|
51
|
+
| Code | Identifier | Description
|
52
|
+
| --- | --- | ---
|
53
|
+
{% for code, spec in value.items() %}| `{{ code }}` | {{ spec.get('key') }} | {{ spec.get('type') }}
|
54
|
+
{% endfor %}
|
55
|
+
{% endfor %}
|
56
|
+
"""
|
57
|
+
)
|
58
|
+
|
59
|
+
PACKAGING_TYPES_TEMPLATE = Template(
|
60
|
+
"""
|
61
|
+
{% for key, value in packaging_mappers.items() %}
|
62
|
+
#### {{ mappers[key]["label"] }}
|
63
|
+
|
64
|
+
| Code | Identifier
|
65
|
+
| --- | ---
|
66
|
+
{% for code, name in value.items() %}| `{{ code }}` | {{ name }}
|
67
|
+
{% endfor %}
|
68
|
+
{% endfor %}
|
69
|
+
"""
|
70
|
+
)
|
71
|
+
|
72
|
+
SHIPMENT_PRESETS_TEMPLATE = Template(
|
73
|
+
"""
|
74
|
+
{% for key, value in preset_mappers.items() %}
|
75
|
+
#### {{ mappers[key]["label"] }}
|
76
|
+
|
77
|
+
| Code | Dimensions | Note
|
78
|
+
| --- | --- | ---
|
79
|
+
{% for code, dim in value.items() %}{{ format_dimension(code, dim) }}
|
80
|
+
{% endfor %}
|
81
|
+
{% endfor %}
|
82
|
+
"""
|
83
|
+
)
|
84
|
+
|
85
|
+
UTILS_TOOLS_TEMPLATE = Template(
|
86
|
+
"""
|
87
|
+
{% for tool, import in utils %}
|
88
|
+
- `{{ tool.__name__ }}`
|
89
|
+
|
90
|
+
Usage:
|
91
|
+
```python
|
92
|
+
{{ import }}
|
93
|
+
```
|
94
|
+
|
95
|
+
```text
|
96
|
+
{{ doc(tool) }}
|
97
|
+
```
|
98
|
+
|
99
|
+
{% endfor %}
|
100
|
+
"""
|
101
|
+
)
|
102
|
+
|
103
|
+
COUNTRY_INFO_TEMPLATES = Template(
|
104
|
+
"""
|
105
|
+
## Countries
|
106
|
+
|
107
|
+
| Code | Name
|
108
|
+
| --- | ---
|
109
|
+
{% for code, name in countries.items() %}| `{{ code }}` | {{ name }}
|
110
|
+
{% endfor %}
|
111
|
+
|
112
|
+
## States and Provinces
|
113
|
+
|
114
|
+
{% for country, states in country_states.items() %}
|
115
|
+
|
116
|
+
### {{ countries[country] }}
|
117
|
+
|
118
|
+
| Code | Name
|
119
|
+
| --- | ---
|
120
|
+
{% for code, name in states.items() %}| `{{ code }}` | {{ name }}
|
121
|
+
{% endfor %}
|
122
|
+
{% endfor %}
|
123
|
+
|
124
|
+
## Currencies
|
125
|
+
|
126
|
+
| Code | Name
|
127
|
+
| --- | ---
|
128
|
+
{% for code, name in currencies.items() %}| `{{ code }}` | {{ name }}
|
129
|
+
{% endfor %}
|
130
|
+
"""
|
131
|
+
)
|
132
|
+
|
133
|
+
UNITS_TEMPLATES = Template(
|
134
|
+
"""
|
135
|
+
## WEIGHT UNITS
|
136
|
+
|
137
|
+
| Code | Identifier
|
138
|
+
| --- | ---
|
139
|
+
{% for code, name in weight_units.items() %}| `{{ code }}` | {{ name }}
|
140
|
+
{% endfor %}
|
141
|
+
|
142
|
+
## DIMENSION UNITS
|
143
|
+
|
144
|
+
| Code | Identifier
|
145
|
+
| --- | ---
|
146
|
+
{% for code, name in dimension_units.items() %}| `{{ code }}` | {{ name }}
|
147
|
+
{% endfor %}
|
148
|
+
|
149
|
+
"""
|
150
|
+
)
|