diff --git a/cope2n-api/fwd_api/api/accuracy_view.py b/cope2n-api/fwd_api/api/accuracy_view.py index e482e58..abeb682 100644 --- a/cope2n-api/fwd_api/api/accuracy_view.py +++ b/cope2n-api/fwd_api/api/accuracy_view.py @@ -10,6 +10,9 @@ from django.db.models import Q from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes # from drf_spectacular.types import OpenApiString from ..models import SubscriptionRequest +from ..exception.exceptions import RequiredFieldException + +import json class AccuracyViewSet(viewsets.ViewSet): @@ -165,7 +168,7 @@ class AccuracyViewSet(viewsets.ViewSet): 'Client Request Time (ms)': request.client_request_time, 'Server Processing Time (ms)': request.preprocessing_time + request.ai_inference_time, 'Is Reviewed': request.is_reviewed, - 'Is Bad Quality': request.is_bad_image_quality, + # 'Is Bad Quality': request.is_bad_image_quality, 'created_at': request.created_at.isoformat() }) @@ -180,4 +183,90 @@ class AccuracyViewSet(viewsets.ViewSet): return JsonResponse(response) - return JsonResponse({'error': 'Invalid request method.'}, status=405) \ No newline at end of file + return JsonResponse({'error': 'Invalid request method.'}, status=405) + + +class RequestViewSet(viewsets.ViewSet): + lookup_field = "username" + + @extend_schema(request = { + 'multipart/form-data': { + 'type': 'object', + 'properties': { + 'reviewed_result': { + 'type': 'string', + }, + } + }, + }, responses=None, tags=['Request'] + ) + @action(detail=False, url_path=r"request/(?P[\w\-]+)", methods=["GET", "POST"]) + def get_subscription_request(self, request, request_id=None): + if request.method == 'GET': + base_query = Q(request_id=request_id) + + subscription_request = SubscriptionRequest.objects.filter(base_query).first() + + data = [] + + imeis = [] + purchase_date = [] + retailer = "" + try: + if subscription_request.reviewed_result is not None: + imeis = subscription_request.reviewed_result.get("imei_number", []) + purchase_date = subscription_request.reviewed_result.get("purchase_date", []) + retailer = subscription_request.reviewed_result.get("retailername", "") + elif subscription_request.feedback_result is not None : + imeis = subscription_request.feedback_result.get("imei_number", []) + purchase_date = subscription_request.feedback_result.get("purchase_date", []) + retailer = subscription_request.feedback_result.get("retailername", "") + elif subscription_request.predict_result is not None: + if subscription_request.predict_result.get("status", 404) == 200: + imeis = subscription_request.predict_result.get("content", {}).get("document", [])[0].get("content", [])[3].get("value", []) + purchase_date = subscription_request.predict_result.get("content", {}).get("document", [])[0].get("content", [])[2].get("value", []) + retailer = subscription_request.predict_result.get("content", {}).get("document", [])[0].get("content", [])[0].get("value", []) + except Exception as e: + print(f"[ERROR]: {e}") + print(f"[ERROR]: {subscription_request}") + data.append({ + 'RequestID': subscription_request.request_id, + 'RedemptionID': subscription_request.redemption_id, + 'IMEIs': imeis, + 'Purchase Date': purchase_date, + 'Retailer': retailer, + 'Reviewed result': subscription_request.reviewed_result, + 'Feedback result': subscription_request.feedback_result, + 'Client Request Time (ms)': subscription_request.client_request_time, + 'Server Processing Time (ms)': subscription_request.preprocessing_time + subscription_request.ai_inference_time, + 'Is Reviewed': subscription_request.is_reviewed, + # 'Is Bad Quality': subscription_request.is_bad_image_quality, + 'created_at': subscription_request.created_at.isoformat(), + 'updated_at': subscription_request.updated_at.isoformat() + }) + + response = { + 'subscription_requests': data + } + + return JsonResponse(response) + + elif request.method == 'POST': + data = request.data + + base_query = Q(request_id=request_id) + + subscription_request = SubscriptionRequest.objects.filter(base_query).first() + + reviewed_result = json.loads(data["reviewed_result"][1:-1]) + for field in ['retailername', 'sold_to_party', 'purchase_date', 'imei_number']: + if not field in reviewed_result.keys(): + raise RequiredFieldException(excArgs=f'reviewed_result.{field}') + subscription_request.reviewed_result = reviewed_result + subscription_request.reviewed_result['request_id'] = request_id + subscription_request.is_reviewed = True + subscription_request.save() + + return JsonResponse({'message': 'success.'}, status=200) + else: + return JsonResponse({'error': 'Invalid request method.'}, status=405) \ No newline at end of file diff --git a/cope2n-api/fwd_api/api_router.py b/cope2n-api/fwd_api/api_router.py index 9a466dc..322cc20 100755 --- a/cope2n-api/fwd_api/api_router.py +++ b/cope2n-api/fwd_api/api_router.py @@ -2,7 +2,7 @@ from django.conf import settings from rest_framework.routers import DefaultRouter, SimpleRouter from fwd_api.api.ctel_view import CtelViewSet -from fwd_api.api.accuracy_view import AccuracyViewSet +from fwd_api.api.accuracy_view import AccuracyViewSet, RequestViewSet from fwd_api.api.ctel_user_view import CtelUserViewSet @@ -16,6 +16,7 @@ else: router.register("ctel", CtelViewSet, basename="CtelAPI") router.register("ctel", CtelUserViewSet, basename="CtelUserAPI") router.register("ctel", AccuracyViewSet, basename="AccuracyAPI") +router.register("ctel", RequestViewSet, basename="RequestAPI") app_name = "api" urlpatterns = router.urls diff --git a/cope2n-api/fwd_api/utils/file.py b/cope2n-api/fwd_api/utils/file.py index 92142da..04eb5ce 100644 --- a/cope2n-api/fwd_api/utils/file.py +++ b/cope2n-api/fwd_api/utils/file.py @@ -19,6 +19,9 @@ from ..celery_worker.client_connector import c_connector import imagesize import csv +from openpyxl import load_workbook +from openpyxl.styles import Font, Border, Side, PatternFill, NamedStyle + def validate_feedback_file(csv_file_path): required_columns = ['redemptionNumber', 'requestId', 'imeiNumber', 'imeiNumber2', 'Purchase Date', 'retailer', 'Sold to party', 'timetakenmilli'] missing_columns = [] @@ -327,21 +330,122 @@ def build_media_url_v2(media_id: str, user_id: int, sub_id: int, u_sync_id: str) token = image_authenticator.generate_img_token_v2(user_id, sub_id, u_sync_id) return f'{settings.BASE_URL}/api/ctel/v2/media/request/{media_id}/?token={token}' -def json2xlsx(input: json): - """_summary_ - Args: - input (json): - : [{ - Subs: Jan, # Subtotal name - Metadata: {num_imei: 1, - ...: ...} - Data: [{num_imei: 1, - ...: ...}] - }] - OR - input (json): - : [] - Return xlsx (object) - """ - pass \ No newline at end of file +def get_value(_dict, keys): + keys = keys.split('.') + value = _dict + for key in keys: + if not key in value.keys(): + return "-" + else: + value = value.get(key, {}) + + if value != 0: + return value + else: + return "-" + + +def dict2xlsx(input: json, _type='report'): + red = "FF0000" + black = "000000" + green = "E2EFDA" + yellow = "FFF2CC" + gray = "D0CECE" + font_black = Font(name="Calibri", size=11, color=black) + font_black_bold = Font(name="Calibri", size=11, color=black, bold=True) + font_red = Font(name="Calibri", size=11, color=red) + thin = Side(border_style="thin", color=black) + border = Border(left=thin, right=thin, top=thin, bottom=thin) + fill_green = PatternFill(start_color=green, end_color=green, fill_type = "solid") + fill_yellow = PatternFill(start_color=yellow, end_color=yellow, fill_type = "solid") + fill_gray = PatternFill(start_color=gray, end_color=gray, fill_type = "solid") + normal_cell = NamedStyle(name="normal_cell", font=font_black, border=border) + normal_cell_red = NamedStyle(name="normal_cell_red", font=font_red, border=border) + + if _type == 'report': + wb = load_workbook(filename = 'report.xlsx') + ws = wb['Sheet1'] + mapping = { + 'A': 'subs', + 'B': 'extraction_date', + 'C': 'num_imei', + 'D': 'num_invoice', + 'E': 'total_images', + 'F': 'images_quality.successful', + 'G': 'images_quality.successful_percent', + 'H': 'images_quality.bad', + 'I': 'images_quality.bad_percent', + 'J': 'average_accuracy_rate.imei', + 'K': 'average_accuracy_rate.purchase_date', + 'L': 'average_accuracy_rate.retailer_name', + 'M': 'average_processing_time.imei', + 'N': 'average_processing_time.invoice', + 'O': 'usage.imei', + 'P': 'usage.invoice', + } + start_index = 5 + + elif _type == 'report_detail': + wb = load_workbook(filename = 'report_detail.xlsx') + ws = wb['Sheet1'] + mapping = { + 'A': 'request_id', + 'B': 'redemption_number', + 'C': 'image_type', + 'D': 'imei_user_submitted', + 'E': "imei_ocr_retrieved", + 'F': "imei1_accuracy", + 'G': "purchase_date_user_submitted", + 'H': "purchase_date_ocr_retrieved", + 'I': "purchase_date_accuracy", + 'J': "retailer_user_submitted", + 'K': "retailer_ocr_retrieved", + 'L': "retailer_accuracy", + 'M': "average_accuracy", + 'N': "ocr_processing_time", + 'O': "is_reviewed", + 'P': "bad_image_reasons", + 'Q': "countermeasures", + } + start_index = 4 + + for subtotal in input: + for key_index, key in enumerate(mapping.keys()): + value = get_value(subtotal, mapping[key]) + ws[key + str(start_index)] = value + ws[key + str(start_index)].border = border + + if _type == 'report': + ws[key + str(start_index)].font = font_black_bold + if key_index == 0 or (key_index >= 9 and key_index <= 15): + ws[key + str(start_index)].fill = fill_gray + elif key_index >= 4 and key_index <= 8: + ws[key + str(start_index)].fill = fill_yellow + elif _type == 'report_detail': + if 'accuracy' in mapping[key] and type(value) in [int, float] and value < 95: + ws[key + str(start_index)].style = normal_cell_red + elif 'time' in mapping[key] and type(value) in [int, float] and value > 2.0: + ws[key + str(start_index)].style = normal_cell_red + else: + ws[key + str(start_index)].style = normal_cell + + start_index += 1 + + if 'data' in subtotal.keys(): + for record in subtotal['data']: + for key in mapping.keys(): + value = get_value(record, mapping[key]) + ws[key + str(start_index)] = value + if 'average_accuracy_rate' in mapping[key] and type(value) in [int, float] and value < 95: + ws[key + str(start_index)].style = normal_cell_red + elif 'average_processing_time' in mapping[key] and type(value) in [int, float] and value > 2.0: + ws[key + str(start_index)].style = normal_cell_red + elif 'bad_percent' in mapping[key] and type(value) in [int, float] and value > 10: + ws[key + str(start_index)].style = normal_cell_red + else : + ws[key + str(start_index)].style = normal_cell + + start_index += 1 + + return wb diff --git a/cope2n-api/report.xlsx b/cope2n-api/report.xlsx new file mode 100644 index 0000000..fc5b37d Binary files /dev/null and b/cope2n-api/report.xlsx differ diff --git a/cope2n-api/report_detail.xlsx b/cope2n-api/report_detail.xlsx new file mode 100644 index 0000000..aa552cc Binary files /dev/null and b/cope2n-api/report_detail.xlsx differ