Skip to content

Commit 8b7fbff

Browse files
authored
Merge pull request #66 from diging/develop
prepare release
2 parents 579333f + ee755c7 commit 8b7fbff

File tree

38 files changed

+1086
-104
lines changed

38 files changed

+1086
-104
lines changed

edrop/settings.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515

1616
logger = logging.getLogger(__name__)
1717
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
18-
logging.basicConfig(format="%(levelname)s: %(name)s: %(message)s", level=LOGLEVEL)
19-
18+
logging.basicConfig(format="%(asctime)s - %(levelname)s: %(name)s: %(funcName)s: %(message)s", level=LOGLEVEL)
2019

2120
# Build paths inside the project like this: BASE_DIR / 'subdir'.
2221
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -146,10 +145,31 @@
146145
REQUEST_TIMEZONE = os.environ.get('REQUEST_TIMEZONE', "MST")
147146

148147
# REDCap configurations
149-
REDCAP_INSTRUMENT_ID = "contact"
150-
REDCAP_FIELD_TO_BE_COMPLETE = "contact_complete"
148+
REDCAP_CITY = 'city'
149+
REDCAP_CONSENT_COMPLETE = 'consent_complete'
150+
REDCAP_CONTACT_COMPLETE = 'contact_complete'
151+
REDCAP_DATE_KIT_REQUEST = 'date_kit_request'
152+
REDCAP_DATE_KIT_SHIPPED = 'date_kit_shipped'
153+
REDCAP_FIELD_TO_BE_COMPLETE = 'contact_complete'
154+
REDCAP_FIRST_NAME = 'first_name'
155+
REDCAP_INSTRUMENT_ID = 'contact'
156+
REDCAP_KIT_ORDER_N = 'kit_order_n'
157+
REDCAP_KIT_STATUS = 'kit_status'
158+
REDCAP_KIT_STATUS_ORDER_VAL = 'ORD'
159+
REDCAP_KIT_STATUS_TRACK_VAL = 'TRN'
160+
REDCAP_KIT_TRACKING_COMPLETE = 'kit_tracking_complete'
161+
REDCAP_KIT_TRACKING_COMPLETE_VAL = '1'
162+
REDCAP_KIT_TRACKING_N = 'kit_tracking_n'
163+
REDCAP_KIT_TRACKING_RETURN_N = 'kit_tracking_return_n'
164+
REDCAP_LAST_NAME = 'last_name'
165+
REDCAP_RECORD_ID = 'record_id'
166+
REDCAP_STATE = 'state'
167+
REDCAP_STREET_1 = 'street_1'
168+
REDCAP_STREET_2 = 'street_2'
151169
REDCAP_TOKEN = os.environ.get('REDCAP_TOKEN')
170+
REDCAP_TUBESERIAL = 'tubeserial'
152171
REDCAP_URL = os.environ.get('REDCAP_URL')
172+
REDCAP_ZIP = 'zip'
153173

154174
#GBF configurations
155175
GBF_TOKEN = os.environ.get('GBF_TOKEN')
@@ -161,4 +181,4 @@
161181
GBF_ITEM_QUANTITY = 1.0
162182
GBF_SHIPPING_METHOD = os.environ.get('GBF_SHIPPING_METHOD', "FedEx Ground")
163183

164-
CRON_JOB_FREQUENCEY = "*/1" # Should run the GBG check job once a day
184+
CRON_JOB_FREQUENCY = "*/1" # Should run the GBG check job once a day

supervisord.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ stopwaitsecs = 600
1818
; send SIGKILL to its whole process group instead,
1919
; taking care of its children as well.
2020
killasgroup=true
21+
stopasgroup=true
2122

2223
; if rabbitmq is supervised, set its priority higher
2324
; so it starts first
@@ -40,6 +41,7 @@ stopwaitsecs = 600
4041
; send SIGKILL to its whole process group instead,
4142
; taking care of its children as well.
4243
killasgroup=true
44+
stopasgroup=true
4345

4446
; if rabbitmq is supervised, set its priority higher
4547
; so it starts first

track/admin.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,40 @@
11
from django.contrib import admin
22
from track.models import *
3+
from django.http import HttpResponseRedirect
4+
from django.urls import path
5+
import logging
6+
from track import orders
7+
8+
logger = logging.getLogger(__name__)
9+
310

411
# Register your models here.
512
class OrderAdmin(admin.ModelAdmin):
13+
change_list_template = "track/check_orders.html"
14+
615
list_display = ["record_id", "order_number", "tracking_nrs", "return_tracking_nrs", "tube_serials", "order_status", "ship_date"]
716

