cc-py-commons 0.5.13__tar.gz → 0.5.35__tar.gz

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 (154) hide show
  1. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/PKG-INFO +1 -1
  2. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/bids/bid_schema.py +13 -13
  3. cc_py_commons-0.5.35/cc_py_commons/carriers/account_carrier_map.py +12 -0
  4. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/account_carrier_map_schema.py +6 -7
  5. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/carrier_schema.py +2 -2
  6. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/config/env.py +5 -0
  7. cc_py_commons-0.5.35/cc_py_commons/config/web_socket_action.py +3 -0
  8. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/geolocate.py +191 -154
  9. cc_py_commons-0.5.35/cc_py_commons/loads/equipment_schema.py +16 -0
  10. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/load.py +1 -0
  11. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/load_schema.py +1 -0
  12. cc_py_commons-0.5.35/cc_py_commons/services/booking_agent/get_quote_by_load_id.py +19 -0
  13. cc_py_commons-0.5.35/cc_py_commons/services/booking_agent/list_bids.py +33 -0
  14. cc_py_commons-0.5.35/cc_py_commons/services/booking_agent/list_quotes.py +27 -0
  15. cc_py_commons-0.5.35/cc_py_commons/services/c4/get_account_settings.py +27 -0
  16. cc_py_commons-0.5.35/cc_py_commons/services/web_socket/send_notification.py +38 -0
  17. cc_py_commons-0.5.35/cc_py_commons/sqs/booking_assistant_cancel_quote_notification.py +31 -0
  18. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sqs/booking_assistant_flow_event.py +3 -2
  19. cc_py_commons-0.5.35/cc_py_commons/sqs/sqs_service.py +86 -0
  20. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/tests/test_map_transaction.py +1 -0
  21. cc_py_commons-0.5.35/cc_py_commons/tests/test_web_socket_send_notification.py +39 -0
  22. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/map_transaction.py +1 -0
  23. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/transaction.py +1 -0
  24. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/transaction_schema.py +1 -0
  25. cc_py_commons-0.5.35/cc_py_commons/utils/__init__.py +0 -0
  26. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/bulk_lane_price_result_utils.py +3 -0
  27. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons.egg-info/PKG-INFO +1 -1
  28. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons.egg-info/SOURCES.txt +8 -1
  29. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/setup.py +1 -1
  30. cc_py_commons-0.5.13/cc_py_commons/carriers/account_carrier_map.py +0 -12
  31. cc_py_commons-0.5.13/cc_py_commons/loads/equipment_schema.py +0 -14
  32. cc_py_commons-0.5.13/cc_py_commons/services/c4/get_account_settings.py +0 -21
  33. cc_py_commons-0.5.13/cc_py_commons/services/mercury/get_lane_price_for_bulk.py +0 -23
  34. cc_py_commons-0.5.13/cc_py_commons/sqs/sqs_service.py +0 -25
  35. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/README.md +0 -0
  36. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/__init__.py +0 -0
  37. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/bids/__init__.py +0 -0
  38. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/bids/bid.py +0 -0
  39. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/bids/bid_history.py +0 -0
  40. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/bids/bid_history_schema.py +0 -0
  41. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/__init__.py +0 -0
  42. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/carrier.py +0 -0
  43. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/contact.py +0 -0
  44. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/carriers/contact_schema.py +0 -0
  45. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/config/__init__.py +0 -0
  46. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/config/c4_account_settings.py +0 -0
  47. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/config/mcleod_load_fields.py +0 -0
  48. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/config/pc_miler_config.py +0 -0
  49. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/db/__init__.py +0 -0
  50. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/db/connection.py +0 -0
  51. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/lanes/__init__.py +0 -0
  52. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/lanes/carrier_lane.py +0 -0
  53. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/lanes/carrier_lane_schema.py +0 -0
  54. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/lanes/lane.py +0 -0
  55. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/lanes/lane_schema.py +0 -0
  56. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/__init__.py +0 -0
  57. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/equipment.py +0 -0
  58. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/equipment_types.py +0 -0
  59. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/freighthub_contact.py +0 -0
  60. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/freighthub_contact_schema.py +0 -0
  61. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/load_status.py +0 -0
  62. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/location.py +0 -0
  63. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/location_schema.py +0 -0
  64. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/loads/map_load_response.py +0 -0
  65. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/quotes/__init__.py +0 -0
  66. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/quotes/quote.py +0 -0
  67. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/quotes/quote_schema.py +0 -0
  68. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/quotes/quote_status.py +0 -0
  69. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/rfp/__init__.py +0 -0
  70. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/rfp/create_table.py +0 -0
  71. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/rfp/rfp_status.py +0 -0
  72. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/rfp/truck_lane_search_mapper.py +0 -0
  73. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/schemas/__init__.py +0 -0
  74. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/schemas/camel_case_schema.py +0 -0
  75. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/__init__.py +0 -0
  76. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/analytics/__init__.py +0 -0
  77. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/analytics/analytics_event.py +0 -0
  78. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/__init__.py +0 -0
  79. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/accept_bid.py +0 -0
  80. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/cancel_bid.py +0 -0
  81. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/constants.py +0 -0
  82. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/counter_bid.py +0 -0
  83. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/decline_bid.py +0 -0
  84. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/get_bid_by_amazon_order_id.py +0 -0
  85. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/get_quote.py +0 -0
  86. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/booking_agent/update_bid.py +0 -0
  87. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/__init__.py +0 -0
  88. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/amazon_session_token.py +0 -0
  89. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/constants.py +0 -0
  90. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/get_account.py +0 -0
  91. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/get_integration.py +0 -0
  92. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/get_users_for_account.py +0 -0
  93. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/c4/refresh_amazon_token.py +0 -0
  94. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/carrier_hub/__init__.py +0 -0
  95. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/carrier_hub/get_carrier.py +0 -0
  96. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/carrier_hub/list_carriers.py +0 -0
  97. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/carrier_hub/list_contacts.py +0 -0
  98. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/carrier_hub/update_contact.py +0 -0
  99. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/freight_hub/__init__.py +0 -0
  100. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/freight_hub/post_freight_hub_load.py +0 -0
  101. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/__init__.py +0 -0
  102. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/create_account_folder.py +0 -0
  103. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/db/__init__.py +0 -0
  104. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/db/delete_table.py +0 -0
  105. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/get_client.py +0 -0
  106. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/get_green_screens_lane_price.py +0 -0
  107. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/get_import_stat.py +0 -0
  108. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/get_lane_price.py +0 -0
  109. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/list_clients.py +0 -0
  110. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/post_import_stat.py +0 -0
  111. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/mercury/update_import_stat.py +0 -0
  112. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/slack/__init__.py +0 -0
  113. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/services/slack/send_slack_message.py +0 -0
  114. {cc_py_commons-0.5.13/cc_py_commons/sns → cc_py_commons-0.5.35/cc_py_commons/services/web_socket}/__init__.py +0 -0
  115. {cc_py_commons-0.5.13/cc_py_commons/sns/booking_agent → cc_py_commons-0.5.35/cc_py_commons/sns}/__init__.py +0 -0
  116. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/book_load_notification.py +0 -0
  117. {cc_py_commons-0.5.13/cc_py_commons/sqs → cc_py_commons-0.5.35/cc_py_commons/sns/booking_agent}/__init__.py +0 -0
  118. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/booking_agent/bid_counter_failure_notification.py +0 -0
  119. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/booking_agent/constants.py +0 -0
  120. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/booking_agent/send_sns_notification.py +0 -0
  121. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/booking_assistant_flow_notification.py +0 -0
  122. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/import_movements_notification.py +0 -0
  123. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/parsed_carrier_notification.py +0 -0
  124. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/sns/sns_service.py +0 -0
  125. {cc_py_commons-0.5.13/cc_py_commons/tests → cc_py_commons-0.5.35/cc_py_commons/sqs}/__init__.py +0 -0
  126. {cc_py_commons-0.5.13/cc_py_commons/transactions → cc_py_commons-0.5.35/cc_py_commons/tests}/__init__.py +0 -0
  127. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/tests/test_case_conversion.py +0 -0
  128. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/tests/test_lambda_utils.py +0 -0
  129. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/tests/test_temperature_utils.py +0 -0
  130. {cc_py_commons-0.5.13/cc_py_commons/utils → cc_py_commons-0.5.35/cc_py_commons/transactions}/__init__.py +0 -0
  131. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/equipment_clas_schema.py +0 -0
  132. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/equipment_class.py +0 -0
  133. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/equipment_mapping.py +0 -0
  134. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/transactions/equipment_mapping_schema.py +0 -0
  135. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/case_conversion.py +0 -0
  136. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/datadog_logger.py +0 -0
  137. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/datetimes.py +0 -0
  138. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/dimension_utils.py +0 -0
  139. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/geocode_location.py +0 -0
  140. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/get_delivery_date.py +0 -0
  141. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/json_logger.py +0 -0
  142. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/lambda_utils.py +0 -0
  143. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/local_file.py +0 -0
  144. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/logger.py +0 -0
  145. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/logger_v2.py +0 -0
  146. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/pc_miler_utils.py +0 -0
  147. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/price_utils.py +0 -0
  148. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/redis.py +0 -0
  149. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/s3_file.py +0 -0
  150. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons/utils/temperature_utils.py +0 -0
  151. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons.egg-info/dependency_links.txt +0 -0
  152. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons.egg-info/requires.txt +0 -0
  153. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/cc_py_commons.egg-info/top_level.txt +0 -0
  154. {cc_py_commons-0.5.13 → cc_py_commons-0.5.35}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cc_py_commons
