karrio-server-core 2025.5rc12__py3-none-any.whl → 2026.1.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.
- karrio/server/core/authentication.py +59 -25
- karrio/server/core/config.py +31 -0
- karrio/server/core/datatypes.py +30 -4
- karrio/server/core/dataunits.py +53 -22
- karrio/server/core/exceptions.py +287 -17
- karrio/server/core/filters.py +14 -0
- karrio/server/core/gateway.py +285 -10
- karrio/server/core/logging.py +403 -0
- karrio/server/core/management/commands/runserver.py +5 -0
- karrio/server/core/middleware.py +104 -2
- karrio/server/core/migrations/0006_add_api_log_requested_at_index.py +22 -0
- karrio/server/core/models/base.py +34 -1
- karrio/server/core/oauth_validators.py +2 -3
- karrio/server/core/permissions.py +1 -2
- karrio/server/core/serializers.py +183 -10
- karrio/server/core/signals.py +22 -28
- karrio/server/core/telemetry.py +573 -0
- karrio/server/core/tests/__init__.py +27 -0
- karrio/server/core/{tests.py → tests/base.py} +6 -7
- karrio/server/core/tests/test_exception_level.py +159 -0
- karrio/server/core/tests/test_resource_token.py +593 -0
- karrio/server/core/utils.py +688 -38
- karrio/server/core/validators.py +144 -222
- karrio/server/core/views/oauth.py +13 -12
- karrio/server/core/views/references.py +2 -2
- karrio/server/iam/apps.py +1 -4
- karrio/server/iam/migrations/0002_setup_carrier_permission_groups.py +103 -0
- karrio/server/iam/migrations/0003_remove_permission_groups.py +91 -0
- karrio/server/iam/permissions.py +7 -134
- karrio/server/iam/serializers.py +17 -2
- karrio/server/iam/signals.py +2 -4
- karrio/server/providers/admin.py +1 -1
- karrio/server/providers/management/commands/migrate_rate_sheets.py +101 -0
- karrio/server/providers/migrations/0082_add_zone_identifiers.py +50 -0
- karrio/server/providers/migrations/0083_add_optimized_rate_sheet_structure.py +33 -0
- karrio/server/providers/migrations/0084_alter_servicelevel_currency.py +168 -0
- karrio/server/providers/migrations/0085_populate_dhl_parcel_de_oauth_credentials.py +82 -0
- karrio/server/providers/migrations/0086_rename_dhl_parcel_de_customer_number_to_billing_number.py +71 -0
- karrio/server/providers/migrations/0087_alter_carrier_capabilities.py +38 -0
- karrio/server/providers/migrations/0088_servicelevel_surcharges.py +24 -0
- karrio/server/providers/migrations/0089_servicelevel_cost_max_volume.py +31 -0
- karrio/server/providers/migrations/0090_ratesheet_surcharges_servicelevel_zone_surcharge_ids.py +47 -0
- karrio/server/providers/migrations/0091_migrate_legacy_zones_surcharges.py +154 -0
- karrio/server/providers/models/__init__.py +1 -2
- karrio/server/providers/models/carrier.py +103 -18
- karrio/server/providers/models/service.py +188 -1
- karrio/server/providers/models/sheet.py +371 -0
- karrio/server/providers/serializers/base.py +263 -2
- karrio/server/providers/signals.py +2 -4
- karrio/server/providers/templates/providers/oauth_callback.html +105 -0
- karrio/server/providers/tests/__init__.py +5 -0
- karrio/server/providers/tests/test_connections.py +895 -0
- karrio/server/providers/views/carriers.py +1 -3
- karrio/server/providers/views/connections.py +322 -2
- karrio/server/samples.py +1 -1
- karrio/server/serializers/abstract.py +116 -21
- karrio/server/tracing/migrations/0007_tracingrecord_tracing_created_at_idx.py +19 -0
- karrio/server/tracing/models.py +2 -0
- karrio/server/tracing/utils.py +5 -8
- karrio/server/user/migrations/0007_user_metadata.py +25 -0
- karrio/server/user/models.py +38 -23
- karrio/server/user/serializers.py +1 -0
- karrio/server/user/templates/registration/registration_confirm_email.html +1 -1
- {karrio_server_core-2025.5rc12.dist-info → karrio_server_core-2026.1.1.dist-info}/METADATA +2 -2
- {karrio_server_core-2025.5rc12.dist-info → karrio_server_core-2026.1.1.dist-info}/RECORD +67 -86
- karrio/server/providers/extension/__init__.py +0 -1
- karrio/server/providers/extension/models/__init__.py +0 -1
- karrio/server/providers/extension/models/allied_express.py +0 -22
- karrio/server/providers/extension/models/allied_express_local.py +0 -22
- karrio/server/providers/extension/models/amazon_shipping.py +0 -27
- karrio/server/providers/extension/models/aramex.py +0 -25
- karrio/server/providers/extension/models/asendia_us.py +0 -21
- karrio/server/providers/extension/models/australiapost.py +0 -20
- karrio/server/providers/extension/models/boxknight.py +0 -19
- karrio/server/providers/extension/models/bpost.py +0 -21
- karrio/server/providers/extension/models/canadapost.py +0 -21
- karrio/server/providers/extension/models/canpar.py +0 -19
- karrio/server/providers/extension/models/chronopost.py +0 -22
- karrio/server/providers/extension/models/colissimo.py +0 -22
- karrio/server/providers/extension/models/dhl_express.py +0 -23
- karrio/server/providers/extension/models/dhl_parcel_de.py +0 -25
- karrio/server/providers/extension/models/dhl_poland.py +0 -22
- karrio/server/providers/extension/models/dhl_universal.py +0 -19
- karrio/server/providers/extension/models/dicom.py +0 -20
- karrio/server/providers/extension/models/dpd.py +0 -37
- karrio/server/providers/extension/models/dpdhl.py +0 -26
- karrio/server/providers/extension/models/easypost.py +0 -20
- karrio/server/providers/extension/models/eshipper.py +0 -21
- karrio/server/providers/extension/models/fedex.py +0 -25
- karrio/server/providers/extension/models/fedex_ws.py +0 -24
- karrio/server/providers/extension/models/freightcom.py +0 -21
- karrio/server/providers/extension/models/generic.py +0 -35
- karrio/server/providers/extension/models/geodis.py +0 -22
- karrio/server/providers/extension/models/hay_post.py +0 -22
- karrio/server/providers/extension/models/laposte.py +0 -19
- karrio/server/providers/extension/models/locate2u.py +0 -22
- karrio/server/providers/extension/models/nationex.py +0 -22
- karrio/server/providers/extension/models/purolator.py +0 -21
- karrio/server/providers/extension/models/roadie.py +0 -18
- karrio/server/providers/extension/models/royalmail.py +0 -19
- karrio/server/providers/extension/models/sendle.py +0 -22
- karrio/server/providers/extension/models/tge.py +0 -63
- karrio/server/providers/extension/models/tnt.py +0 -23
- karrio/server/providers/extension/models/ups.py +0 -23
- karrio/server/providers/extension/models/usps.py +0 -23
- karrio/server/providers/extension/models/usps_international.py +0 -23
- karrio/server/providers/extension/models/usps_wt.py +0 -24
- karrio/server/providers/extension/models/usps_wt_international.py +0 -24
- karrio/server/providers/extension/models/zoom2u.py +0 -23
- karrio/server/providers/tests.py +0 -3
- {karrio_server_core-2025.5rc12.dist-info → karrio_server_core-2026.1.1.dist-info}/WHEEL +0 -0
- {karrio_server_core-2025.5rc12.dist-info → karrio_server_core-2026.1.1.dist-info}/top_level.txt +0 -0
|
@@ -21,6 +21,7 @@ class ShipmentStatus(utils.Enum):
|
|
|
21
21
|
|
|
22
22
|
class TrackerStatus(utils.Enum):
|
|
23
23
|
pending = "pending"
|
|
24
|
+
picked_up = "picked_up"
|
|
24
25
|
unknown = "unknown"
|
|
25
26
|
on_hold = "on_hold"
|
|
26
27
|
cancelled = "cancelled"
|
|
@@ -49,6 +50,9 @@ CUSTOMS_CONTENT_TYPE = [(c.name, c.name) for c in list(units.CustomsContentType)
|
|
|
49
50
|
UPLOAD_DOCUMENT_TYPE = [(c.name, c.name) for c in list(units.UploadDocumentType)]
|
|
50
51
|
LABEL_TYPES = [(c.name, c.name) for c in list(units.LabelType)]
|
|
51
52
|
LABEL_TEMPLATE_TYPES = [("SVG", "SVG"), ("ZPL", "ZPL")]
|
|
53
|
+
TRACKING_INCIDENT_REASONS = [
|
|
54
|
+
(c.name, c.name) for c in list(units.TrackingIncidentReason)
|
|
55
|
+
]
|
|
52
56
|
|
|
53
57
|
|
|
54
58
|
class CarrierDetails(serializers.Serializer):
|
|
@@ -129,6 +133,7 @@ class APIError(serializers.Serializer):
|
|
|
129
133
|
required=False, help_text="The error or warning message"
|
|
130
134
|
)
|
|
131
135
|
code = serializers.CharField(required=False, help_text="The message code")
|
|
136
|
+
level = serializers.CharField(required=False, help_text="The message level")
|
|
132
137
|
details = serializers.DictField(required=False, help_text="any additional details")
|
|
133
138
|
|
|
134
139
|
|
|
@@ -153,7 +158,7 @@ class AddressData(validators.AugmentedAddressSerializer):
|
|
|
153
158
|
required=False,
|
|
154
159
|
allow_blank=True,
|
|
155
160
|
allow_null=True,
|
|
156
|
-
max_length=
|
|
161
|
+
max_length=20,
|
|
157
162
|
help_text="""The address postal code
|
|
158
163
|
**(required for shipment purchase)**
|
|
159
164
|
""",
|
|
@@ -323,6 +328,26 @@ class CommodityData(serializers.Serializer):
|
|
|
323
328
|
choices=COUNTRIES,
|
|
324
329
|
help_text="The origin or manufacture country",
|
|
325
330
|
)
|
|
331
|
+
product_url = serializers.CharField(
|
|
332
|
+
required=False,
|
|
333
|
+
allow_null=True,
|
|
334
|
+
help_text="The product url",
|
|
335
|
+
)
|
|
336
|
+
image_url = serializers.CharField(
|
|
337
|
+
required=False,
|
|
338
|
+
allow_null=True,
|
|
339
|
+
help_text="The image url",
|
|
340
|
+
)
|
|
341
|
+
product_id = serializers.CharField(
|
|
342
|
+
required=False,
|
|
343
|
+
allow_null=True,
|
|
344
|
+
help_text="The product id",
|
|
345
|
+
)
|
|
346
|
+
variant_id = serializers.CharField(
|
|
347
|
+
required=False,
|
|
348
|
+
allow_null=True,
|
|
349
|
+
help_text="The variant id",
|
|
350
|
+
)
|
|
326
351
|
parent_id = serializers.CharField(
|
|
327
352
|
required=False,
|
|
328
353
|
allow_null=True,
|
|
@@ -372,7 +397,7 @@ class ParcelData(validators.PresetSerializer):
|
|
|
372
397
|
required=False,
|
|
373
398
|
allow_blank=True,
|
|
374
399
|
allow_null=True,
|
|
375
|
-
max_length=
|
|
400
|
+
max_length=100,
|
|
376
401
|
help_text=f"""The parcel's packaging type.<br/>
|
|
377
402
|
**Note that the packaging is optional when using a package preset.**<br/>
|
|
378
403
|
values: <br/>
|
|
@@ -384,7 +409,7 @@ class ParcelData(validators.PresetSerializer):
|
|
|
384
409
|
required=False,
|
|
385
410
|
allow_blank=True,
|
|
386
411
|
allow_null=True,
|
|
387
|
-
max_length=
|
|
412
|
+
max_length=100,
|
|
388
413
|
help_text="""The parcel's package preset.<br/>
|
|
389
414
|
For carrier specific package presets, please consult the reference.
|
|
390
415
|
""",
|
|
@@ -613,6 +638,12 @@ class Charge(serializers.Serializer):
|
|
|
613
638
|
allow_null=True,
|
|
614
639
|
help_text="The charge amount currency",
|
|
615
640
|
)
|
|
641
|
+
id = serializers.CharField(
|
|
642
|
+
required=False,
|
|
643
|
+
allow_blank=True,
|
|
644
|
+
allow_null=True,
|
|
645
|
+
help_text="A surcharge id",
|
|
646
|
+
)
|
|
616
647
|
|
|
617
648
|
|
|
618
649
|
@serializers.allow_model_id(
|
|
@@ -660,6 +691,7 @@ class RateRequest(validators.OptionDefaultSerializer):
|
|
|
660
691
|
{
|
|
661
692
|
"currency": "USD",
|
|
662
693
|
"insurance": 100.00,
|
|
694
|
+
"insured_by": "carrier",
|
|
663
695
|
"cash_on_delivery": 30.00,
|
|
664
696
|
"dangerous_good": true,
|
|
665
697
|
"declared_value": 150.00,
|
|
@@ -701,6 +733,18 @@ class RateRequest(validators.OptionDefaultSerializer):
|
|
|
701
733
|
allow_null=True,
|
|
702
734
|
help_text="The shipment reference",
|
|
703
735
|
)
|
|
736
|
+
payment = Payment(
|
|
737
|
+
required=False,
|
|
738
|
+
allow_null=True,
|
|
739
|
+
help_text="The payment details",
|
|
740
|
+
)
|
|
741
|
+
customs = CustomsData(
|
|
742
|
+
required=False,
|
|
743
|
+
allow_null=True,
|
|
744
|
+
help_text="""The customs details.<br/>
|
|
745
|
+
**Note that this is required for international shipments.**
|
|
746
|
+
""",
|
|
747
|
+
)
|
|
704
748
|
carrier_ids = serializers.StringListField(
|
|
705
749
|
required=False,
|
|
706
750
|
allow_null=True,
|
|
@@ -1042,11 +1086,24 @@ class TrackingEvent(serializers.Serializer):
|
|
|
1042
1086
|
date = serializers.CharField(
|
|
1043
1087
|
required=False, help_text="The tracking event's date. Format: `YYYY-MM-DD`"
|
|
1044
1088
|
)
|
|
1045
|
-
|
|
1046
|
-
required=False,
|
|
1089
|
+
time = serializers.CharField(
|
|
1090
|
+
required=False,
|
|
1091
|
+
allow_blank=True,
|
|
1092
|
+
allow_null=True,
|
|
1093
|
+
help_text="The tracking event's time. Format: `HH:MM AM/PM`",
|
|
1047
1094
|
)
|
|
1048
|
-
|
|
1049
|
-
required=False,
|
|
1095
|
+
timestamp = serializers.CharField(
|
|
1096
|
+
required=False,
|
|
1097
|
+
allow_blank=True,
|
|
1098
|
+
allow_null=True,
|
|
1099
|
+
help_text="The tracking event's timestamp. Format: `YYYY-MM-DDTHH:MM:SS.sssZ` (ISO 8601)",
|
|
1100
|
+
)
|
|
1101
|
+
status = serializers.ChoiceField(
|
|
1102
|
+
required=False,
|
|
1103
|
+
allow_blank=True,
|
|
1104
|
+
allow_null=True,
|
|
1105
|
+
choices=TRACKER_STATUS,
|
|
1106
|
+
help_text="The normalized status of this specific event",
|
|
1050
1107
|
)
|
|
1051
1108
|
code = serializers.CharField(
|
|
1052
1109
|
required=False,
|
|
@@ -1054,11 +1111,18 @@ class TrackingEvent(serializers.Serializer):
|
|
|
1054
1111
|
allow_null=True,
|
|
1055
1112
|
help_text="The tracking event's code",
|
|
1056
1113
|
)
|
|
1057
|
-
|
|
1114
|
+
reason = serializers.ChoiceField(
|
|
1058
1115
|
required=False,
|
|
1059
1116
|
allow_blank=True,
|
|
1060
1117
|
allow_null=True,
|
|
1061
|
-
|
|
1118
|
+
choices=TRACKING_INCIDENT_REASONS,
|
|
1119
|
+
help_text="The normalized incident reason (for exception events only)",
|
|
1120
|
+
)
|
|
1121
|
+
description = serializers.CharField(
|
|
1122
|
+
required=False, help_text="The tracking event's description"
|
|
1123
|
+
)
|
|
1124
|
+
location = serializers.CharField(
|
|
1125
|
+
required=False, help_text="The tracking event's location"
|
|
1062
1126
|
)
|
|
1063
1127
|
latitude = serializers.FloatField(
|
|
1064
1128
|
required=False,
|
|
@@ -1153,6 +1217,62 @@ class TrackingStatus(TrackerDetails):
|
|
|
1153
1217
|
)
|
|
1154
1218
|
|
|
1155
1219
|
|
|
1220
|
+
class ShippingDocument(serializers.Serializer):
|
|
1221
|
+
"""Serializer for shipping document download response."""
|
|
1222
|
+
|
|
1223
|
+
category = serializers.CharField(
|
|
1224
|
+
required=True,
|
|
1225
|
+
help_text="The document category (e.g., 'label', 'invoice', 'manifest')",
|
|
1226
|
+
)
|
|
1227
|
+
format = serializers.CharField(
|
|
1228
|
+
required=True,
|
|
1229
|
+
help_text="The document format (e.g., 'PDF', 'ZPL')",
|
|
1230
|
+
)
|
|
1231
|
+
url = serializers.CharField(
|
|
1232
|
+
required=False,
|
|
1233
|
+
allow_blank=True,
|
|
1234
|
+
allow_null=True,
|
|
1235
|
+
help_text="The document URL",
|
|
1236
|
+
)
|
|
1237
|
+
base64 = serializers.CharField(
|
|
1238
|
+
required=False,
|
|
1239
|
+
allow_blank=True,
|
|
1240
|
+
allow_null=True,
|
|
1241
|
+
help_text="The document content encoded in base64",
|
|
1242
|
+
)
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
class ShippingDocument(serializers.Serializer):
|
|
1246
|
+
"""Serializer for shipping document download response."""
|
|
1247
|
+
|
|
1248
|
+
category = serializers.CharField(
|
|
1249
|
+
required=True,
|
|
1250
|
+
help_text="The document category (e.g., 'label', 'invoice', 'manifest')",
|
|
1251
|
+
)
|
|
1252
|
+
format = serializers.CharField(
|
|
1253
|
+
required=True,
|
|
1254
|
+
help_text="The document format (e.g., 'PDF', 'ZPL')",
|
|
1255
|
+
)
|
|
1256
|
+
print_format = serializers.CharField(
|
|
1257
|
+
required=False,
|
|
1258
|
+
allow_blank=True,
|
|
1259
|
+
allow_null=True,
|
|
1260
|
+
help_text="The document print format (e.g., 'A4', '6x4', '8.5x11')",
|
|
1261
|
+
)
|
|
1262
|
+
url = serializers.CharField(
|
|
1263
|
+
required=False,
|
|
1264
|
+
allow_blank=True,
|
|
1265
|
+
allow_null=True,
|
|
1266
|
+
help_text="The document URL",
|
|
1267
|
+
)
|
|
1268
|
+
base64 = serializers.CharField(
|
|
1269
|
+
required=False,
|
|
1270
|
+
allow_blank=True,
|
|
1271
|
+
allow_null=True,
|
|
1272
|
+
help_text="The document content encoded in base64",
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1275
|
+
|
|
1156
1276
|
class Documents(serializers.Serializer):
|
|
1157
1277
|
label = serializers.CharField(
|
|
1158
1278
|
required=False,
|
|
@@ -1585,6 +1705,7 @@ class ShipmentContent(serializers.Serializer):
|
|
|
1585
1705
|
)
|
|
1586
1706
|
|
|
1587
1707
|
|
|
1708
|
+
@validators.shipment_documents_accessor
|
|
1588
1709
|
class Shipment(serializers.EntitySerializer, ShipmentContent, ShipmentDetails):
|
|
1589
1710
|
docs = None
|
|
1590
1711
|
label_url = serializers.URLField(
|
|
@@ -1599,6 +1720,12 @@ class Shipment(serializers.EntitySerializer, ShipmentContent, ShipmentDetails):
|
|
|
1599
1720
|
allow_null=True,
|
|
1600
1721
|
help_text="The shipment invoice URL",
|
|
1601
1722
|
)
|
|
1723
|
+
shipping_documents = ShippingDocument(
|
|
1724
|
+
required=False,
|
|
1725
|
+
many=True,
|
|
1726
|
+
default=[],
|
|
1727
|
+
help_text="The list of shipping documents",
|
|
1728
|
+
)
|
|
1602
1729
|
|
|
1603
1730
|
|
|
1604
1731
|
class ShipmentCancelRequest(serializers.Serializer):
|
|
@@ -1750,7 +1877,10 @@ class OperationConfirmation(Operation):
|
|
|
1750
1877
|
required=True, help_text="The operation carrier"
|
|
1751
1878
|
)
|
|
1752
1879
|
carrier_id = serializers.CharField(
|
|
1753
|
-
required=
|
|
1880
|
+
required=False,
|
|
1881
|
+
allow_blank=True,
|
|
1882
|
+
allow_null=True,
|
|
1883
|
+
help_text="The targeted carrier's name (unique identifier)",
|
|
1754
1884
|
)
|
|
1755
1885
|
|
|
1756
1886
|
|
|
@@ -1887,6 +2017,49 @@ class DocumentUploadRecord(serializers.EntitySerializer):
|
|
|
1887
2017
|
)
|
|
1888
2018
|
|
|
1889
2019
|
|
|
2020
|
+
class OAuthAuthorizeData(serializers.Serializer):
|
|
2021
|
+
"""OAuth authorization request data serializer."""
|
|
2022
|
+
|
|
2023
|
+
state = serializers.CharField(
|
|
2024
|
+
required=True,
|
|
2025
|
+
help_text="A unique state value for CSRF protection.",
|
|
2026
|
+
)
|
|
2027
|
+
redirect_uri = serializers.CharField(
|
|
2028
|
+
required=False,
|
|
2029
|
+
allow_null=True,
|
|
2030
|
+
help_text="The URI to redirect to after authorization.",
|
|
2031
|
+
)
|
|
2032
|
+
options = serializers.PlainDictField(
|
|
2033
|
+
required=False,
|
|
2034
|
+
default={},
|
|
2035
|
+
help_text="Additional carrier-specific options.",
|
|
2036
|
+
)
|
|
2037
|
+
|
|
2038
|
+
|
|
2039
|
+
class OAuthCallbackData(serializers.Serializer):
|
|
2040
|
+
"""OAuth callback request data serializer."""
|
|
2041
|
+
|
|
2042
|
+
url = serializers.CharField(
|
|
2043
|
+
required=True,
|
|
2044
|
+
help_text="The full callback URL.",
|
|
2045
|
+
)
|
|
2046
|
+
query = serializers.PlainDictField(
|
|
2047
|
+
required=False,
|
|
2048
|
+
default={},
|
|
2049
|
+
help_text="The query parameters from the callback URL.",
|
|
2050
|
+
)
|
|
2051
|
+
body = serializers.PlainDictField(
|
|
2052
|
+
required=False,
|
|
2053
|
+
default={},
|
|
2054
|
+
help_text="The request body if any.",
|
|
2055
|
+
)
|
|
2056
|
+
headers = serializers.PlainDictField(
|
|
2057
|
+
required=False,
|
|
2058
|
+
default={},
|
|
2059
|
+
help_text="The request headers.",
|
|
2060
|
+
)
|
|
2061
|
+
|
|
2062
|
+
|
|
1890
2063
|
class ErrorMessages(serializers.Serializer):
|
|
1891
2064
|
messages = Message(
|
|
1892
2065
|
many=True,
|
karrio/server/core/signals.py
CHANGED
|
@@ -1,57 +1,51 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
from django.conf import settings
|
|
3
2
|
from django.dispatch import receiver
|
|
4
3
|
from constance import config
|
|
5
4
|
from constance.signals import config_updated
|
|
6
5
|
from django.core.signals import request_started
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
from karrio.server.core.logging import logger
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def register_signals():
|
|
12
11
|
config_updated.connect(constance_updated)
|
|
13
|
-
# Defer config initialization until after Django is fully loaded
|
|
14
12
|
request_started.connect(initialize_settings)
|
|
15
13
|
|
|
16
|
-
logger.info("karrio.core
|
|
14
|
+
logger.info("Signal registration complete", module="karrio.core")
|
|
17
15
|
|
|
18
16
|
|
|
19
|
-
def initialize_settings(
|
|
20
|
-
|
|
21
|
-
if
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
def initialize_settings(_sender=None, **_kwargs):
|
|
18
|
+
"""Initialize Django settings from constance on first request."""
|
|
19
|
+
if getattr(initialize_settings, "has_run", False):
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
update_settings(config)
|
|
24
|
+
initialize_settings.has_run = True
|
|
25
|
+
except Exception as e:
|
|
26
|
+
logger.error("Failed to initialize settings", error=str(e))
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
@receiver(config_updated)
|
|
30
|
-
def constance_updated(sender,
|
|
31
|
-
logger.info(f"Updated config {key} to {new_value}")
|
|
30
|
+
def constance_updated(sender, **_kwargs):
|
|
32
31
|
update_settings(sender)
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
def update_settings(current):
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
]
|
|
35
|
+
"""Sync constance values to Django settings."""
|
|
36
|
+
keys = [k for k in settings.CONSTANCE_CONFIG.keys() if hasattr(settings, k)]
|
|
39
37
|
|
|
40
|
-
for key in
|
|
38
|
+
for key in keys:
|
|
41
39
|
try:
|
|
42
40
|
setattr(settings, key, getattr(current, key))
|
|
43
|
-
except Exception
|
|
44
|
-
|
|
41
|
+
except Exception:
|
|
42
|
+
pass # Ignore errors during test/initialization when constance table may not exist
|
|
45
43
|
|
|
46
|
-
#
|
|
44
|
+
# Update EMAIL_ENABLED based on email config
|
|
47
45
|
try:
|
|
48
46
|
settings.EMAIL_ENABLED = all(
|
|
49
|
-
cfg
|
|
50
|
-
for cfg in [
|
|
51
|
-
current.EMAIL_HOST,
|
|
52
|
-
current.EMAIL_HOST_USER,
|
|
53
|
-
]
|
|
47
|
+
cfg not in (None, "")
|
|
48
|
+
for cfg in [current.EMAIL_HOST, current.EMAIL_HOST_USER]
|
|
54
49
|
)
|
|
55
|
-
except Exception
|
|
56
|
-
logger.error(f"Failed to set EMAIL_ENABLED: {e}")
|
|
50
|
+
except Exception:
|
|
57
51
|
settings.EMAIL_ENABLED = False
|