17+
class ConfirmationCheckLogAdmin(admin.ModelAdmin):
18+
list_display = ["id", "job_id", "start_time", "end_time", "is_complete"]
19+
fields = ("job_id", "apscheduler", "orders", "gbf", "redcap", "end_time", "is_complete")
20+
21+
def get_urls(self):
22+
urls = super().get_urls()
23+
my_urls = [
24+
path('check_order_status/', self.call_check_order_status),
25+
]
26+
return my_urls + urls
27+
28+
def call_check_order_status(self, request):
29+
logger.info("Initiatied check for tracking info")
30+
orders.check_orders_shipping_info()
31+
logger.info("Tracking info check completed.")
32+
return HttpResponseRedirect("../")
33+
34+
class OrderLogAdmin(admin.ModelAdmin):
35+
list_display = ["id", "order_number", "start_time", "end_time", "is_complete"]
36+
fields = ("order_number", "redcap", "orders", "gbf", "end_time", "is_complete")
837

9-
admin.site.register(Order, OrderAdmin)
38+
admin.site.register(Order, OrderAdmin)
39+
admin.site.register(OrderLog, OrderLogAdmin)
40+
admin.site.register(ConfirmationCheckLog, ConfirmationCheckLogAdmin)

track/api.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.conf import settings
55
from track.models import *
66
import track.orders as orders
7+
from track.exceptions import REDCapError
78

89
import logging
910
logger = logging.getLogger(__name__)
@@ -31,7 +32,11 @@ def initiate_order(request):
3132
return HttpResponse(status=HTTPStatus.OK)
3233

3334
# create a new order only if no order exists
34-
order = orders.place_order(record_id, request.POST.get('project_id'), request.POST.get('project_url'))
35+
try:
36+
order = orders.place_order(record_id, request.POST.get('project_id'), request.POST.get('project_url'))
37+
except REDCapError as e:
38+
return HttpResponse(status=HTTPStatus.INTERNAL_SERVER_ERROR)
39+
3540
if not order:
3641
logger.error("Endpoint was called with contact_complete = 2 but REDCap order actually does not have contact_complete = 2.")
3742
return HttpResponse(status=HTTPStatus.BAD_REQUEST)

track/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class REDCapError(Exception):
2+
"""
3+
Exception raised for issues with REDCap.
4+
"""
5+
6+
def __init__(self, message="There was an issue with connecting to REDCap."):
7+
self.message = message
8+
super().__init__(self.message)

track/gbf.py

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from django.conf import settings
22
import requests, json
33
from http import HTTPStatus
4-
import logging
4+
import logging, inspect
5+
6+
from track.models import *
7+
from track.log_manager import LogManager
58

69
logger = logging.getLogger(__name__)
10+
log_manager = LogManager()
11+
712

813
def create_order(order, adress_data):
914
"""
@@ -16,13 +21,20 @@ def create_order(order, adress_data):
1621
order.order_number = order_number
1722
order.save()
1823

24+
log_manager.start_order_log(order_number)
1925
# generate order json
2026
order_json = _generate_order_json(order, adress_data)
21-
logger.error("Sending to GBF:")
22-
logger.error(order_json)
27+
28+
# we cannot store PII (which the shipping address is,
29+
# so this is here just for testing purposes and should not be executed in production)
30+
#logger.error(order_json)
31+
32+
message = f"Placing order {order.order_number} with GBF."
33+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message, order_number)
34+
logger.info(message)
2335

2436
# make order with GBF
25-
return _place_order_with_GBF(order_json)
37+
return _place_order_with_GBF(order_json, order_number)
2638

2739
def _generate_order_number(order):
2840
"""
@@ -62,8 +74,7 @@ def _generate_order_json(order, address_data):
6274

6375
return json.dumps(order_json)
6476

65-
66-
def _place_order_with_GBF(order_json):
77+
def _place_order_with_GBF(order_json, order_number):
6778
"""
6879
Makes a POST request to the GBF endpoint /oap/api/order with the proper
6980
order Json.
@@ -74,27 +85,52 @@ def _place_order_with_GBF(order_json):
7485
"""
7586
# make post request to GBF
7687
# By default requests should be made as "test" via an environment variable.
77-
# Once we go live, the environemnt variable needs to be set to true explictly,
88+
# Once we go live, the environemnt variable needs to be set to true explictly.
7889
headers = {
7990
'Authorization': f'Bearer {settings.GBF_TOKEN}',
8091
'Content-Type': 'application/json'
8192
}
93+
8294
response = requests.post(f"{settings.GBF_URL}oap/api/order", data=order_json, headers=headers)
83-
logger.error("Response from GBF:")
84-
logger.error(response)
95+
96+
message = "Response from GBF:"
97+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message, order_number)
98+
logger.info(message)
99+
100+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, response, order_number)
101+
logger.info(response)
85102

86103
response_body = response.json()
104+
log_manager.append_to_gbf_log(LogManager.LEVEL_DEBUG, response_body, order_number)
105+
logger.debug(response_body)
87106
if response.status_code != HTTPStatus.OK:
88-
logger.error("Could not submit order to GBF.")
89-
logger.error(response)
90-
logger.error(response_body)
107+
message = f"Could not submit order {order_number} to GBF."
108+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message, order_number)
109+
logger.error(message)
110+
111+
message = response
112+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message, order_number)
113+
logger.error(message)
114+
115+
message = {response_body}
116+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message, order_number)
117+
logger.error(message)
91118
return False
92119