3
- Version: 0.5.13
3
+ Version: 0.5.35
4
4
  Summary: Common code for microservices
5
5
  Home-page: https://github.com/Cargo-Chief/py-commons
6
6
  Author: CargoChief
@@ -10,26 +10,26 @@ class BidSchema(CamelCaseSchema):
10
10
  id = fields.UUID()
11
11
  quote_id = fields.UUID()
12
12
  carrier_id = fields.UUID()
13
- receipt_id = fields.String(allow_none=True)
13
+ receipt_id = fields.String(allow_none=True, missing=None)
14
14
  amount = fields.Integer()
15
- estimated_days = fields.Integer()
16
- notes = fields.String(allow_none=True)
15
+ estimated_days = fields.Integer(allow_none=True, missing=None)
16
+ notes = fields.String(allow_none=True, missing=None)
17
17
  match_score = fields.Float(allow_none=True)
18
18
  status_id = fields.UUID()
19
19
  pickup_date = fields.Date()
20
20
  delivery_date = fields.Date()
21
- decline_reason = fields.String(allow_none=True)
21
+ decline_reason = fields.String(allow_none=True, missing=None)
22
22
  bid_histories = fields.List(fields.Nested(BidHistorySchema), allow_none=True)
23
23
  origin_deadhead = fields.Float(allow_none=True)