93120
if "success" not in response_body or response_body["success"] != True:
94-
logger.error("Could not submit order to GBF.")
95-
logger.error(response_body)
121+
message = f"Could not submit order {order_number} to GBF."
122+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message, order_number)
123+
logger.error(message)
124+
125+
message = response_body
126+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message, order_number)
127+
logger.error(message)
96128
return False
97129

130+
message = f'Order {order_number} has been successfully placed with GBF!'
131+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message, order_number)
132+
logger.info(message)
133+
log_manager.complete_log(order_number)
98134
return True
99135

100136
def get_order_confirmations(order_numbers):
@@ -126,48 +162,72 @@ def get_order_confirmations(order_numbers):
126162
}
127163
}
128164
"""
165+
message = f"Getting GBF Order Confirmations for the following order numbers: {order_numbers}"
166+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message)
167+
logger.info(message)
168+
129169
headers = {'Authorization': f'Bearer {settings.GBF_TOKEN}'}
130170
content = {'orderNumbers': order_numbers, 'format': 'json'}
171+
logger.debug('Sending to GBF:')
172+
logger.debug(content)
173+
log_manager.append_to_gbf_log(LogManager.LEVEL_DEBUG, content)
131174
try:
132175
response = requests.post(f"{settings.GBF_URL}oap/api/confirm2", data=content, headers=headers)
133176
response.raise_for_status() # Raises an exception for bad status codes
177+
134178
logger.debug(response.json())
135179
except requests.exceptions.HTTPError as err:
136-
logger.error(f"Could not get order confirmation from GBF.")
137-
logger.error(err)
180+
message = f"Could not get order confirmation from GBF for the following order numbers: {order_numbers}."
181+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
182+
logger.error(message)
183+
184+
message = err
185+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
186+
logger.error(message)
138187
return None
139188

140189
response_body = response.json()
141190
# if for some reason GBF does not return a success response
142191
if response_body['success'] != True:
143-
logger.error("GBF returned success is false.")
144-
logger.error(response_body)
192+
message = "GBF returned success is false."
193+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
194+
logger.error(message)
195+
196+
message = response_body
197+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
198+
logger.error(message)
145199

146200
if "dataArray" not in response_body or not response_body["dataArray"]:
147-
logger.info("No GBF confirmations available.")
201+
message = "No GBF confirmations available."
202+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message)
203+
logger.info(message)
148204
return None
149205

150206
# GBF sends one object in a list in 'dataArray', so we'll use the first one
151207
data_object = response_body["dataArray"][0]
152208
if 'format' not in data_object or data_object["format"] != "json":
153-
logger.error("GBF did not send json back.")
209+
message = "GBF did not send json back."
210+
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
211+
logger.error(message)
154212
return None
155213

156214
if 'data' not in data_object or not data_object["data"]:
157-
logger.info("No GBF confirmations available.")
215+
message = "No GBF confirmations available."
216+
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, message)
217+
logger.info(message)
158218
return None
159219

160220
confirmations = json.loads(data_object["data"])
161221
tracking_info = {}
162222
if "ShippingConfirmations" in confirmations:
163223
for shipping_confirmation in confirmations['ShippingConfirmations']:
164224
tracking_info[shipping_confirmation['OrderNumber']] = {
165-
'date_kit_shipped': shipping_confirmation['ShipDate'],
166-
'kit_tracking_n': shipping_confirmation['Tracking'],
225+
'date_kit_shipped': shipping_confirmation['ShipDate'] if 'ShipDate' in shipping_confirmation else None,
226+
'kit_tracking_n': shipping_confirmation['Tracking'] if 'Tracking' in shipping_confirmation else None,
167227
#filter for items with return tracking numbers and returns tracking numbers
168-
'return_tracking_n': [return_track for item in shipping_confirmation['Items'] if 'ReturnTracking' in item for return_track in item['ReturnTracking']],
228+
'return_tracking_n': [return_track if 'Items' in shipping_confirmation else None for item in shipping_confirmation['Items'] if 'ReturnTracking' in item for return_track in item['ReturnTracking']],
169229
#filter for items with return tracking numbers and returns tracking numbers
170-
'tube_serial_n': [tube_serial for item in shipping_confirmation['Items'] if 'TubeSerial' in item for tube_serial in item['TubeSerial']]
230+
'tube_serial_n': [tube_serial if 'Items' in shipping_confirmation else None for item in shipping_confirmation['Items'] if 'TubeSerial' in item for tube_serial in item['TubeSerial']]
171231
}
172232
return tracking_info
173233

0 commit comments

Comments
 (0)