24
- porus_truck_id = fields.UUID(allow_none=True)
25
- truck_lane_id = fields.UUID(allow_none=True)
26
- distance = fields.Float(allow_none=True)
27
- origin_city = fields.String()
28
- origin_state = fields.String()
29
- origin_zip = fields.String(allow_none=True)
30
- dest_city = fields.String()
31
- dest_state = fields.String()
32
- dest_zip = fields.String(allow_none=True)
24
+ porus_truck_id = fields.UUID(allow_none=True, missing=None)
25
+ truck_lane_id = fields.UUID(allow_none=True, missing=None)
26
+ distance = fields.Float(allow_none=True, missing=None)
27
+ origin_city = fields.String(missing=None)
28
+ origin_state = fields.String(missing=None)
29
+ origin_zip = fields.String(allow_none=True, missing=None)
30
+ dest_city = fields.String(missing=None)
31
+ dest_state = fields.String(missing=None)
32
+ dest_zip = fields.String(allow_none=True, missing=None)
33
33
  invite_emailed_at = fields.DateTime(allow_none=True)
34
34
 
35
35
  @post_load
@@ -0,0 +1,12 @@
1
+ import datetime
2
+ import uuid
3
+ from dataclasses import dataclass, field
4
+
5
+ @dataclass
6
+ class AccountCarrierMap:
7
+ account_id: int
8
+ carrier_id: uuid.UUID
9
+ status: str
10
+ customer_code: str = field(default=None)
11
+ warmup_email_sent_at: datetime.date = field(default=None)
12
+ no_dispatch: bool = field(default=False)
@@ -1,18 +1,17 @@
1
1
  from marshmallow import fields, EXCLUDE, post_load
2
2
  from cc_py_commons.schemas.camel_case_schema import CamelCaseSchema
3
- from cc_py_commons.carriers.contact_schema import ContactSchema
4
3
  from cc_py_commons.carriers.account_carrier_map import AccountCarrierMap
5
4
  class AccountCarrierMapSchema(CamelCaseSchema):
6
5
  class Meta:
7
6
  unknown = EXCLUDE
8
7
 
9
8
  account_id = fields.Integer()
10
- customer_code = fields.String(allow_none=True)
11
9
  carrier_id = fields.UUID()
12
- status = fields.String(allow_none=True)
13
- warmup_email_sent_at = fields.Date(allow_none=True)
14
- no_dispatch = fields.Boolean(allow_none=True)
15
-
10
+ status = fields.String()
11
+ customer_code = fields.String(allow_none=True, missing=None)
12
+ warmup_email_sent_at = fields.Date(allow_none=True, missing=None)
13
+ no_dispatch = fields.Boolean(allow_none=True, missing=None)
14
+
16
15
  @post_load
17
- def make_bid(self, data, **kwargs):
16
+ def make_account_carrier_map(self, data, **kwargs):
18
17
  return AccountCarrierMap(**data)
@@ -63,7 +63,7 @@ class CarrierSchema(CamelCaseSchema):
63
63
  vetting_overall_display = fields.String(allow_none=True)
64
64
  last_vetting_time = fields.Date(allow_none=True)
65
65
  vetting_remarks = fields.String(allow_none=True)
66
-
66
+
67
67
  @post_load
68
- def make_bid(self, data, **kwargs):
68
+ def make_carrier(self, data, **kwargs):
69
69
  return Carrier(**data)
@@ -33,6 +33,8 @@ class Config(object):
33
33
  BOOKING_ASSISTANT_SNS_TOPIC_ARN = 'arn:aws:sns:us-west-1:791608169866:booking-assistant-flow-test'
34
34
  BOOKING_ASSISTANT_SNS_SUBJECT = 'booking-assistant-flow'
35
35
  BOOKING_ASSISTANT_SNS_CLASS_NAME = 'com.cargochief.sdk.bookingagent.sns.BookingAssistantFlowNotification'
36
+ BOOKING_ASSISTANT_QUOTE_CANCEL_SUBJECT = 'quote-cancelled'
37
+ BOOKING_ASSISTANT_QUOTE_CANCEL_CLASS_NAME = 'com.cargochief.sdk.bookingagent.sns.QuoteCancelNotification'
36
38
  IMPORT_MOVEMENTS_SNS_TOPIC_ARN = 'arn:aws:sns:us-west-1:791608169866:mcleod-import-movements-test'
37
39
  IMPORT_MOVEMENTS_SNS_SUBJECT = 'import-movements'
38
40
  IMPORT_MOVEMENTS_SNS_CLASS_NAME = 'com.cargochief.sdk.c4.sns.mcleod.ImportMovementsNotification'
@@ -58,6 +60,8 @@ class Config(object):
58
60
  HERMES_SNS_ARN = 'arn:aws:sns:us-west-1:791608169866:hermes-sns-test'
59
61
  C4_ACCOUNTS_BUCKET = 'c4-accounts-test'
60
62
  ANALYTICS_SNS_ARN = 'arn:aws:sns:us-west-1:791608169866:analytics-test'
63
+ WEB_SOCKET_SNS_TOPIC_ARN = 'arn:aws:sns:us-west-1:791608169866:websocket-dev'
64
+ WEB_SOCKET_AUTH_TOKEN = os.getenv('WEB_SOCKET_AUTH_TOKEN')
61
65
 
62
66
  # Database
63
67
  DB_USERNAME = os.getenv('DB_USERNAME')
@@ -117,6 +121,7 @@ class ProductionConfig(Config):
117
121
  HERMES_SNS_ARN = 'arn:aws:sns:us-west-1:791608169866:hermes-sns-prod'
118
122
  C4_ACCOUNTS_BUCKET = 'c4-accounts-prod'
119
123
  ANALYTICS_SNS_ARN ='arn:aws:sns:us-west-1:791608169866:analytics-prod'
124
+ WEB_SOCKET_SNS_TOPIC_ARN = 'arn:aws:sns:us-west-1:791608169866:websocket-prod'
120
125
 
121
126
  config_options = {
122
127
  'local': LocalConfig,
@@ -0,0 +1,3 @@
1
+ class WebSocketAction:
2
+ BROADCAST_TO_USER = 'broadcastActionToUser'
3
+ BROADCAST_TO_ACCOUNT = 'broadcastActionToAccount'
@@ -25,7 +25,7 @@ def get_location_with_country(city, state, zipcode, country, logger, reset_cache
25
25
  from_cache = True
26
26
  else:
27
27
  logger.debug(json.dumps({
28
- 'message': 'Found locatin in cache that was not a match',
28
+ 'message': 'Found location in cache that was not a match',
29
29
  'city': city,
30
30
  'state': state,
31
31
  'zipcode': zipcode,
@@ -50,6 +50,23 @@ def get_location_with_country(city, state, zipcode, country, logger, reset_cache
50
50
  from_cache = False
51
51
  data = __get_google_location(city=None, state=None, zipcode=postcode, country=None, logger=logger)
52
52
 
53
+ if data and not data.get('postcode'):
54
+ matching_location = __get_matching_location_from_cache(city, state, data.get('lat'), data.get('lng'), logger)
55
+ if matching_location:
56
+ data['postcode'] = matching_location.get('postcode')
57
+ else:
58
+ gmaps = googlemaps.Client(key=app_config.GOOGLE_API_KEY)
59
+ response = None
60
+ if zipcode:
61
+ response = gmaps.geocode(zipcode)
62
+ elif data.get('lat') and data.get('lng'):
63
+ response = gmaps.reverse_geocode((data.get('lat'), data.get('lng')))
64
+ post_code_data = __parse_response(response, city)
65
+ data['postcode'] = post_code_data['postcode'] if post_code_data else None
66
+
67
+ if data.get('postcode'):
68
+ __cache_location(city, state, zipcode, data, logger)
69
+
53
70
  if data:
54
71
  if not from_cache:
55
72
  __cache_location(city, state, zipcode, data, logger)
@@ -58,29 +75,34 @@ def get_location_with_country(city, state, zipcode, country, logger, reset_cache
58
75
 
59
76
  return data
60
77
 
61
- def get_distance(origin_latitude, origin_longitude, destination_latitude, destination_longitude, logger):
78
+ def get_distance(origin_latitude, origin_longitude, destination_latitude, destination_longitude, logger, with_duration=False):
62
79
  if not origin_latitude or not origin_longitude or \
63
80
  not destination_latitude or not destination_longitude:
64
81
  raise Exception("Calculating distance requires both origin and destination lat/lng")
65
82
 
66
83
  from_cache = False
67
- pc_miler_can_be_called = False
68
84
  distance_cache_key = __get_distance_cache_key(origin_latitude, origin_longitude, destination_latitude, destination_longitude)
69
- distance = __get_distance_from_cache(distance_cache_key, logger)
85
+ distance_data_dict = __get_distance_from_cache(distance_cache_key, logger)
70
86
 
71
- if distance:
87
+ if distance_data_dict:
72
88
  from_cache = True
73
89
 
74
- if not distance:
75
- distance = __get_google_distance(origin_latitude, origin_longitude, destination_latitude, destination_longitude, logger)
90
+ missing_duration_and_is_required = (with_duration and not distance_data_dict.get('duration'))
91
+ if not distance_data_dict or missing_duration_and_is_required:
92
+ distance_data_dict = __get_google_distance(origin_latitude, origin_longitude, destination_latitude, destination_longitude, logger)
76
93
 
77
- if distance:
78
- if not from_cache:
79
- __cache_distance(distance_cache_key, distance, logger)
94
+ if distance_data_dict:
95
+ if not from_cache or missing_duration_and_is_required:
96
+ __cache_distance(distance_cache_key, distance_data_dict, logger)
80
97
  else:
81
98
  logger.debug(f"geolocate.get_distance - Google returned no result for {distance_cache_key}")
82
99
 
83
- return distance
100
+ if distance_data_dict:
101
+ if with_duration:
102
+ return distance_data_dict
103
+ else:
104
+ return distance_data_dict.get('distance')
105
+ return None
84
106
 
85
107
  def get_timezone(lat,lng, logger):
86
108
  from_cache = False
@@ -107,7 +129,7 @@ def get_timezone(lat,lng, logger):
107
129
  return timezone
108
130
 
109
131
  def __get_google_distance(origin_latitude, origin_longitude, destination_latitude, destination_longitude, logger):
110
- distance = None
132
+ distance_data = None
111
133
  try:
112
134
  gmaps = googlemaps.Client(key=app_config.GOOGLE_API_KEY)
113
135
  origins = [f'{origin_latitude} {origin_longitude}']
@@ -120,14 +142,23 @@ def __get_google_distance(origin_latitude, origin_longitude, destination_latitud
120
142
  if response and response['rows'][0]['elements'][0].get('distance', None):
121
143
  distance_in_meters = response['rows'][0]['elements'][0]['distance']['value']
122
144
  distance = distance_in_meters / 1609
145
+ duration_seconds = response['rows'][0]['elements'][0]['duration']['value']
146
+ duration_minutes = duration_seconds / 60
147
+ distance_data = {
148
+ 'distance': distance,
149
+ 'duration': duration_minutes
150
+ }
123
151
  except Exception as e:
124
152
  logger.error("geolocate.__get_google_distance: Error while getting distance from google", e)
125
- distance = None
126
- return distance
153
+ distance_data = None
154
+ return distance_data
127
155
 
128
156
  def __get_google_location(city, state, zipcode, country, logger):
129
157
  try:
158
+ if (not city or not state) and not zipcode:
159
+ return None
130
160
  loc_str = ''
161
+ data = None
131
162
 
132
163
  if city:
133
164
  loc_str = city
@@ -150,123 +181,6 @@ def __get_google_location(city, state, zipcode, country, logger):
150
181
 
151
182
  loc_str = loc_str + country
152
183
 
153
- def __parse_response(response):
154
- if not response or len(response) == 0:
155
- return None
156
- '''
157
- Sample response of Google Maps API for address: Cincinati, OH, 45204
158
- {
159
- "address_components": [
160
- {
161
- "long_name": "45204",
162
- "short_name": "45204",
163
- "types": [
164
- "postal_code"
165
- ]
166
- },
167
- {
168
- "long_name": "Cincinnati",
169
- "short_name": "Cincinnati",
170
- "types": [
171
- "locality",
172
- "political"
173
- ]
174
- },
175
- {
176
- "long_name": "Hamilton County",
177
- "short_name": "Hamilton County",
178
- "types": [
179
- "administrative_area_level_2",
180
- "political"
181
- ]
182
- },
183
- {
184
- "long_name": "Ohio",
185
- "short_name": "OH",
186
- "types": [
187
- "administrative_area_level_1",
188
- "political"
189
- ]
190
- },
191
- {
192
- "long_name": "United States",
193
- "short_name": "US",
194
- "types": [
195
- "country",
196
- "political"
197
- ]
198
- }
199
- ],
200
- "formatted_address": "Cincinnati, OH 45204, USA",
201
- "geometry": {
202
- "bounds": {
203
- "northeast": {
204
- "lat": 39.1251898,
205
- "lng": -84.539683
206
- },
207
- "southwest": {
208
- "lat": 39.073286,
209
- "lng": -84.62870989999999
210
- }
211
- },
212
- "location": {
213
- "lat": 39.0930395,
214
- "lng": -84.56676650000001
215
- },
216
- "location_type": "APPROXIMATE",
217
- "viewport": {
218
- "northeast": {
219
- "lat": 39.1251898,
220
- "lng": -84.539683
221
- },
222
- "southwest": {
223
- "lat": 39.073286,
224
- "lng": -84.62870989999999
225
- }
226
- }
227
- },
228
- "place_id": "ChIJjYEP0hK2QYgRTJoYykoQeqw",
229
- "postcode_localities": [
230
- "Cincinnati",
231
- "Queen City Square"
232
- ],
233
- "types": [
234
- "postal_code"
235
- ]
236
- }
237
- '''
238
- components = response[0]['address_components']
239
- types = response[0]['types']
240
- location = response[0]['geometry']['location']
241
- postcode_localities = response[0].get('postcode_localities', None)
242
-
243
- data = { 'city': None, 'state': None, 'postcode': None }
244
- data['lat'] = location['lat']
245
- data['lng'] = location['lng']
246
-
247
- # when checking the response for a zipcode verify the city name is in the list of localities
248
- if 'postal_code' in types and postcode_localities and city:
249
- matches = [l for l in components if (
250
- 'postal_localities' in l.get('types') and
251
- (l.short_name.lower() == city.lower() or l.long_name.lower() == city.lower())
252
- )]
253
- city_present_in_postcode_localities = (city in postcode_localities)
254
-
255
- if not matches and not city_present_in_postcode_localities:
256
- return None
257
-
258
- for component in components:
259
- if 'locality' in component['types']:
260
- data['city'] = component['short_name']
261
- elif 'administrative_area_level_1' in component['types']:
262
- data['state'] = component['short_name']
263
- elif 'postcode' in component['types'] or 'postal_code' in component['types']:
264
- data['postcode'] = component['short_name']
265
- elif 'country' in component['types']:
266
- data['country'] = component['short_name']
267
-
268
- return data
269
-
270
184
  gmaps = googlemaps.Client(key=app_config.GOOGLE_API_KEY)
271
185
  response = gmaps.geocode(loc_str)
272
186
 
@@ -282,21 +196,7 @@ def __get_google_location(city, state, zipcode, country, logger):
282
196
  logger.warning(f"Cannot select a single address from multiple for {loc_str}.")
283
197
  return None
284
198
  response = [selected_address]
285
-
286
- data = __parse_response(response)
287
-
288
- # For some locals like neighborhood google does not return postal_code
289
- ## if zipcode is present run it only and verify against city
290
- if data and not data['postcode']:
291
- if zipcode:
292
- response = gmaps.geocode(zipcode)
293
- else:
294
- response = gmaps.reverse_geocode((data['lat'], data['lng']))
295
- post_code_data = __parse_response(response)
296
- data['postcode'] = post_code_data['postcode'] if post_code_data else None
297
- if data['postcode']:
298
- __cache_location(city, state, zipcode, data, logger)
299
- return data
199
+ return __parse_response(response, city)
300
200
  except googlemaps.exceptions.Timeout as e:
301
201
  msg = "geolocate.__get_google_location - Location lookup timed out {0}, {1}, {2}: {3}".format(city, state, zipcode, e)
302
202
 
@@ -314,6 +214,123 @@ def __get_google_location(city, state, zipcode, country, logger):
314
214
 
315
215
  return None
316
216
 
217
+ def __parse_response(response, city):
218
+ if not response or len(response) == 0:
219
+ return None
220
+ '''
221
+ Sample response of Google Maps API for address: Cincinati, OH, 45204
222
+ {
223
+ "address_components": [
224
+ {
225
+ "long_name": "45204",
226
+ "short_name": "45204",
227
+ "types": [
228
+ "postal_code"
229
+ ]
230
+ },
231
+ {
232
+ "long_name": "Cincinnati",
233
+ "short_name": "Cincinnati",
234
+ "types": [
235
+ "locality",
236
+ "political"
237
+ ]
238
+ },
239
+ {
240
+ "long_name": "Hamilton County",
241
+ "short_name": "Hamilton County",
242
+ "types": [
243
+ "administrative_area_level_2",
244
+ "political"
245
+ ]
246
+ },
247
+ {
248
+ "long_name": "Ohio",
249
+ "short_name": "OH",
250
+ "types": [
251
+ "administrative_area_level_1",
252
+ "political"
253
+ ]
254
+ },
255
+ {
256
+ "long_name": "United States",
257
+ "short_name": "US",
258
+ "types": [
259
+ "country",
260
+ "political"
261
+ ]
262
+ }
263
+ ],
264
+ "formatted_address": "Cincinnati, OH 45204, USA",
265
+ "geometry": {
266
+ "bounds": {
267
+ "northeast": {
268
+ "lat": 39.1251898,
269
+ "lng": -84.539683
270
+ },
271
+ "southwest": {
272
+ "lat": 39.073286,
273
+ "lng": -84.62870989999999
274
+ }
275
+ },
276
+ "location": {
277
+ "lat": 39.0930395,
278
+ "lng": -84.56676650000001
279
+ },
280
+ "location_type": "APPROXIMATE",
281
+ "viewport": {
282
+ "northeast": {
283
+ "lat": 39.1251898,
284
+ "lng": -84.539683
285
+ },
286
+ "southwest": {
287
+ "lat": 39.073286,
288
+ "lng": -84.62870989999999
289
+ }
290
+ }
291
+ },
292
+ "place_id": "ChIJjYEP0hK2QYgRTJoYykoQeqw",
293
+ "postcode_localities": [
294
+ "Cincinnati",
295
+ "Queen City Square"
296
+ ],
297
+ "types": [
298
+ "postal_code"
299
+ ]
300
+ }
301
+ '''
302
+ components = response[0]['address_components']
303
+ types = response[0]['types']
304
+ location = response[0]['geometry']['location']
305
+ postcode_localities = response[0].get('postcode_localities', None)
306
+
307
+ data = {'city': None, 'state': None, 'postcode': None}
308
+ data['lat'] = location['lat']
309
+ data['lng'] = location['lng']
310
+
311
+ # when checking the response for a zipcode verify the city name is in the list of localities
312
+ if 'postal_code' in types and postcode_localities and city:
313
+ matches = [l for l in components if (
314
+ 'postal_localities' in l.get('types') and
315
+ (l.short_name.lower() == city.lower() or l.long_name.lower() == city.lower())
316
+ )]
317
+ city_present_in_postcode_localities = (city in postcode_localities)
318
+
319
+ if not matches and not city_present_in_postcode_localities:
320
+ return None
321
+
322
+ for component in components:
323
+ if 'locality' in component['types']:
324
+ data['city'] = component['short_name']
325
+ elif 'administrative_area_level_1' in component['types']:
326
+ data['state'] = component['short_name']
327
+ elif 'postcode' in component['types'] or 'postal_code' in component['types']:
328
+ data['postcode'] = component['short_name']
329
+ elif 'country' in component['types']:
330
+ data['country'] = component['short_name']
331
+
332
+ return data
333
+
317
334
  def __cache_location(city, state, zipcode, location_data, logger):
318
335
  location_string = __get_location_string(city, state, zipcode)
319
336
  location_db_conn.set(location_string, str(location_data))
@@ -344,19 +361,22 @@ def __get_location_string(city, state, zipcode):
344
361
 
345
362
  return location_string.lower()
346
363
 
347
- def __cache_distance(distance_cache_key, distance, logger):
348
- distance_db_conn.set(distance_cache_key, distance)
364
+ def __cache_distance(distance_cache_key, distance_data, logger):
365
+ distance_db_conn.set(distance_cache_key, str(distance_data))
349
366
 
350
367
  def __get_distance_from_cache(distance_cache_key, logger):
351
- distance = None
368
+ distance_data = {}
352
369
  try:
353
- distance = distance_db_conn.get(distance_cache_key)
354
- if distance:
355
- distance = float(distance)
370
+ cached_data = distance_db_conn.get(distance_cache_key)
371
+ if cached_data:
372
+ if 'duration' in str(cached_data):
373
+ distance_data = literal_eval(cached_data.decode('utf-8'))
374
+ else:
375
+ distance_data['distance'] = float(cached_data)
356
376
  except Exception as e:
357
377
  logger.warn("geolocate.__get_distance_from_cache: Error while getting distance from cache", e)
358
- distance = None
359
- return distance
378
+ distance_data = {}
379
+ return distance_data
360
380
 
361
381
  def __get_distance_cache_key(origin_latitude, origin_longitude, destination_latitude, destination_longitude):
362
382
  return f'{__truncate(origin_latitude)},{__truncate(origin_longitude)}->{__truncate(destination_latitude)},{__truncate(destination_longitude)}'
@@ -404,3 +424,20 @@ def __select_address(city, state, address_list):
404
424
  and parsed_city_and_state['state'].strip().lower() == state.strip().lower():
405
425
  return parsed_city_and_state['address']
406
426
  return None
427
+
428
+ def __get_matching_location_from_cache(city, state, lat, lng, logger):
429
+ matching_location = None
430
+ try:
431
+ if city and state and lat and lng:
432
+ key = __get_location_string(city, state, None)
433
+ pattern = f'{key}*'
434
+ matching_keys = location_db_conn.keys(pattern)
435
+ if matching_keys:
436
+ for matching_key in matching_keys:
437
+ location = literal_eval(location_db_conn.get(matching_key).decode('utf-8'))
438
+ if location.get('lat') == lat and location.get('lng') == lng and location.get('postcode'):
439
+ matching_location = location
440
+ break
441
+ except Exception as e:
442
+ logger.warn("geolocate.__get_matching_location_from_cache: Failed: ", e)
443
+ return matching_location
@@ -0,0 +1,16 @@
1
+ from marshmallow import fields, EXCLUDE, post_load
2
+ from cc_py_commons.schemas.camel_case_schema import CamelCaseSchema
3
+ from cc_py_commons.loads.equipment import Equipment
4
+
5
+ class EquipmentSchema(CamelCaseSchema):
6
+ class Meta:
7
+ unknown = EXCLUDE
8
+
9
+ id = fields.UUID()
10
+ name = fields.String()
11
+ active = fields.Boolean(default=True) # Match the dataclass default
12
+
13
+ @post_load
14
+ def make_equipment(self, data, **kwargs):
15
+ return Equipment(**data)
16
+
@@ -81,3 +81,4 @@ class Load:
81
81
  customer_code: str = field(default=None)
82
82
  ltl: bool = field(default=False)
83
83
  request_id: uuid.UUID = field(default=None)
84
+ predicted_price: int = field(default=None)
@@ -76,6 +76,7 @@ class LoadSchema(CamelCaseSchema):
76
76
  ltl = fields.Boolean(allow_none=True, default=False, missing=False)
77
77
  special_instructions = fields.String(allow_none=True)
78
78
  request_id = fields.UUID(allow_none=True)
79
+ predicted_price: int = fields.Integer(allow_none=True)
79
80
 
80
81
  @post_load
81
82
  def make_load(self, data, **kwargs):
@@ -0,0 +1,19 @@
1
+ import requests
2
+
3
+ from cc_py_commons.utils import json_logger
4
+ from .constants import BOOKING_AGENT_TOKEN, BOOKING_AGENT_URL
5
+
6
+
7
+ def execute(load_id):
8
+ url = f"{BOOKING_AGENT_URL}/quote/by/loadId/{load_id}"
9
+ booking_agent_headers = {
10
+ "Authorization": f"Bearer {BOOKING_AGENT_TOKEN}",
11
+ "Content-Type": "application/json"
12
+ }
13
+ response = requests.get(url, headers=booking_agent_headers)
14
+ if response.status_code == 200:
15
+ return response.json()
16
+ else:
17
+ json_logger.warning(None, 'Failed to get quote from Booking Agent',
18
+ status_code=response.status_code, load_id=str(load_id))
19
+ return None
@@ -0,0 +1,33 @@
1
+ import os
2
+
3
+ import requests
4
+ from dateutil.parser import parse
5
+
6
+ from cc_py_commons.bids.bid_schema import BidSchema
7
+ from cc_py_commons.utils import json_logger
8
+
9
+ BOOKING_AGENT_URL = os.environ.get('BOOKING_AGENT_URL')
10
+ BOOKING_AGENT_TOKEN = os.environ.get("BOOKING_AGENT_TOKEN")
11
+
12
+
13
+ def execute(account_id, filters_dict):
14
+ url = f"{BOOKING_AGENT_URL}/bid/match"
15
+ token = f"Bearer {BOOKING_AGENT_TOKEN}"
16
+ headers = {
17
+ "Authorization": token
18
+ }
19
+ json_logger.debug(account_id, f"Requesting bids from booking-agent", url=url, filters=filters_dict)
20
+ response = requests.get(url, headers=headers, params=filters_dict)
21
+ bids = []
22
+ if response.status_code == 200:
23
+ bids = response.json()['content']
24
+ for bid in bids:
25
+ bid['pickupDate'] = parse(bid['pickupDate']).strftime('%Y-%m-%d')
26
+ bid['deliveryDate'] = parse(bid['deliveryDate']).strftime('%Y-%m-%d')
27
+ if bid.get('inviteEmailedAt'):
28
+ bid['inviteEmailedAt'] = parse(bid['inviteEmailedAt']).isoformat()
29
+ for bid_history in bid.get('bidHistories', []):
30
+ bid_history['pickupDate'] = parse(bid_history['pickupDate']).strftime('%Y-%m-%d')
31
+ bid_history['deliveryDate'] = parse(bid_history['deliveryDate']).strftime('%Y-%m-%d')
32
+ bids = BidSchema().load(bids, many=True)
33
+ return bids