Merge branch 'main' of https://code.sdsdev.co.kr/SDSRV-IDP/sbt-idp into trungpt/invoice_no
This commit is contained in:
commit
565e2c52a6
@ -241,6 +241,7 @@ BAD_THRESHOLD = 0.75
|
|||||||
NEED_REVIEW = 1.0
|
NEED_REVIEW = 1.0
|
||||||
|
|
||||||
SUB_FOR_BILLING = ["all", "seao"]
|
SUB_FOR_BILLING = ["all", "seao"]
|
||||||
|
FIELD = ["imei_number", "purchase_date", "retailername", "sold_to_party", "invoice_no"]
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
|
@ -284,6 +284,7 @@ class AccuracyViewSet(viewsets.ViewSet):
|
|||||||
|
|
||||||
return JsonResponse(status=status.HTTP_200_OK, data={"report_id": report_id})
|
return JsonResponse(status=status.HTTP_200_OK, data={"report_id": report_id})
|
||||||
|
|
||||||
|
# Redundant, will be removed by 19 March 2024
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
parameters=[
|
parameters=[
|
||||||
OpenApiParameter(
|
OpenApiParameter(
|
||||||
@ -417,6 +418,9 @@ class AccuracyViewSet(viewsets.ViewSet):
|
|||||||
acc[key] = report.combined_accuracy.get(key, 0) if report.combined_accuracy else max([fb, rv])
|
acc[key] = report.combined_accuracy.get(key, 0) if report.combined_accuracy else max([fb, rv])
|
||||||
else:
|
else:
|
||||||
acc[key] = None
|
acc[key] = None
|
||||||
|
processing_time = report.average_OCR_time.get("avg", None) if report.average_OCR_time else None
|
||||||
|
if processing_time and processing_time == 0:
|
||||||
|
processing_time = None
|
||||||
data.append({
|
data.append({
|
||||||
"ID": report.id,
|
"ID": report.id,
|
||||||
"Created Date": report.created_at,
|
"Created Date": report.created_at,
|
||||||
@ -429,7 +433,7 @@ class AccuracyViewSet(viewsets.ViewSet):
|
|||||||
"IMEI Acc": acc["imei_number"],
|
"IMEI Acc": acc["imei_number"],
|
||||||
"Avg. Accuracy": acc["avg"],
|
"Avg. Accuracy": acc["avg"],
|
||||||
"Avg. Client Request Time": report.average_client_time.get("avg", 0) if report.average_client_time else 0,
|
"Avg. Client Request Time": report.average_client_time.get("avg", 0) if report.average_client_time else 0,
|
||||||
"Avg. OCR Processing Time": report.average_OCR_time.get("avg", 0) if report.average_OCR_time else 0,
|
"Avg. OCR Processing Time": processing_time,
|
||||||
"report_id": report.report_id,
|
"report_id": report.report_id,
|
||||||
"Subsidiary": map_subsidiary_short_to_long(report.subsidiary),
|
"Subsidiary": map_subsidiary_short_to_long(report.subsidiary),
|
||||||
})
|
})
|
||||||
@ -544,7 +548,7 @@ class AccuracyViewSet(viewsets.ViewSet):
|
|||||||
for key in keys:
|
for key in keys:
|
||||||
if report_fine_data[i][key]:
|
if report_fine_data[i][key]:
|
||||||
for x_key in report_fine_data[i][key].keys():
|
for x_key in report_fine_data[i][key].keys():
|
||||||
report_fine_data[i][key][x_key] = report_fine_data[i][key][x_key]*100
|
report_fine_data[i][key][x_key] = report_fine_data[i][key][x_key]*100 if report_fine_data[i][key][x_key] is not None else None
|
||||||
overview_filename = _subsidiary + "_" + duration + ".xlsx"
|
overview_filename = _subsidiary + "_" + duration + ".xlsx"
|
||||||
data_workbook = dict2xlsx(report_fine_data, _type='report')
|
data_workbook = dict2xlsx(report_fine_data, _type='report')
|
||||||
|
|
||||||
|
@ -144,6 +144,8 @@ def create_accuracy_report(report_id, **kwargs):
|
|||||||
report.average_OCR_time = {"invoice": time_cost["invoice"](), "imei": time_cost["imei"](),
|
report.average_OCR_time = {"invoice": time_cost["invoice"](), "imei": time_cost["imei"](),
|
||||||
"invoice_count": time_cost["invoice"].count, "imei_count": time_cost["imei"].count}
|
"invoice_count": time_cost["invoice"].count, "imei_count": time_cost["imei"].count}
|
||||||
|
|
||||||
|
report.average_OCR_time["invoice"] = 0 if report.average_OCR_time["invoice"] is None else report.average_OCR_time["invoice"]
|
||||||
|
report.average_OCR_time["imei"] = 0 if report.average_OCR_time["imei"] is None else report.average_OCR_time["imei"]
|
||||||
report.average_OCR_time["avg"] = (report.average_OCR_time["invoice"]*report.average_OCR_time["invoice_count"] + report.average_OCR_time["imei"]*report.average_OCR_time["imei_count"])/(
|
report.average_OCR_time["avg"] = (report.average_OCR_time["invoice"]*report.average_OCR_time["invoice_count"] + report.average_OCR_time["imei"]*report.average_OCR_time["imei_count"])/(
|
||||||
report.average_OCR_time["imei_count"] + report.average_OCR_time["invoice_count"]) if (report.average_OCR_time["imei_count"] + report.average_OCR_time["invoice_count"]) > 0 else None
|
report.average_OCR_time["imei_count"] + report.average_OCR_time["invoice_count"]) if (report.average_OCR_time["imei_count"] + report.average_OCR_time["invoice_count"]) > 0 else None
|
||||||
report.number_imei_transaction = transaction_att.get("imei", 0)
|
report.number_imei_transaction = transaction_att.get("imei", 0)
|
||||||
|
@ -101,8 +101,8 @@ class Command(BaseCommand):
|
|||||||
request.is_reviewed = False
|
request.is_reviewed = False
|
||||||
request.save()
|
request.save()
|
||||||
image.predict_result = _predict_result
|
image.predict_result = _predict_result
|
||||||
image.feedback_result = _feedback_result
|
# image.feedback_result = _feedback_result
|
||||||
image.reviewed_result = _reviewed_result
|
# image.reviewed_result = _reviewed_result
|
||||||
image.save()
|
image.save()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stdout.write(self.style.ERROR(f"Request: {request.request_id} failed with {e}"))
|
self.stdout.write(self.style.ERROR(f"Request: {request.request_id} failed with {e}"))
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
# myapp/management/commands/mycustomcommand.py
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from tqdm import tqdm
|
||||||
|
from fwd_api.models import SubscriptionRequestFile, SubscriptionRequest
|
||||||
|
from fwd_api.exception.exceptions import InvalidException
|
||||||
|
from fwd_api.utils.accuracy import predict_result_to_ready
|
||||||
|
import traceback
|
||||||
|
import copy
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Move predict result to image level'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
# Add your command-line arguments here
|
||||||
|
parser.add_argument('start', type=str, help='start date, sample: 2023-01-02T00:00:00+0700')
|
||||||
|
parser.add_argument('end', type=str, help='end date, sample: 2023-01-03T00:00:00+0700')
|
||||||
|
|
||||||
|
def process_request(self, request):
|
||||||
|
if len(request.request_id.split(".")[0].split("_")) < 2:
|
||||||
|
return
|
||||||
|
images = SubscriptionRequestFile.objects.filter(request=request)
|
||||||
|
time_cost = {"imei": [], "invoice": [], "all": []}
|
||||||
|
if request.ai_inference_profile is None:
|
||||||
|
time_cost["imei"] = [-1 for _ in range(len(images))]
|
||||||
|
time_cost["invoice"] = [-1]
|
||||||
|
time_cost["all"] = [-1]
|
||||||
|
else:
|
||||||
|
for k, v in request.ai_inference_profile.items():
|
||||||
|
time_cost[k.split("_")[0]].append(v["inference"][1][0] - v["inference"][0] + (v["postprocess"][1]-v["postprocess"][0]))
|
||||||
|
for i, image in enumerate(images):
|
||||||
|
try:
|
||||||
|
image.index_in_request = int(image.file_name.split(".")[0].split("_")[-1]) if len(image.file_name.split(".")[0].split("_")) > 4 else 0
|
||||||
|
image.doc_type = image.file_name.split(".")[0].split("_")[1] if len(image.file_name.split(".")[0].split("_")) > 4 else "all"
|
||||||
|
image.processing_time = time_cost[image.doc_type][image.index_in_request]
|
||||||
|
if not request.predict_result:
|
||||||
|
self.stdout.write(self.style.WARNING(f"Key predict_result not found in {request.request_id}"))
|
||||||
|
return
|
||||||
|
if request.predict_result.get("status", 200) != 200:
|
||||||
|
self.stdout.write(self.style.WARNING(f"Failed request: {request.request_id}"))
|
||||||
|
return
|
||||||
|
_predict_result = copy.deepcopy(predict_result_to_ready(request.predict_result))
|
||||||
|
|
||||||
|
if image.doc_type == "invoice":
|
||||||
|
_predict_result["imei_number"] = []
|
||||||
|
else:
|
||||||
|
_predict_result = {"retailername": None, "sold_to_party": None, "purchase_date": [], "imei_number": [_predict_result["imei_number"][image.index_in_request]]}
|
||||||
|
image.predict_result = _predict_result
|
||||||
|
image.save()
|
||||||
|
except Exception as e:
|
||||||
|
self.stdout.write(self.style.ERROR(f"Request: {request.request_id} failed with {e}"))
|
||||||
|
print(traceback.format_exc())
|
||||||
|
continue
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
start = options['start']
|
||||||
|
end = options['end']
|
||||||
|
|
||||||
|
if start or end:
|
||||||
|
try:
|
||||||
|
start_date = timezone.datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z')
|
||||||
|
end_date = timezone.datetime.strptime(end, '%Y-%m-%dT%H:%M:%S%z')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[INFO]: start: {start}")
|
||||||
|
print(f"[INFO]: end: {end}")
|
||||||
|
raise InvalidException(excArgs="Date format")
|
||||||
|
subcription_iter = SubscriptionRequest.objects.filter(created_at__range=(start_date, end_date))
|
||||||
|
else:
|
||||||
|
subcription_iter = SubscriptionRequest.objects.all()
|
||||||
|
|
||||||
|
for request in tqdm(subcription_iter.iterator()):
|
||||||
|
self.process_request(request)
|
||||||
|
self.stdout.write(self.style.SUCCESS('Sample Django management command executed successfully!'))
|
@ -84,7 +84,7 @@ class ReportAccumulateByRequest:
|
|||||||
'bad_percent': 0
|
'bad_percent': 0
|
||||||
},
|
},
|
||||||
'average_accuracy_rate': {
|
'average_accuracy_rate': {
|
||||||
'imei': IterAvg(),
|
'imei_number': IterAvg(),
|
||||||
'purchase_date': IterAvg(),
|
'purchase_date': IterAvg(),
|
||||||
'retailer_name': IterAvg(),
|
'retailer_name': IterAvg(),
|
||||||
'sold_to_party': IterAvg(),
|
'sold_to_party': IterAvg(),
|
||||||
@ -133,22 +133,12 @@ class ReportAccumulateByRequest:
|
|||||||
total["num_imei"] += 1 if doc_type == "imei" else 0
|
total["num_imei"] += 1 if doc_type == "imei" else 0
|
||||||
total["num_invoice"] += 1 if doc_type == "invoice" else 0
|
total["num_invoice"] += 1 if doc_type == "invoice" else 0
|
||||||
|
|
||||||
|
for key in settings.FIELD:
|
||||||
if sum([len(report_file.reviewed_accuracy[x]) for x in report_file.reviewed_accuracy.keys() if "_count" not in x]) > 0 :
|
if sum([len(report_file.reviewed_accuracy[x]) for x in report_file.reviewed_accuracy.keys() if "_count" not in x]) > 0 :
|
||||||
total["average_accuracy_rate"]["imei"].add(report_file.reviewed_accuracy.get("imei_number", []))
|
total["average_accuracy_rate"][key].add(report_file.reviewed_accuracy.get(key, []))
|
||||||
total["average_accuracy_rate"]["purchase_date"].add(report_file.reviewed_accuracy.get("purchase_date", []))
|
|
||||||
total["average_accuracy_rate"]["retailer_name"].add(report_file.reviewed_accuracy.get("retailername", []))
|
|
||||||
total["average_accuracy_rate"]["sold_to_party"].add(report_file.reviewed_accuracy.get("sold_to_party", []))
|
|
||||||
total["average_accuracy_rate"]["invoice_no"].add(report_file.reviewed_accuracy.get("invoice_no", []))
|
|
||||||
elif sum([len(report_file.feedback_accuracy[x]) for x in report_file.feedback_accuracy.keys() if "_count" not in x]) > 0:
|
elif sum([len(report_file.feedback_accuracy[x]) for x in report_file.feedback_accuracy.keys() if "_count" not in x]) > 0:
|
||||||
total["average_accuracy_rate"]["imei"].add(report_file.feedback_accuracy.get("imei_number", []))
|
total["average_accuracy_rate"][key].add(report_file.feedback_accuracy.get(key, []))
|
||||||
total["average_accuracy_rate"]["purchase_date"].add(report_file.feedback_accuracy.get("purchase_date", []))
|
|
||||||
total["average_accuracy_rate"]["retailer_name"].add(report_file.feedback_accuracy.get("retailername", []))
|
|
||||||
total["average_accuracy_rate"]["sold_to_party"].add(report_file.feedback_accuracy.get("sold_to_party", []))
|
|
||||||
total["average_accuracy_rate"]["invoice_no"].add(report_file.feedback_accuracy.get("invoice_no", []))
|
|
||||||
|
|
||||||
for key in ["imei_number", "purchase_date", "invoice_no", "retailername", "sold_to_party"]:
|
|
||||||
total["feedback_accuracy"][key].add(report_file.feedback_accuracy.get(key, []))
|
total["feedback_accuracy"][key].add(report_file.feedback_accuracy.get(key, []))
|
||||||
for key in ["imei_number", "purchase_date", "invoice_no", "retailername", "sold_to_party"]:
|
|
||||||
total["reviewed_accuracy"][key].add(report_file.reviewed_accuracy.get(key, []))
|
total["reviewed_accuracy"][key].add(report_file.reviewed_accuracy.get(key, []))
|
||||||
|
|
||||||
if not total["average_processing_time"].get(report_file.doc_type, None):
|
if not total["average_processing_time"].get(report_file.doc_type, None):
|
||||||
@ -182,22 +172,12 @@ class ReportAccumulateByRequest:
|
|||||||
day_data["num_invoice"] += 1 if doc_type == "invoice" else 0
|
day_data["num_invoice"] += 1 if doc_type == "invoice" else 0
|
||||||
day_data["report_files"].append(report_file)
|
day_data["report_files"].append(report_file)
|
||||||
|
|
||||||
|
for key in settings.FIELD:
|
||||||
if sum([len(report_file.reviewed_accuracy[x]) for x in report_file.reviewed_accuracy.keys() if "_count" not in x]) > 0:
|
if sum([len(report_file.reviewed_accuracy[x]) for x in report_file.reviewed_accuracy.keys() if "_count" not in x]) > 0:
|
||||||
day_data["average_accuracy_rate"]["imei"].add(report_file.reviewed_accuracy.get("imei_number", []))
|
day_data["average_accuracy_rate"][key].add(report_file.reviewed_accuracy.get(key, []))
|
||||||
day_data["average_accuracy_rate"]["purchase_date"].add(report_file.reviewed_accuracy.get("purchase_date", []))
|
|
||||||
day_data["average_accuracy_rate"]["retailer_name"].add(report_file.reviewed_accuracy.get("retailername", []))
|
|
||||||
day_data["average_accuracy_rate"]["sold_to_party"].add(report_file.reviewed_accuracy.get("sold_to_party", []))
|
|
||||||
day_data["average_accuracy_rate"]["invoice_no"].add(report_file.reviewed_accuracy.get("invoice_no", []))
|
|
||||||
elif sum([len(report_file.feedback_accuracy[x]) for x in report_file.feedback_accuracy.keys() if "_count" not in x]) > 0:
|
elif sum([len(report_file.feedback_accuracy[x]) for x in report_file.feedback_accuracy.keys() if "_count" not in x]) > 0:
|
||||||
day_data["average_accuracy_rate"]["imei"].add(report_file.feedback_accuracy.get("imei_number", []))
|
day_data["average_accuracy_rate"][key].add(report_file.feedback_accuracy.get(key, []))
|
||||||
day_data["average_accuracy_rate"]["purchase_date"].add(report_file.feedback_accuracy.get("purchase_date", []))
|
|
||||||
day_data["average_accuracy_rate"]["retailer_name"].add(report_file.feedback_accuracy.get("retailername", []))
|
|
||||||
day_data["average_accuracy_rate"]["sold_to_party"].add(report_file.feedback_accuracy.get("sold_to_party", []))
|
|
||||||
day_data["average_accuracy_rate"]["invoice_no"].add(report_file.feedback_accuracy.get("invoice_no", []))
|
|
||||||
|
|
||||||
for key in ["imei_number", "purchase_date", "invoice_no", "retailername", "sold_to_party"]:
|
|
||||||
day_data["feedback_accuracy"][key].add(report_file.feedback_accuracy.get(key, []))
|
day_data["feedback_accuracy"][key].add(report_file.feedback_accuracy.get(key, []))
|
||||||
for key in ["imei_number", "purchase_date", "invoice_no", "retailername", "sold_to_party"]:
|
|
||||||
day_data["reviewed_accuracy"][key].add(report_file.reviewed_accuracy.get(key, []))
|
day_data["reviewed_accuracy"][key].add(report_file.reviewed_accuracy.get(key, []))
|
||||||
|
|
||||||
if not day_data["average_processing_time"].get(report_file.doc_type, None):
|
if not day_data["average_processing_time"].get(report_file.doc_type, None):
|
||||||
@ -274,7 +254,7 @@ class ReportAccumulateByRequest:
|
|||||||
"reviewed_accuracy": {}}
|
"reviewed_accuracy": {}}
|
||||||
for acc_type in ["feedback_accuracy", "reviewed_accuracy"]:
|
for acc_type in ["feedback_accuracy", "reviewed_accuracy"]:
|
||||||
avg_acc = IterAvg()
|
avg_acc = IterAvg()
|
||||||
for key in ["imei_number", "purchase_date", "invoice_no", "retailername", "sold_to_party"]:
|
for key in settings.FIELD:
|
||||||
acumulated_acc[acc_type][key] = self.data[month][1][day][acc_type][key]()
|
acumulated_acc[acc_type][key] = self.data[month][1][day][acc_type][key]()
|
||||||
acumulated_acc[acc_type][key+"_count"] = self.data[month][1][day][acc_type][key].count
|
acumulated_acc[acc_type][key+"_count"] = self.data[month][1][day][acc_type][key].count
|
||||||
avg_acc.add_avg(acumulated_acc[acc_type][key], acumulated_acc[acc_type][key+"_count"])
|
avg_acc.add_avg(acumulated_acc[acc_type][key], acumulated_acc[acc_type][key+"_count"])
|
||||||
@ -318,26 +298,13 @@ class ReportAccumulateByRequest:
|
|||||||
for day in _data[month][1].keys():
|
for day in _data[month][1].keys():
|
||||||
num_transaction_imei += _data[month][1][day]["usage"].get("imei", 0)
|
num_transaction_imei += _data[month][1][day]["usage"].get("imei", 0)
|
||||||
num_transaction_invoice += _data[month][1][day]["usage"].get("invoice", 0)
|
num_transaction_invoice += _data[month][1][day]["usage"].get("invoice", 0)
|
||||||
_data[month][1][day]["average_accuracy_rate"]["imei"] = _data[month][1][day]["average_accuracy_rate"]["imei"]()
|
|
||||||
_data[month][1][day]["average_accuracy_rate"]["purchase_date"] = _data[month][1][day]["average_accuracy_rate"]["purchase_date"]()
|
|
||||||
_data[month][1][day]["average_accuracy_rate"]["retailer_name"] = _data[month][1][day]["average_accuracy_rate"]["retailer_name"]()
|
|
||||||
_data[month][1][day]["average_accuracy_rate"]["sold_to_party"] = _data[month][1][day]["average_accuracy_rate"]["sold_to_party"]()
|
|
||||||
_data[month][1][day]["average_accuracy_rate"]["invoice_no"] = _data[month][1][day]["average_accuracy_rate"]["invoice_no"]()
|
|
||||||
for key in _data[month][1][day]["average_processing_time"].keys():
|
for key in _data[month][1][day]["average_processing_time"].keys():
|
||||||
_data[month][1][day]["average_processing_time"][key] = _data[month][1][day]["average_processing_time"][key]()
|
_data[month][1][day]["average_processing_time"][key] = _data[month][1][day]["average_processing_time"][key]()
|
||||||
|
|
||||||
_data[month][1][day]["feedback_accuracy"]["imei_number"] = _data[month][1][day]["feedback_accuracy"]["imei_number"]()
|
for key in settings.FIELD:
|
||||||
_data[month][1][day]["feedback_accuracy"]["purchase_date"] = _data[month][1][day]["feedback_accuracy"]["purchase_date"]()
|
_data[month][1][day]["average_accuracy_rate"][key] = _data[month][1][day]["average_accuracy_rate"][key]()
|
||||||
_data[month][1][day]["feedback_accuracy"]["retailername"] = _data[month][1][day]["feedback_accuracy"]["retailername"]()
|
for accuracy_type in ["feedback_accuracy", key]:
|
||||||
_data[month][1][day]["feedback_accuracy"]["sold_to_party"] = _data[month][1][day]["feedback_accuracy"]["sold_to_party"]()
|
_data[month][1][day][accuracy_type]["imei_number"] = _data[month][1][day]["feedback_accuracy"]["imei_number"]()
|
||||||
_data[month][1][day]["feedback_accuracy"]["invoice_no"] = _data[month][1][day]["feedback_accuracy"]["invoice_no"]()
|
|
||||||
|
|
||||||
_data[month][1][day]["reviewed_accuracy"]["imei_number"] = _data[month][1][day]["reviewed_accuracy"]["imei_number"]()
|
|
||||||
_data[month][1][day]["reviewed_accuracy"]["purchase_date"] = _data[month][1][day]["reviewed_accuracy"]["purchase_date"]()
|
|
||||||
_data[month][1][day]["reviewed_accuracy"]["retailername"] = _data[month][1][day]["reviewed_accuracy"]["retailername"]()
|
|
||||||
_data[month][1][day]["reviewed_accuracy"]["sold_to_party"] = _data[month][1][day]["reviewed_accuracy"]["sold_to_party"]()
|
|
||||||
_data[month][1][day]["reviewed_accuracy"]["invoice_no"] = _data[month][1][day]["reviewed_accuracy"]["invoice_no"]()
|
|
||||||
|
|
||||||
_data[month][1][day]["review_progress"] = _data[month][1][day]["review_progress"].count(1)/(_data[month][1][day]["review_progress"].count(0)+ _data[month][1][day]["review_progress"].count(1)) if (_data[month][1][day]["review_progress"].count(0)+ _data[month][1][day]["review_progress"].count(1)) >0 else 0
|
_data[month][1][day]["review_progress"] = _data[month][1][day]["review_progress"].count(1)/(_data[month][1][day]["review_progress"].count(0)+ _data[month][1][day]["review_progress"].count(1)) if (_data[month][1][day]["review_progress"].count(0)+ _data[month][1][day]["review_progress"].count(1)) >0 else 0
|
||||||
_data[month][1][day].pop("report_files")
|
_data[month][1][day].pop("report_files")
|
||||||
|
|
||||||
@ -347,28 +314,13 @@ class ReportAccumulateByRequest:
|
|||||||
_data[month][0]["usage"]["imei"] = num_transaction_imei
|
_data[month][0]["usage"]["imei"] = num_transaction_imei
|
||||||
_data[month][0]["usage"]["invoice"] = num_transaction_invoice
|
_data[month][0]["usage"]["invoice"] = num_transaction_invoice
|
||||||
_data[month][0]["usage"]["total_images"] = num_transaction_invoice + num_transaction_imei
|
_data[month][0]["usage"]["total_images"] = num_transaction_invoice + num_transaction_imei
|
||||||
_data[month][0]["average_accuracy_rate"]["imei"] = _data[month][0]["average_accuracy_rate"]["imei"]()
|
|
||||||
_data[month][0]["average_accuracy_rate"]["purchase_date"] = _data[month][0]["average_accuracy_rate"]["purchase_date"]()
|
|
||||||
_data[month][0]["average_accuracy_rate"]["retailer_name"] = _data[month][0]["average_accuracy_rate"]["retailer_name"]()
|
|
||||||
_data[month][0]["average_accuracy_rate"]["sold_to_party"] = _data[month][0]["average_accuracy_rate"]["sold_to_party"]()
|
|
||||||
_data[month][0]["average_accuracy_rate"]["invoice_no"] = _data[month][0]["average_accuracy_rate"]["invoice_no"]()
|
|
||||||
for key in _data[month][0]["average_processing_time"].keys():
|
for key in _data[month][0]["average_processing_time"].keys():
|
||||||
_data[month][0]["average_processing_time"][key] = _data[month][0]["average_processing_time"][key]()
|
_data[month][0]["average_processing_time"][key] = _data[month][0]["average_processing_time"][key]()
|
||||||
|
for key in settings.FIELD:
|
||||||
_data[month][0]["feedback_accuracy"]["imei_number"] = _data[month][0]["feedback_accuracy"]["imei_number"]()
|
_data[month][0]["average_accuracy_rate"][key] = _data[month][0]["average_accuracy_rate"][key]()
|
||||||
_data[month][0]["feedback_accuracy"]["purchase_date"] = _data[month][0]["feedback_accuracy"]["purchase_date"]()
|
for accuracy_type in ["feedback_accuracy", key]:
|
||||||
_data[month][0]["feedback_accuracy"]["retailername"] = _data[month][0]["feedback_accuracy"]["retailername"]()
|
_data[month][0][accuracy_type][key] = _data[month][0][accuracy_type][key]()
|
||||||
_data[month][0]["feedback_accuracy"]["sold_to_party"] = _data[month][0]["feedback_accuracy"]["sold_to_party"]()
|
|
||||||
_data[month][0]["feedback_accuracy"]["invoice_no"] = _data[month][0]["feedback_accuracy"]["invoice_no"]()
|
|
||||||
|
|
||||||
_data[month][0]["reviewed_accuracy"]["imei_number"] = _data[month][0]["reviewed_accuracy"]["imei_number"]()
|
|
||||||
_data[month][0]["reviewed_accuracy"]["purchase_date"] = _data[month][0]["reviewed_accuracy"]["purchase_date"]()
|
|
||||||
_data[month][0]["reviewed_accuracy"]["retailername"] = _data[month][0]["reviewed_accuracy"]["retailername"]()
|
|
||||||
_data[month][0]["reviewed_accuracy"]["sold_to_party"] = _data[month][0]["reviewed_accuracy"]["sold_to_party"]()
|
|
||||||
_data[month][0]["reviewed_accuracy"]["invoice_no"] = _data[month][0]["reviewed_accuracy"]["invoice_no"]()
|
|
||||||
|
|
||||||
_data[month][0]["review_progress"] = _data[month][0]["review_progress"].count(1)/(_data[month][0]["review_progress"].count(0)+ _data[month][0]["review_progress"].count(1)) if (_data[month][0]["review_progress"].count(0)+ _data[month][0]["review_progress"].count(1)) >0 else 0
|
_data[month][0]["review_progress"] = _data[month][0]["review_progress"].count(1)/(_data[month][0]["review_progress"].count(0)+ _data[month][0]["review_progress"].count(1)) if (_data[month][0]["review_progress"].count(0)+ _data[month][0]["review_progress"].count(1)) >0 else 0
|
||||||
|
|
||||||
return _data
|
return _data
|
||||||
|
|
||||||
class MonthReportAccumulate:
|
class MonthReportAccumulate:
|
||||||
@ -580,6 +532,7 @@ def first_of_list(the_list):
|
|||||||
def extract_report_detail_list(report_detail_list, lower=False, in_percent=True):
|
def extract_report_detail_list(report_detail_list, lower=False, in_percent=True):
|
||||||
data = []
|
data = []
|
||||||
for report_file in report_detail_list:
|
for report_file in report_detail_list:
|
||||||
|
# FIXME: #79 Fill None with value
|
||||||
data.append({
|
data.append({
|
||||||
"Subs": report_file.subsidiary,
|
"Subs": report_file.subsidiary,
|
||||||
"Request ID": report_file.correspond_request_id,
|
"Request ID": report_file.correspond_request_id,
|
||||||
@ -587,12 +540,19 @@ def extract_report_detail_list(report_detail_list, lower=False, in_percent=True)
|
|||||||
"Image type": report_file.doc_type,
|
"Image type": report_file.doc_type,
|
||||||
"IMEI_user submitted": first_of_list(report_file.feedback_result.get("imei_number", [None])),
|
"IMEI_user submitted": first_of_list(report_file.feedback_result.get("imei_number", [None])),
|
||||||
"IMEI_OCR retrieved": first_of_list(report_file.predict_result.get("imei_number", [None])),
|
"IMEI_OCR retrieved": first_of_list(report_file.predict_result.get("imei_number", [None])),
|
||||||
|
"IMEI Revised": None,
|
||||||
"IMEI1 Accuracy": first_of_list(report_file.feedback_accuracy.get("imei_number", [None])),
|
"IMEI1 Accuracy": first_of_list(report_file.feedback_accuracy.get("imei_number", [None])),
|
||||||
|
"Invoice_Number_User": None,
|
||||||
|
"Invoice_Number_OCR": None,
|
||||||
|
"Invoice_Number Revised": None,
|
||||||
|
"Invoice_Number_Accuracy": None,
|
||||||
"Invoice_Purchase Date_Consumer": report_file.feedback_result.get("purchase_date", None),
|
"Invoice_Purchase Date_Consumer": report_file.feedback_result.get("purchase_date", None),
|
||||||
"Invoice_Purchase Date_OCR": report_file.predict_result.get("purchase_date", []),
|
"Invoice_Purchase Date_OCR": report_file.predict_result.get("purchase_date", []),
|
||||||
|
"Invoice_Purchase Date Revised": None,
|
||||||
"Invoice_Purchase Date Accuracy": first_of_list(report_file.feedback_accuracy.get("purchase_date", [None])),
|
"Invoice_Purchase Date Accuracy": first_of_list(report_file.feedback_accuracy.get("purchase_date", [None])),
|
||||||
"Invoice_Retailer_Consumer": report_file.feedback_result.get("retailername", None),
|
"Invoice_Retailer_Consumer": report_file.feedback_result.get("retailername", None),
|
||||||
"Invoice_Retailer_OCR": report_file.predict_result.get("retailername", None),
|
"Invoice_Retailer_OCR": report_file.predict_result.get("retailername", None),
|
||||||
|
"Invoice_Purchase Date Revised": None,
|
||||||
"Invoice_Retailer Accuracy": first_of_list(report_file.feedback_accuracy.get("retailername", [None])),
|
"Invoice_Retailer Accuracy": first_of_list(report_file.feedback_accuracy.get("retailername", [None])),
|
||||||
"Invoice_No_Consumer": report_file.feedback_result.get("invoice_no", None),
|
"Invoice_No_Consumer": report_file.feedback_result.get("invoice_no", None),
|
||||||
"Invoice_No_OCR": report_file.predict_result.get("invoice_no", None),
|
"Invoice_No_OCR": report_file.predict_result.get("invoice_no", None),
|
||||||
@ -811,6 +771,12 @@ def create_billing_data(subscription_requests):
|
|||||||
return billing_data
|
return billing_data
|
||||||
|
|
||||||
def calculate_a_request(report, request):
|
def calculate_a_request(report, request):
|
||||||
|
def review_status_map(input):
|
||||||
|
review_status = {-1: "Not Required",
|
||||||
|
0: "No",
|
||||||
|
1: "Yes"}
|
||||||
|
return review_status.get(input, "N/A")
|
||||||
|
|
||||||
request_att = {"acc": {"feedback": {"imei_number": [],
|
request_att = {"acc": {"feedback": {"imei_number": [],
|
||||||
"purchase_date": [],
|
"purchase_date": [],
|
||||||
"retailername": [],
|
"retailername": [],
|
||||||
@ -871,8 +837,8 @@ def calculate_a_request(report, request):
|
|||||||
if len(att["normalized_data"]["reviewed"].get("purchase_date", [])) > 0:
|
if len(att["normalized_data"]["reviewed"].get("purchase_date", [])) > 0:
|
||||||
image.predict_result["purchase_date"] = [att["normalized_data"]["reviewed"]["purchase_date"][i][0] for i in range(len(att["normalized_data"]["reviewed"]["purchase_date"]))]
|
image.predict_result["purchase_date"] = [att["normalized_data"]["reviewed"]["purchase_date"][i][0] for i in range(len(att["normalized_data"]["reviewed"]["purchase_date"]))]
|
||||||
image.reviewed_result["purchase_date"] = att["normalized_data"]["reviewed"]["purchase_date"][rv_max_indexes["purchase_date"]][1]
|
image.reviewed_result["purchase_date"] = att["normalized_data"]["reviewed"]["purchase_date"][rv_max_indexes["purchase_date"]][1]
|
||||||
if request.is_reviewed:
|
# if request.is_reviewed:
|
||||||
att["is_reviewed"] = 1
|
# att["is_reviewed"] = 1
|
||||||
request_att["is_reviewed"].append(att["is_reviewed"])
|
request_att["is_reviewed"].append(att["is_reviewed"])
|
||||||
new_report_file = ReportFile(report=report,
|
new_report_file = ReportFile(report=report,
|
||||||
subsidiary=_sub,
|
subsidiary=_sub,
|
||||||
@ -886,7 +852,7 @@ def calculate_a_request(report, request):
|
|||||||
reviewed_accuracy=att["acc"]["reviewed"],
|
reviewed_accuracy=att["acc"]["reviewed"],
|
||||||
acc=att["avg_acc"],
|
acc=att["avg_acc"],
|
||||||
is_bad_image=att["is_bad_image"],
|
is_bad_image=att["is_bad_image"],
|
||||||
is_reviewed= "Yes" if request.is_reviewed else "No",
|
is_reviewed= review_status_map(att["is_reviewed"]),
|
||||||
time_cost=image.processing_time,
|
time_cost=image.processing_time,
|
||||||
bad_image_reason=image.reason,
|
bad_image_reason=image.reason,
|
||||||
counter_measures=image.counter_measures,
|
counter_measures=image.counter_measures,
|
||||||
@ -979,6 +945,10 @@ def calculate_subcription_file(subcription_request_file):
|
|||||||
avg_acc = avg_reviewed
|
avg_acc = avg_reviewed
|
||||||
att["is_reviewed"] = 1
|
att["is_reviewed"] = 1
|
||||||
|
|
||||||
|
# Little trick to overcome issue caused by misleading manually review process
|
||||||
|
if subcription_request_file.reason or subcription_request_file.counter_measures:
|
||||||
|
att["is_reviewed"] = 1
|
||||||
|
|
||||||
att["avg_acc"] = avg_acc
|
att["avg_acc"] = avg_acc
|
||||||
if avg_acc < settings.BAD_THRESHOLD:
|
if avg_acc < settings.BAD_THRESHOLD:
|
||||||
att["is_bad_image"] = True
|
att["is_bad_image"] = True
|
||||||
|
@ -487,9 +487,10 @@ def dict2xlsx(input: json, _type='report'):
|
|||||||
'M': 'average_accuracy_rate.imei',
|
'M': 'average_accuracy_rate.imei',
|
||||||
'N': 'average_accuracy_rate.purchase_date',
|
'N': 'average_accuracy_rate.purchase_date',
|
||||||
'O': 'average_accuracy_rate.retailer_name',
|
'O': 'average_accuracy_rate.retailer_name',
|
||||||
'P': 'average_processing_time.imei',
|
'P': 'average_accuracy_rate.invoice_number',
|
||||||
'Q': 'average_processing_time.invoice',
|
'Q': 'average_processing_time.imei',
|
||||||
'R': 'review_progress'
|
'R': 'average_processing_time.invoice',
|
||||||
|
'S': 'review_progress'
|
||||||
}
|
}
|
||||||
start_index = 5
|
start_index = 5
|
||||||
|
|
||||||
@ -503,21 +504,29 @@ def dict2xlsx(input: json, _type='report'):
|
|||||||
'D': 'image_type',
|
'D': 'image_type',
|
||||||
'E': 'imei_user_submitted',
|
'E': 'imei_user_submitted',
|
||||||
'F': "imei_ocr_retrieved",
|
'F': "imei_ocr_retrieved",
|
||||||
'G': "imei1_accuracy",
|
'G': "imei_revised",
|
||||||
'H': "invoice_purchase_date_consumer",
|
'H': "imei1_accuracy",
|
||||||
'I': "invoice_purchase_date_ocr",
|
'I': "invoice_number_user",
|
||||||
'J': "invoice_purchase_date_accuracy",
|
'J': "invoice_number_ocr",
|
||||||
'K': "invoice_retailer_consumer",
|
'K': "invoice_number_revised",
|
||||||
'L': "invoice_retailer_ocr",
|
'L': "invoice_number_accuracy",
|
||||||
'M': "invoice_retailer_accuracy",
|
'M': "invoice_purchase_date_consumer",
|
||||||
'N': "ocr_image_accuracy",
|
'N': "invoice_purchase_date_ocr",
|
||||||
'O': "ocr_image_speed_(seconds)",
|
'O': "invoice_purchase_date_revised",
|
||||||
'P': "is_reviewed",
|
'P': "invoice_purchase_date_accuracy",
|
||||||
'Q': "bad_image_reasons",
|
'Q': "invoice_retailer_consumer",
|
||||||
'R': "countermeasures",
|
'R': "invoice_retailer_ocr",
|
||||||
'S': 'imei_revised_accuracy',
|
'S': 'invoice_retailer_revised',
|
||||||
'T': 'purchase_date_revised_accuracy',
|
'T': "invoice_retailer_accuracy",
|
||||||
'U': 'retailer_revised_accuracy',
|
'U': "ocr_image_accuracy",
|
||||||
|
'V': "ocr_image_speed_(seconds)",
|
||||||
|
'W': "is_reviewed",
|
||||||
|
'X': "bad_image_reasons",
|
||||||
|
'Y': "countermeasures",
|
||||||
|
'Z': "imei_revised_accuracy",
|
||||||
|
'AA': "invoice_number_revised_accuracy",
|
||||||
|
'AB': 'purchase_date_revised_accuracy',
|
||||||
|
'AC': 'retailer_revised_accuracy',
|
||||||
}
|
}
|
||||||
start_index = 4
|
start_index = 4
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
VITE_PORT=8080
|
VITE_PORT=8080
|
||||||
VITE_PROXY=https://107.120.133.22/
|
VITE_PROXY=http://42.96.42.13:9881
|
||||||
VITE_KUBEFLOW_HOST=https://107.120.133.22:8085
|
VITE_KUBEFLOW_HOST=https://107.120.133.22:8085
|
@ -1,7 +1,7 @@
|
|||||||
import type { TableColumnsType } from 'antd';
|
import type { TableColumnsType } from 'antd';
|
||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ensureMax, ensureMin, formatPercent } from 'utils/metric-format';
|
import { ensureMax, ensureMin, formatPercent, formatNumber } from 'utils/metric-format';
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
key: React.Key;
|
key: React.Key;
|
||||||
@ -224,7 +224,7 @@ const columns: TableColumnsType<DataType> = [
|
|||||||
const isAbnormal = ensureMax(record.snImeiAPT, 2);
|
const isAbnormal = ensureMax(record.snImeiAPT, 2);
|
||||||
return (
|
return (
|
||||||
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
||||||
{record?.snImeiAPT?.toFixed(1)}
|
{formatNumber(record?.snImeiAPT)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -237,7 +237,7 @@ const columns: TableColumnsType<DataType> = [
|
|||||||
const isAbnormal = ensureMax(record.invoiceAPT, 2);
|
const isAbnormal = ensureMax(record.invoiceAPT, 2);
|
||||||
return (
|
return (
|
||||||
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
||||||
{record?.invoiceAPT?.toFixed(1)}
|
{formatNumber(record?.invoiceAPT)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ import { useReportList } from 'queries/report';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { downloadReport } from 'request/report';
|
import { downloadReport } from 'request/report';
|
||||||
import { formatPercent, ensureMin, ensureMax } from 'utils/metric-format';
|
import { formatPercent, ensureMin, ensureMax, formatNumber } from 'utils/metric-format';
|
||||||
import { datetimeStrToDate } from 'utils/time';
|
import { datetimeStrToDate } from 'utils/time';
|
||||||
|
|
||||||
|
|
||||||
@ -125,8 +125,7 @@ const ReportTable: React.FC = () => {
|
|||||||
const isAbnormal = ensureMin(record['IMEI Acc'], 0.95);
|
const isAbnormal = ensureMin(record['IMEI Acc'], 0.95);
|
||||||
return (
|
return (
|
||||||
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
||||||
{record['IMEI Acc'] &&
|
{formatPercent(record['IMEI Acc'])}
|
||||||
formatPercent(Number(record['IMEI Acc']))}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -139,22 +138,20 @@ const ReportTable: React.FC = () => {
|
|||||||
const isAbnormal = ensureMin(record['Avg. Accuracy'], 0.95);
|
const isAbnormal = ensureMin(record['Avg. Accuracy'], 0.95);
|
||||||
return (
|
return (
|
||||||
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
||||||
{record['Avg. Accuracy'] &&
|
{formatPercent(record['Avg. Accuracy'])}
|
||||||
formatPercent(Number(record['Avg. Accuracy']))}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Avg. OCR Processing Time',
|
title: 'Avg. OCR Processing Time (ms)',
|
||||||
dataIndex: 'Avg. OCR Processing Time',
|
dataIndex: 'Avg. OCR Processing Time',
|
||||||
key: 'Avg. OCR Processing Time',
|
key: 'Avg. OCR Processing Time',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
const isAbnormal = ensureMax(record['Avg. OCR Processing Time'], 2);
|
const isAbnormal = ensureMax(record['Avg. OCR Processing Time'], 2);
|
||||||
return (
|
return (
|
||||||
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
<span style={{ color: isAbnormal ? 'red' : '' }}>
|
||||||
{record['Avg. OCR Processing Time'] &&
|
{formatNumber(record['Avg. OCR Processing Time'], 1)}
|
||||||
Number(record['Avg. OCR Processing Time'])?.toFixed(2)}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -11,11 +11,14 @@
|
|||||||
"Email format is not correct": "Email format is not correct",
|
"Email format is not correct": "Email format is not correct",
|
||||||
"English": "English",
|
"English": "English",
|
||||||
"Go to Reports": "Go to Reports",
|
"Go to Reports": "Go to Reports",
|
||||||
|
"Handwritten": "Handwritten",
|
||||||
"Inference": "Inference",
|
"Inference": "Inference",
|
||||||
|
"Invalid image": "Invalid image",
|
||||||
"Is Test": "Is Test",
|
"Is Test": "Is Test",
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
|
"Missing information": "Missing information",
|
||||||
"New Report": "New Report",
|
"New Report": "New Report",
|
||||||
"Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available": "Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available",
|
"Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available": "Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
@ -28,8 +31,11 @@
|
|||||||
"Please enter a valid domain": "Please enter a valid domain",
|
"Please enter a valid domain": "Please enter a valid domain",
|
||||||
"Please specify a password": "Please specify a password",
|
"Please specify a password": "Please specify a password",
|
||||||
"Please specify a username": "Please specify a username",
|
"Please specify a username": "Please specify a username",
|
||||||
|
"Reason for bad quality:": "Reason for bad quality:",
|
||||||
|
"Recheck": "Recheck",
|
||||||
"Report Details": "Report Details",
|
"Report Details": "Report Details",
|
||||||
"Report Filters": "Report Filters",
|
"Report Filters": "Report Filters",
|
||||||
|
"Report Type": "Report Type",
|
||||||
"Reports": "Reports",
|
"Reports": "Reports",
|
||||||
"Retry": "Retry",
|
"Retry": "Retry",
|
||||||
"Review": "Review",
|
"Review": "Review",
|
||||||
@ -43,6 +49,8 @@
|
|||||||
"This field must not have more than {MAX_EMAIL_LENGTH} characters": "This field must not have more than {MAX_EMAIL_LENGTH} characters",
|
"This field must not have more than {MAX_EMAIL_LENGTH} characters": "This field must not have more than {MAX_EMAIL_LENGTH} characters",
|
||||||
"This field must not have more than {MAX_STRING_LENGTH} characters": "This field must not have more than {MAX_STRING_LENGTH} characters",
|
"This field must not have more than {MAX_STRING_LENGTH} characters": "This field must not have more than {MAX_STRING_LENGTH} characters",
|
||||||
"This field must not have more than {MAX_USERNAME_LENGTH} characters": "This field must not have more than {MAX_USERNAME_LENGTH} characters",
|
"This field must not have more than {MAX_USERNAME_LENGTH} characters": "This field must not have more than {MAX_USERNAME_LENGTH} characters",
|
||||||
|
"Too blurry text": "Too blurry text",
|
||||||
|
"Too small text": "Too small text",
|
||||||
"Upload files to process. The requests here will not be used in accuracy or payment calculations.": "Upload files to process. The requests here will not be used in accuracy or payment calculations.",
|
"Upload files to process. The requests here will not be used in accuracy or payment calculations.": "Upload files to process. The requests here will not be used in accuracy or payment calculations.",
|
||||||
"User log in successfully": "User log in successfully",
|
"User log in successfully": "User log in successfully",
|
||||||
"Username": "Username",
|
"Username": "Username",
|
||||||
|
@ -11,11 +11,14 @@
|
|||||||
"Email format is not correct": "Định dạng email không hợp lệ",
|
"Email format is not correct": "Định dạng email không hợp lệ",
|
||||||
"English": "Tiếng Anh",
|
"English": "Tiếng Anh",
|
||||||
"Go to Reports": "",
|
"Go to Reports": "",
|
||||||
|
"Handwritten": "",
|
||||||
"Inference": "",
|
"Inference": "",
|
||||||
|
"Invalid image": "",
|
||||||
"Is Test": "",
|
"Is Test": "",
|
||||||
"Language": "Ngôn ngữ",
|
"Language": "Ngôn ngữ",
|
||||||
"Login": "Đăng nhập",
|
"Login": "Đăng nhập",
|
||||||
"Logout": "Đăng xuất",
|
"Logout": "Đăng xuất",
|
||||||
|
"Missing information": "",
|
||||||
"New Report": "",
|
"New Report": "",
|
||||||
"Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available": "Chỉ cho phép các ký tự (a-z), (A-Z), (0-9), @, ., +, -, _",
|
"Only characters (a-z), (A-Z), (0-9), @, ., +, -, _ are available": "Chỉ cho phép các ký tự (a-z), (A-Z), (0-9), @, ., +, -, _",
|
||||||
"Password": "Mật khẩu",
|
"Password": "Mật khẩu",
|
||||||
@ -28,8 +31,11 @@
|
|||||||
"Please enter a valid domain": "Vui lòng nhập một tên miền hợp lệ",
|
"Please enter a valid domain": "Vui lòng nhập một tên miền hợp lệ",
|
||||||
"Please specify a password": "Vui lòng nhập một mật khẩu",
|
"Please specify a password": "Vui lòng nhập một mật khẩu",
|
||||||
"Please specify a username": "Vui lòng nhập một tên tài khoản",
|
"Please specify a username": "Vui lòng nhập một tên tài khoản",
|
||||||
|
"Reason for bad quality:": "",
|
||||||
|
"Recheck": "",
|
||||||
"Report Details": "",
|
"Report Details": "",
|
||||||
"Report Filters": "",
|
"Report Filters": "",
|
||||||
|
"Report Type": "",
|
||||||
"Reports": "",
|
"Reports": "",
|
||||||
"Retry": "Thử lại",
|
"Retry": "Thử lại",
|
||||||
"Review": "",
|
"Review": "",
|
||||||
@ -43,6 +49,8 @@
|
|||||||
"This field must not have more than {MAX_EMAIL_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_EMAIL_LENGTH} kí tự",
|
"This field must not have more than {MAX_EMAIL_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_EMAIL_LENGTH} kí tự",
|
||||||
"This field must not have more than {MAX_STRING_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_STRING_LENGTH} kí tự",
|
"This field must not have more than {MAX_STRING_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_STRING_LENGTH} kí tự",
|
||||||
"This field must not have more than {MAX_USERNAME_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_USERNAME_LENGTH} kí tự",
|
"This field must not have more than {MAX_USERNAME_LENGTH} characters": "Độ dài chuỗi không được vượt quá {MAX_USERNAME_LENGTH} kí tự",
|
||||||
|
"Too blurry text": "",
|
||||||
|
"Too small text": "",
|
||||||
"Upload files to process. The requests here will not be used in accuracy or payment calculations.": "",
|
"Upload files to process. The requests here will not be used in accuracy or payment calculations.": "",
|
||||||
"User log in successfully": "Đăng nhập thành công",
|
"User log in successfully": "Đăng nhập thành công",
|
||||||
"Username": "Tên tài khoản",
|
"Username": "Tên tài khoản",
|
||||||
|
@ -65,6 +65,7 @@ export interface MakeReportParams {
|
|||||||
start_date: string;
|
start_date: string;
|
||||||
end_date: string;
|
end_date: string;
|
||||||
subsidiary: string;
|
subsidiary: string;
|
||||||
|
report_type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CustomUseMutationOptions<
|
export type CustomUseMutationOptions<
|
||||||
|
@ -1,21 +1,219 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Button, message, Upload } from 'antd';
|
import { Button, Input, Table, Tag, DatePicker, Form, Modal, Select, Spin, message, Upload } from 'antd';
|
||||||
|
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||||
|
import type { GetRef } from 'antd';
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import {
|
||||||
|
DownloadOutlined, CheckCircleOutlined,
|
||||||
|
ClockCircleFilled,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
const { Sider, Content } = Layout;
|
||||||
|
import { baseURL } from "request/api";
|
||||||
|
import { Viewer } from '@react-pdf-viewer/core';
|
||||||
import { SbtPageHeader } from 'components/page-header';
|
import { SbtPageHeader } from 'components/page-header';
|
||||||
import { useState } from 'react';
|
|
||||||
import { UploadOutlined } from '@ant-design/icons';
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import type { GetProp, UploadFile, UploadProps } from 'antd';
|
import type { GetProp, UploadFile, UploadProps } from 'antd';
|
||||||
import { JsonView, allExpanded, defaultStyles } from 'react-json-view-lite';
|
|
||||||
import 'react-json-view-lite/dist/index.css';
|
import 'react-json-view-lite/dist/index.css';
|
||||||
import { baseURL } from "request/api"
|
|
||||||
|
|
||||||
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
||||||
|
|
||||||
|
const ENABLE_REVIEW = true;
|
||||||
|
|
||||||
|
|
||||||
|
// Import the styles
|
||||||
|
import '@react-pdf-viewer/core/lib/styles/index.css';
|
||||||
|
|
||||||
|
|
||||||
|
const siderStyle: React.CSSProperties = {
|
||||||
|
backgroundColor: '#fafafa',
|
||||||
|
padding: 10,
|
||||||
|
width: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const StyledTable = styled(Table)`
|
||||||
|
& .sbt-table-cell {
|
||||||
|
padding: 4px!important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type InputRef = GetRef<typeof Input>;
|
||||||
|
type FormInstance<T> = GetRef<typeof Form<T>>;
|
||||||
|
|
||||||
|
const EditableContext = React.createContext<FormInstance<any> | null>(null);
|
||||||
|
|
||||||
|
interface Item {
|
||||||
|
key: string;
|
||||||
|
accuracy: number;
|
||||||
|
revised: string;
|
||||||
|
action: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditableRowProps {
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<Form form={form} component={false}>
|
||||||
|
<EditableContext.Provider value={form}>
|
||||||
|
<tr {...props} />
|
||||||
|
</EditableContext.Provider>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EditableCellProps {
|
||||||
|
title: React.ReactNode;
|
||||||
|
editable: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
dataIndex: keyof Item;
|
||||||
|
record: Item;
|
||||||
|
handleSave: (record: Item) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const EditableCell: React.FC<EditableCellProps> = ({
|
||||||
|
title,
|
||||||
|
editable,
|
||||||
|
children,
|
||||||
|
dataIndex,
|
||||||
|
record,
|
||||||
|
handleSave,
|
||||||
|
...restProps
|
||||||
|
}) => {
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const inputRef = useRef<InputRef>(null);
|
||||||
|
const form = useContext(EditableContext)!;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editing) {
|
||||||
|
inputRef.current!.focus();
|
||||||
|
}
|
||||||
|
}, [editing]);
|
||||||
|
|
||||||
|
const toggleEdit = () => {
|
||||||
|
setEditing(!editing);
|
||||||
|
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
|
||||||
|
toggleEdit();
|
||||||
|
handleSave({ ...record, ...values });
|
||||||
|
} catch (errInfo) {
|
||||||
|
console.log('Save failed:', errInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let childNode = children;
|
||||||
|
|
||||||
|
if (editable) {
|
||||||
|
childNode = editing ? (
|
||||||
|
<Form.Item
|
||||||
|
style={{ margin: 0 }}
|
||||||
|
name={dataIndex}
|
||||||
|
>
|
||||||
|
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
|
||||||
|
</Form.Item>
|
||||||
|
) : (
|
||||||
|
<div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <td {...restProps}>{childNode}</td>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// type EditableTableProps = Parameters<typeof Table>[0];
|
||||||
|
|
||||||
|
const FileCard = ({ file, isSelected, onClick, setIsReasonModalOpen }) => {
|
||||||
|
const fileName = file["File Name"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
width: '200px',
|
||||||
|
backgroundColor: isSelected ? '#d4ecff' : '#fff',
|
||||||
|
padding: '4px 8px',
|
||||||
|
marginRight: '4px',
|
||||||
|
marginTop: '2px',
|
||||||
|
position: 'relative',
|
||||||
|
height: '100px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}} onClick={onClick}>
|
||||||
|
<div>
|
||||||
|
<span style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#333',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
padding: '4px 8px',
|
||||||
|
cursor: 'default',
|
||||||
|
}}>{file["Doc Type"].toUpperCase()}</span>
|
||||||
|
<span style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#aaa',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
padding: '4px 8px',
|
||||||
|
cursor: 'default',
|
||||||
|
maxWidth: '50px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
}}>
|
||||||
|
{fileName ? fileName.substring(0, 25).replace("temp_", "") : fileName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
padding: '4px',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 2,
|
||||||
|
right: 2,
|
||||||
|
}}>
|
||||||
|
{/* <Button style={{
|
||||||
|
margin: '4px 2px',
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setIsReasonModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Review
|
||||||
|
</Button> */}
|
||||||
|
<Button style={{
|
||||||
|
margin: '4px 2px',
|
||||||
|
}} onClick={() => {
|
||||||
|
const downloadUrl = file["File URL"];
|
||||||
|
window.open(downloadUrl, '_blank');
|
||||||
|
}}>
|
||||||
|
<DownloadOutlined />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRequest = async (id) => {
|
||||||
|
const token = localStorage.getItem('sbt-token') || '';
|
||||||
|
const response = await fetch(`${baseURL}/ctel/request/${id}/`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Authorization": `${JSON.parse(token)}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return await (await response.json()).subscription_requests[0];
|
||||||
|
};
|
||||||
|
|
||||||
const InferencePage = () => {
|
const InferencePage = () => {
|
||||||
const [invoiceFiles, setInvoiceFiles] = useState<UploadFile[]>([]);
|
const [invoiceFiles, setInvoiceFiles] = useState<UploadFile[]>([]);
|
||||||
const [imei1Files, setImei1Files] = useState<UploadFile[]>([]);
|
const [imei1Files, setImei1Files] = useState<UploadFile[]>([]);
|
||||||
const [imei2Files, setImei2Files] = useState<UploadFile[]>([]);
|
const [imei2Files, setImei2Files] = useState<UploadFile[]>([]);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [jsonData, setJsonData] = useState({});
|
const [responseData, setResponseData] = useState(null);
|
||||||
const [finishedProcessing, setFinishedProcessing] = useState(false);
|
const [finishedProcessing, setFinishedProcessing] = useState(false);
|
||||||
|
|
||||||
const handleUpload = () => {
|
const handleUpload = () => {
|
||||||
@ -31,9 +229,7 @@ const InferencePage = () => {
|
|||||||
}
|
}
|
||||||
formData.append('is_test_request', 'true');
|
formData.append('is_test_request', 'true');
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
setJsonData({
|
setResponseData(null);
|
||||||
"message": "Please wait..."
|
|
||||||
})
|
|
||||||
const token = localStorage.getItem('sbt-token') || '';
|
const token = localStorage.getItem('sbt-token') || '';
|
||||||
fetch(`${baseURL}/ctel/images/process_sync/`, {
|
fetch(`${baseURL}/ctel/images/process_sync/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -44,31 +240,285 @@ const InferencePage = () => {
|
|||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setJsonData(data);
|
if (data["status"] != "200") {
|
||||||
|
setResponseData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loadRequestById(data["request_id"]);
|
||||||
|
}, 2000);
|
||||||
setFinishedProcessing(true);
|
setFinishedProcessing(true);
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
message.success('Upload successfully.');
|
message.success('Upload successfully.');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
message.error('Upload failed.');
|
message.error('Upload failed.');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setUploading(false);
|
setUploading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isReasonModalOpen, setIsReasonModalOpen] = useState(false);
|
||||||
|
const [selectedFileId, setSelectedFileId] = useState(0);
|
||||||
|
const [selectedFileData, setSelectedFileData] = useState(null);
|
||||||
|
const [selectedFileName, setSelectedFileName] = useState(null);
|
||||||
|
const [currentRequest, setCurrentRequest] = useState(null);
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
|
||||||
|
const setAndLoadSelectedFile = async (requestData, index) => {
|
||||||
|
setSelectedFileId(index);
|
||||||
|
if (!requestData["Files"][index]) {
|
||||||
|
setSelectedFileData("FAILED_TO_LOAD_FILE");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const fileName = requestData["Files"][index]["File Name"];
|
||||||
|
const fileURL = requestData["Files"][index]["File URL"];
|
||||||
|
const response = await fetch(fileURL);
|
||||||
|
if (response.status === 200) {
|
||||||
|
setSelectedFileName(fileName);
|
||||||
|
setSelectedFileData(fileURL);
|
||||||
|
console.log("Loading file: " + fileName);
|
||||||
|
console.log("URL: " + fileURL);
|
||||||
|
} else {
|
||||||
|
setSelectedFileData("FAILED_TO_LOAD_FILE");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadRequestById = (requestID) => {
|
||||||
|
setLoading(true);
|
||||||
|
const requestData = fetchRequest(requestID);
|
||||||
|
requestData.then(async (data) => {
|
||||||
|
if (data) setCurrentRequest(data);
|
||||||
|
const predicted = (data && data["Predicted Result"]) ? data["Predicted Result"] : {};
|
||||||
|
const revised = (data && data["Reviewed Result"]) ? data["Reviewed Result"] : {};
|
||||||
|
const keys = Object.keys(predicted);
|
||||||
|
const tableRows = [];
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let instance = {};
|
||||||
|
instance["key"] = keys[i];
|
||||||
|
instance["predicted"] = predicted[keys[i]];
|
||||||
|
instance["revised"] = revised[keys[i]];
|
||||||
|
tableRows.push(instance);
|
||||||
|
}
|
||||||
|
setDataSource(tableRows);
|
||||||
|
setLoading(false);
|
||||||
|
setAndLoadSelectedFile(data, 0);
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
body: {
|
||||||
|
row: EditableRow,
|
||||||
|
cell: EditableCell,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// "Key", "Accuracy", "Revised"
|
||||||
|
interface DataType {
|
||||||
|
key: string;
|
||||||
|
accuracy: number;
|
||||||
|
revised: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRevisedData = async (newRevisedData: any) => {
|
||||||
|
const requestID = currentRequest.RequestID;
|
||||||
|
const token = localStorage.getItem('sbt-token') || '';
|
||||||
|
await fetch(`${baseURL}/ctel/request/${requestID}/`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Authorization": `${JSON.parse(token)}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
"reviewed_result": newRevisedData
|
||||||
|
}),
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
message.error("Could not update revised data");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (row: DataType) => {
|
||||||
|
const newData = [...dataSource];
|
||||||
|
const index = newData.findIndex((item) => row.key === item.key);
|
||||||
|
const item = newData[index];
|
||||||
|
newData.splice(index, 1, {
|
||||||
|
...item,
|
||||||
|
...row,
|
||||||
|
});
|
||||||
|
setDataSource(newData);
|
||||||
|
const newRevisedData = {};
|
||||||
|
for (let i = 0; i < newData.length; i++) {
|
||||||
|
newRevisedData[newData[i].key] = newData[i].revised;
|
||||||
|
}
|
||||||
|
updateRevisedData(newRevisedData).then(() => {
|
||||||
|
// "[Is Reviewed]" => true
|
||||||
|
setCurrentRequest({
|
||||||
|
...currentRequest,
|
||||||
|
["Is Reviewed"]: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitRevisedData = async () => {
|
||||||
|
const newData = [...dataSource];
|
||||||
|
const newRevisedData = {};
|
||||||
|
for (let i = 0; i < newData.length; i++) {
|
||||||
|
if (newData[i].revised === "<empty>") {
|
||||||
|
newData[i].revised = null;
|
||||||
|
}
|
||||||
|
if (typeof(newData[i].revised) === "string") {
|
||||||
|
newData[i].revised = newData[i].revised.trim();
|
||||||
|
}
|
||||||
|
if (newData[i].revised === "" || newData[i].revised === null || newData[i].revised === undefined) {
|
||||||
|
newData[i].revised = null;
|
||||||
|
}
|
||||||
|
if ((newData[i].key === "imei_number" || newData[i].key === "purchase_date") && typeof(newData[i].revised) === "string") {
|
||||||
|
// Convert to list
|
||||||
|
newData[i].revised = new Array(newData[i].revised.split(","));
|
||||||
|
}
|
||||||
|
if (Array.isArray(newData[i].revised)) {
|
||||||
|
// Trim all empty strings
|
||||||
|
for (let j = 0; j < newData[i].revised.length; j++) {
|
||||||
|
if (typeof(newData[i].revised[j]) === "string") {
|
||||||
|
newData[i].revised[j] = newData[i].revised[j].trim();
|
||||||
|
}
|
||||||
|
if (newData[i].revised[j] === "<empty>") {
|
||||||
|
newData[i].revised[j] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRevisedData[newData[i].key] = newData[i].revised;
|
||||||
|
}
|
||||||
|
updateRevisedData(newRevisedData).then(() => {
|
||||||
|
// "[Is Reviewed]" => true
|
||||||
|
setCurrentRequest({
|
||||||
|
...currentRequest,
|
||||||
|
["Is Reviewed"]: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const defaultColumns = [
|
||||||
|
{
|
||||||
|
title: 'Key',
|
||||||
|
dataIndex: 'key',
|
||||||
|
key: 'key',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Predicted',
|
||||||
|
dataIndex: 'predicted',
|
||||||
|
key: 'predicted',
|
||||||
|
render: (text) => {
|
||||||
|
if (!text) return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
const displayedContent = text;
|
||||||
|
if (typeof(displayedContent) === "string") {
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
} else if (typeof(displayedContent) === "object") {
|
||||||
|
if (displayedContent.length === 0) {
|
||||||
|
return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
}
|
||||||
|
// Set all empty values to "<empty>"
|
||||||
|
for (const key in displayedContent) {
|
||||||
|
if (!displayedContent[key]) {
|
||||||
|
displayedContent[key] = "<empty>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent.join(", ")}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: (<div style={{
|
||||||
|
width: 120,
|
||||||
|
display: 'flex',
|
||||||
|
lineHeight: '32px',
|
||||||
|
marginLeft: 10,
|
||||||
|
}}>Revised
|
||||||
|
{ENABLE_REVIEW && <Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!dataSource || !dataSource.length) return;
|
||||||
|
setDataSource(dataSource.map(item => {
|
||||||
|
item.revised = item.predicted;
|
||||||
|
return item;
|
||||||
|
}));
|
||||||
|
setTimeout(() => {
|
||||||
|
submitRevisedData();
|
||||||
|
}, 1000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Copy Predicted
|
||||||
|
</Button>}
|
||||||
|
</div>),
|
||||||
|
dataIndex: 'revised',
|
||||||
|
key: 'revised',
|
||||||
|
editable: ENABLE_REVIEW,
|
||||||
|
render: (text) => {
|
||||||
|
if (!text) return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
const displayedContent = text;
|
||||||
|
if (typeof(displayedContent) === "string") {
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
} else if (typeof(displayedContent) === "object") {
|
||||||
|
if (displayedContent.length === 0) {
|
||||||
|
return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
}
|
||||||
|
// Set all empty values to "<empty>"
|
||||||
|
for (const key in displayedContent) {
|
||||||
|
if (!displayedContent[key]) {
|
||||||
|
displayedContent[key] = "<empty>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent.join(", ")}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const columns = defaultColumns.map((col) => {
|
||||||
|
if (!col.editable) {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
onCell: (record: DataType) => ({
|
||||||
|
record,
|
||||||
|
editable: col.key != "request_id" && col.editable,
|
||||||
|
dataIndex: col.dataIndex,
|
||||||
|
title: col.title,
|
||||||
|
handleSave,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const fileExtension = selectedFileName ? selectedFileName.split('.').pop() : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div style={{
|
||||||
|
height: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
}}>
|
||||||
<SbtPageHeader
|
<SbtPageHeader
|
||||||
title={t`Inference`}
|
title={t`Inference`}
|
||||||
/>
|
/>
|
||||||
<p>
|
|
||||||
{t`Upload files to process. The requests here will not be used in accuracy or payment calculations.`}
|
|
||||||
</p>
|
|
||||||
<div style={{
|
<div style={{
|
||||||
paddingTop: "0.5rem"
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
paddingTop: "0.5rem",
|
||||||
|
padding: "0.5rem",
|
||||||
|
height: "80px",
|
||||||
}}>
|
}}>
|
||||||
<Upload
|
<Upload
|
||||||
onRemove={(file) => {
|
onRemove={(file) => {
|
||||||
@ -82,11 +532,13 @@ const InferencePage = () => {
|
|||||||
}}
|
}}
|
||||||
fileList={invoiceFiles}
|
fileList={invoiceFiles}
|
||||||
>
|
>
|
||||||
Invoice File: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image/PDF</Button>
|
Invoice: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image/PDF</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
paddingTop: "0.5rem"
|
paddingTop: "0.5rem",
|
||||||
|
padding: "0.5rem",
|
||||||
|
height: "80px",
|
||||||
}}>
|
}}>
|
||||||
<Upload
|
<Upload
|
||||||
onRemove={(file) => {
|
onRemove={(file) => {
|
||||||
@ -100,11 +552,13 @@ const InferencePage = () => {
|
|||||||
}}
|
}}
|
||||||
fileList={imei1Files}
|
fileList={imei1Files}
|
||||||
>
|
>
|
||||||
IMEI File 1: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image</Button>
|
IMEI 1: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
paddingTop: "0.5rem"
|
paddingTop: "0.5rem",
|
||||||
|
padding: "0.5rem",
|
||||||
|
height: "80px",
|
||||||
}}>
|
}}>
|
||||||
<Upload
|
<Upload
|
||||||
onRemove={(file) => {
|
onRemove={(file) => {
|
||||||
@ -118,15 +572,21 @@ const InferencePage = () => {
|
|||||||
}}
|
}}
|
||||||
fileList={imei2Files}
|
fileList={imei2Files}
|
||||||
>
|
>
|
||||||
IMEI File 2: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image</Button>
|
IMEI 2: <Button disabled={finishedProcessing} icon={<UploadOutlined />}>Select Image</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{
|
||||||
|
paddingTop: "0.5rem",
|
||||||
|
padding: "0.5rem",
|
||||||
|
height: "80px",
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}>
|
||||||
{!finishedProcessing && <Button
|
{!finishedProcessing && <Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={handleUpload}
|
onClick={handleUpload}
|
||||||
disabled={imei1Files.length === 0 && imei2Files.length === 0}
|
disabled={imei1Files.length === 0 && imei2Files.length === 0}
|
||||||
loading={uploading}
|
loading={uploading}
|
||||||
style={{ marginTop: 16, marginBottom: 24 }}
|
|
||||||
>
|
>
|
||||||
{uploading ? 'Uploading' : 'Process Data'}
|
{uploading ? 'Uploading' : 'Process Data'}
|
||||||
</Button>}
|
</Button>}
|
||||||
@ -134,20 +594,153 @@ const InferencePage = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFinishedProcessing(false);
|
setFinishedProcessing(false);
|
||||||
setJsonData({});
|
setResponseData(null);
|
||||||
setInvoiceFiles([]);
|
setInvoiceFiles([]);
|
||||||
setImei1Files([]);
|
setImei1Files([]);
|
||||||
setImei2Files([]);
|
setImei2Files([]);
|
||||||
|
setCurrentRequest(null);
|
||||||
|
setDataSource([]);
|
||||||
|
setSelectedFileData(null);
|
||||||
|
setUploading(false);
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: 16, marginBottom: 24 }}
|
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>}
|
</Button>}
|
||||||
<div style={{
|
</div>
|
||||||
paddingTop: "0.5rem"
|
</div>
|
||||||
|
<div
|
||||||
|
style={{ height: '100%', position: 'absolute', top: 0, left: 0, width: '100%', background: "#00000033", zIndex: 1000, display: loading ? 'block' : 'none' }}
|
||||||
|
>
|
||||||
|
<Spin spinning={true} style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
marginTop: -12,
|
||||||
|
marginLeft: -12,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
borderRadius: '50%',
|
||||||
|
}} size='large' />
|
||||||
|
</div>
|
||||||
|
<Layout style={{
|
||||||
|
overflow: 'auto',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
maxWidth: '100%',
|
||||||
|
minHeight: '70%',
|
||||||
|
maxHeight: '70%',
|
||||||
|
display: 'flex',
|
||||||
|
padding: '8px',
|
||||||
}}>
|
}}>
|
||||||
<h3>Result:</h3>
|
<Content style={{
|
||||||
<JsonView data={jsonData} shouldExpandNode={allExpanded} style={defaultStyles} />
|
textAlign: 'center',
|
||||||
|
color: '#fff',
|
||||||
|
backgroundColor: '#efefef',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}>
|
||||||
|
{currentRequest?.Files?.length && <div
|
||||||
|
style={{
|
||||||
|
width: "200px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
flexGrow: 0,
|
||||||
|
}}>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
color: "#333",
|
||||||
|
padding: 10,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 18,
|
||||||
|
textTransform: 'uppercase'
|
||||||
|
}}
|
||||||
|
>Files ({currentRequest?.Files?.length})</h2>
|
||||||
|
{currentRequest?.Files.map((file, index) => (
|
||||||
|
<FileCard key={index} file={file} isSelected={index === selectedFileId} onClick={
|
||||||
|
() => {
|
||||||
|
setAndLoadSelectedFile(currentRequest, index);
|
||||||
|
}
|
||||||
|
} setIsReasonModalOpen={setIsReasonModalOpen} />
|
||||||
|
))}
|
||||||
|
</div>}
|
||||||
|
{selectedFileData && <div style={{
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
flexGrow: 1,
|
||||||
|
height: '100%',
|
||||||
|
}}>
|
||||||
|
{selectedFileData === "FAILED_TO_LOAD_FILE" ? <p style={{ color: "#333" }}></p> : (fileExtension === "pdf" ? (<Viewer
|
||||||
|
fileUrl={selectedFileData}
|
||||||
|
/>) : <div style={{
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
overflow: "auto",
|
||||||
|
}}><img width={"100%"} src={selectedFileData} alt="file" /></div>)}
|
||||||
|
</div>}
|
||||||
|
</Content>
|
||||||
|
<Sider width="400px" style={siderStyle}>
|
||||||
|
<div>
|
||||||
|
<Input size='small' addonBefore="Request ID" style={{ marginBottom: "4px" }} readOnly value={currentRequest ? currentRequest.RequestID : ""} />
|
||||||
|
<Input size='small' addonBefore="Redemption" style={{ marginBottom: "4px" }} readOnly value={currentRequest?.RedemptionID ? currentRequest.RedemptionID : ""} />
|
||||||
|
<Input size='small' addonBefore="Uploaded date" style={{ marginBottom: "4px" }} readOnly value={currentRequest ? currentRequest.created_at : ""} />
|
||||||
|
<Input size='small' addonBefore="Request time" style={{ marginBottom: "4px" }} readOnly value={currentRequest ? currentRequest["Client Request Time (ms)"] : ""} />
|
||||||
|
<Input size='small' addonBefore="Processing time" style={{ marginBottom: "4px" }} readOnly value={currentRequest ? currentRequest["Server Processing Time (ms)"] : ""} />
|
||||||
|
<div style={{ marginBottom: "8px", marginTop: "8px", display: "flex" }}>
|
||||||
|
{currentRequest && (currentRequest["Is Reviewed"] ? <Tag icon={<CheckCircleOutlined />} color="success" style={{ padding: "4px 16px" }}>
|
||||||
|
Reviewed
|
||||||
|
</Tag> : <Tag icon={<ClockCircleFilled />} color="warning" style={{ padding: "4px 16px" }}>
|
||||||
|
Not Reviewed
|
||||||
|
</Tag>)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Sider>
|
||||||
|
</Layout>
|
||||||
|
<Modal
|
||||||
|
title={t`Review`}
|
||||||
|
open={isReasonModalOpen}
|
||||||
|
width={700}
|
||||||
|
onOk={
|
||||||
|
() => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCancel={
|
||||||
|
() => {
|
||||||
|
setIsReasonModalOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
style={{
|
||||||
|
marginTop: 30,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name='reason'
|
||||||
|
label={t`Reason for bad quality:`}
|
||||||
|
style={{
|
||||||
|
marginBottom: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
placeholder='Select a reason'
|
||||||
|
style={{ width: 200 }}
|
||||||
|
options={[
|
||||||
|
{ value: 'invalid_image', label: t`Invalid image` },
|
||||||
|
{ value: 'missing_information', label: t`Missing information` },
|
||||||
|
{ value: 'too_blurry_text', label: t`Too blurry text` },
|
||||||
|
{ value: 'too_small_text', label: t`Too small text` },
|
||||||
|
{ value: 'handwritten', label: t`Handwritten` },
|
||||||
|
{ value: 'recheck', label: t`Recheck` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
<StyledTable components={components}
|
||||||
|
rowClassName={() => 'editable-row'}
|
||||||
|
bordered dataSource={dataSource} columns={columns}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -10,12 +10,34 @@ import { useState } from 'react';
|
|||||||
export interface ReportFormValues {
|
export interface ReportFormValues {
|
||||||
dateRange: [Dayjs, Dayjs];
|
dateRange: [Dayjs, Dayjs];
|
||||||
subsidiary: string;
|
subsidiary: string;
|
||||||
|
reportType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_SUBSIDIARY_OPTIONS = [
|
||||||
|
{ value: 'SEAO', label: 'SEAO' },
|
||||||
|
{ value: 'SEAU', label: 'SEAU' },
|
||||||
|
{ value: 'SESP', label: 'SESP' },
|
||||||
|
{ value: 'SME', label: 'SME' },
|
||||||
|
{ value: 'SEPCO', label: 'SEPCO' },
|
||||||
|
{ value: 'TSE', label: 'TSE' },
|
||||||
|
{ value: 'SEIN', label: 'SEIN' },
|
||||||
|
];
|
||||||
|
|
||||||
const ReportsPage = () => {
|
const ReportsPage = () => {
|
||||||
const [form] = Form.useForm<ReportFormValues>();
|
const [form] = Form.useForm<ReportFormValues>();
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const makeReportMutation = useMakeReport();
|
const makeReportMutation = useMakeReport();
|
||||||
|
const [subsidiaryOptions, setSubsidiaryOptions] = useState(DEFAULT_SUBSIDIARY_OPTIONS);
|
||||||
|
|
||||||
|
const onReportTypeChange = (value: string) => {
|
||||||
|
if (value === 'billing') {
|
||||||
|
setSubsidiaryOptions([DEFAULT_SUBSIDIARY_OPTIONS[0]]);
|
||||||
|
form.setFieldValue('subsidiary', 'SEAO');
|
||||||
|
} else if (value === 'payment') {
|
||||||
|
} else {
|
||||||
|
setSubsidiaryOptions(DEFAULT_SUBSIDIARY_OPTIONS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const showModal = () => {
|
const showModal = () => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
@ -30,10 +52,10 @@ const ReportsPage = () => {
|
|||||||
end_date: values.dateRange[1].format('YYYY-MM-DD'),
|
end_date: values.dateRange[1].format('YYYY-MM-DD'),
|
||||||
start_date: values.dateRange[0].format('YYYY-MM-DD'),
|
start_date: values.dateRange[0].format('YYYY-MM-DD'),
|
||||||
subsidiary: values.subsidiary,
|
subsidiary: values.subsidiary,
|
||||||
|
report_type: values.reportType,
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!!data && data?.report_id) {
|
if (!!data && data?.report_id) {
|
||||||
form.resetFields();
|
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
@ -107,18 +129,33 @@ const ReportsPage = () => {
|
|||||||
message: 'Please select a subsidiary',
|
message: 'Please select a subsidiary',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
initialValue={'SEAO'}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
placeholder='Select a subsidiary'
|
placeholder='Select a subsidiary'
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
|
options={subsidiaryOptions}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name='reportType'
|
||||||
|
label={t`Report Type`}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please select a type',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
initialValue={'accuracy'}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
onSelect={onReportTypeChange}
|
||||||
|
placeholder='Select a report type'
|
||||||
|
style={{ width: 200 }}
|
||||||
options={[
|
options={[
|
||||||
{ value: 'SEAO', label: 'SEAO' },
|
{ value: 'billing', label: 'Billing' },
|
||||||
{ value: 'SEAU', label: 'SEAU' },
|
{ value: 'accuracy', label: 'Accuracy' },
|
||||||
{ value: 'SESP', label: 'SESP' },
|
|
||||||
{ value: 'SME', label: 'SME' },
|
|
||||||
{ value: 'SEPCO', label: 'SEPCO' },
|
|
||||||
{ value: 'TSE', label: 'TSE' },
|
|
||||||
{ value: 'SEIN', label: 'SEIN' },
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -290,14 +290,14 @@ const ReviewPage = () => {
|
|||||||
|
|
||||||
const loadCurrentRequest = (requestIndex) => {
|
const loadCurrentRequest = (requestIndex) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
fetchAllRequests(filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, requestIndex, 1).then((data) => {
|
fetchAllRequests(filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, requestIndex, 2).then((data) => {
|
||||||
setRequests(data?.subscription_requests);
|
setRequests(data?.subscription_requests);
|
||||||
setHasNextRequest(data?.subscription_requests.length > 1);
|
setHasNextRequest(data?.subscription_requests.length > 1);
|
||||||
setTotalPages(data?.page?.total_requests);
|
setTotalPages(data?.page?.total_requests);
|
||||||
const requestData = fetchRequest(data?.subscription_requests[0].RequestID);
|
const requestData = fetchRequest(data?.subscription_requests[0].RequestID);
|
||||||
requestData.then(async (data) => {
|
requestData.then(async (data) => {
|
||||||
if (data) setCurrentRequest(data);
|
if (data) setCurrentRequest(data);
|
||||||
const predicted = (data && data["Reviewed Result"]) ? data["Reviewed Result"] : {};
|
const predicted = (data && data["Predicted Result"]) ? data["Predicted Result"] : {};
|
||||||
const submitted = (data && data["Feedback Result"]) ? data["Feedback Result"] : {};
|
const submitted = (data && data["Feedback Result"]) ? data["Feedback Result"] : {};
|
||||||
const revised = (data && data["Reviewed Result"]) ? data["Reviewed Result"] : {};
|
const revised = (data && data["Reviewed Result"]) ? data["Reviewed Result"] : {};
|
||||||
const keys = Object.keys(predicted);
|
const keys = Object.keys(predicted);
|
||||||
@ -430,9 +430,32 @@ const ReviewPage = () => {
|
|||||||
const newData = [...dataSource];
|
const newData = [...dataSource];
|
||||||
const newRevisedData = {};
|
const newRevisedData = {};
|
||||||
for (let i = 0; i < newData.length; i++) {
|
for (let i = 0; i < newData.length; i++) {
|
||||||
|
if (newData[i].revised === "<empty>") {
|
||||||
|
newData[i].revised = null;
|
||||||
|
}
|
||||||
|
if (typeof(newData[i].revised) === "string") {
|
||||||
|
newData[i].revised = newData[i].revised.trim();
|
||||||
|
}
|
||||||
|
if (newData[i].revised === "" || newData[i].revised === null || newData[i].revised === undefined) {
|
||||||
|
newData[i].revised = null;
|
||||||
|
}
|
||||||
|
if ((newData[i].key === "imei_number" || newData[i].key === "purchase_date") && typeof(newData[i].revised) === "string") {
|
||||||
|
// Convert to list
|
||||||
|
newData[i].revised = new Array(newData[i].revised.split(","));
|
||||||
|
}
|
||||||
|
if (Array.isArray(newData[i].revised)) {
|
||||||
|
// Trim all empty strings
|
||||||
|
for (let j = 0; j < newData[i].revised.length; j++) {
|
||||||
|
if (typeof(newData[i].revised[j]) === "string") {
|
||||||
|
newData[i].revised[j] = newData[i].revised[j].trim();
|
||||||
|
}
|
||||||
|
if (newData[i].revised[j] === "<empty>") {
|
||||||
|
newData[i].revised[j] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
newRevisedData[newData[i].key] = newData[i].revised;
|
newRevisedData[newData[i].key] = newData[i].revised;
|
||||||
}
|
}
|
||||||
console.log(currentRequest)
|
|
||||||
updateRevisedData(newRevisedData).then(() => {
|
updateRevisedData(newRevisedData).then(() => {
|
||||||
// "[Is Reviewed]" => true
|
// "[Is Reviewed]" => true
|
||||||
setCurrentRequest({
|
setCurrentRequest({
|
||||||
@ -443,6 +466,7 @@ const ReviewPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const defaultColumns = [
|
const defaultColumns = [
|
||||||
{
|
{
|
||||||
title: 'Key',
|
title: 'Key',
|
||||||
@ -454,11 +478,49 @@ const ReviewPage = () => {
|
|||||||
title: 'Predicted',
|
title: 'Predicted',
|
||||||
dataIndex: 'predicted',
|
dataIndex: 'predicted',
|
||||||
key: 'predicted',
|
key: 'predicted',
|
||||||
|
render: (text) => {
|
||||||
|
if (!text) return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
const displayedContent = text;
|
||||||
|
if (typeof(displayedContent) === "string") {
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
} else if (typeof(displayedContent) === "object") {
|
||||||
|
if (displayedContent.length === 0) {
|
||||||
|
return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
}
|
||||||
|
// Set all empty values to "<empty>"
|
||||||
|
for (const key in displayedContent) {
|
||||||
|
if (!displayedContent[key]) {
|
||||||
|
displayedContent[key] = "<empty>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent.join(", ")}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Submitted',
|
title: 'Submitted',
|
||||||
dataIndex: 'submitted',
|
dataIndex: 'submitted',
|
||||||
key: 'submitted',
|
key: 'submitted',
|
||||||
|
render: (text) => {
|
||||||
|
if (!text) return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
const displayedContent = text;
|
||||||
|
if (typeof(displayedContent) === "string") {
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
} else if (typeof(displayedContent) === "object") {
|
||||||
|
if (displayedContent.length === 0) {
|
||||||
|
return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
}
|
||||||
|
// Set all empty values to "<empty>"
|
||||||
|
for (const key in displayedContent) {
|
||||||
|
if (!displayedContent[key]) {
|
||||||
|
displayedContent[key] = "<empty>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent.join(", ")}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: (<div style={{
|
title: (<div style={{
|
||||||
@ -499,11 +561,24 @@ const ReviewPage = () => {
|
|||||||
dataIndex: 'revised',
|
dataIndex: 'revised',
|
||||||
key: 'revised',
|
key: 'revised',
|
||||||
editable: true,
|
editable: true,
|
||||||
render: (text, record) => {
|
render: (text) => {
|
||||||
return <div style={{
|
if (!text) return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
color: '#000',
|
const displayedContent = text;
|
||||||
fontWeight: 'bold',
|
if (typeof(displayedContent) === "string") {
|
||||||
}}>{text}</div>;
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
|
} else if (typeof(displayedContent) === "object") {
|
||||||
|
if (displayedContent.length === 0) {
|
||||||
|
return <span style={{ color: '#888' }}>{"<empty>"}</span>;
|
||||||
|
}
|
||||||
|
// Set all empty values to "<empty>"
|
||||||
|
for (const key in displayedContent) {
|
||||||
|
if (!displayedContent[key]) {
|
||||||
|
displayedContent[key] = "<empty>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent.join(", ")}</span>;
|
||||||
|
}
|
||||||
|
return <span style={{ color: '#000000' }}>{displayedContent}</span>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -11,7 +11,9 @@ const environment = process.env.NODE_ENV;
|
|||||||
const AXIOS_TIMEOUT_MS = 30 * 60 * 1000; // This config sastified long-live upload file request
|
const AXIOS_TIMEOUT_MS = 30 * 60 * 1000; // This config sastified long-live upload file request
|
||||||
const EXPIRED_PASSWORD_SIGNAL = 'expired_password';
|
const EXPIRED_PASSWORD_SIGNAL = 'expired_password';
|
||||||
|
|
||||||
export const baseURL = environment === 'development' ? 'http://42.96.42.13:9881/api' : '/api';
|
// export const baseURL = environment === 'development' ? 'http://42.96.42.13:9881/api' : '/api';
|
||||||
|
export const baseURL = '/api';
|
||||||
|
|
||||||
|
|
||||||
export const API = axios.create({
|
export const API = axios.create({
|
||||||
timeout: AXIOS_TIMEOUT_MS,
|
timeout: AXIOS_TIMEOUT_MS,
|
||||||
|
@ -32,12 +32,13 @@ export async function getReportDetailList(params: ReportDetailListParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function makeReport(params: MakeReportParams) {
|
export async function makeReport(params: MakeReportParams) {
|
||||||
const { end_date, start_date, subsidiary } = params;
|
const { end_date, start_date, subsidiary, report_type } = params;
|
||||||
try {
|
try {
|
||||||
const response = await API.post<MakeReportResponse>(`/ctel/make_report/`, {
|
const response = await API.post<MakeReportResponse>(`/ctel/make_report/`, {
|
||||||
start_date: start_date,
|
start_date: start_date,
|
||||||
end_date: end_date,
|
end_date: end_date,
|
||||||
subsidiary: subsidiary,
|
subsidiary: subsidiary,
|
||||||
|
report_type: report_type,
|
||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -9,7 +9,7 @@ import { PrivateRoute, PublicRoute } from './guard-route';
|
|||||||
|
|
||||||
const LoginPage = React.lazy(() => import('pages/login'));
|
const LoginPage = React.lazy(() => import('pages/login'));
|
||||||
const DashboardPage = React.lazy(() => import('pages/dashboard'));
|
const DashboardPage = React.lazy(() => import('pages/dashboard'));
|
||||||
const InferencePage = React.lazy(() => import('pages/inference'));
|
const InferencePage = React.lazy(() => import('pages/inference/index'));
|
||||||
|
|
||||||
const ReviewsPage = React.lazy(() => import('pages/reviews'));
|
const ReviewsPage = React.lazy(() => import('pages/reviews'));
|
||||||
const ReportsPage = React.lazy(() => import('pages/reports'));
|
const ReportsPage = React.lazy(() => import('pages/reports'));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export const formatPercent = (value: number, floatingPoint: number = 1) => {
|
export const formatPercent = (value: any, floatingPoint: number = 1, maskZero: boolean = false) => {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined || (value === 0 && maskZero)) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
if (value < 100.0) {
|
if (value < 100.0) {
|
||||||
@ -8,11 +8,19 @@ export const formatPercent = (value: number, floatingPoint: number = 1) => {
|
|||||||
return value.toFixed(floatingPoint);
|
return value.toFixed(floatingPoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatNumber = (value: any, floatingPoint: number = 1, maskZero: boolean = false) => {
|
||||||
|
if (value === null || value === undefined || (value === 0 && maskZero)) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return value.toFixed(floatingPoint);
|
||||||
|
};
|
||||||
|
|
||||||
export const ensureMin = (
|
export const ensureMin = (
|
||||||
value: number,
|
value: number,
|
||||||
min: number,
|
min: number,
|
||||||
skipZero: boolean = true,
|
skipZero: boolean = true,
|
||||||
) => {
|
) => {
|
||||||
|
if (formatPercent(value) == '-') return false;
|
||||||
if (skipZero && value === 0) {
|
if (skipZero && value === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -27,6 +35,7 @@ export const ensureMax = (
|
|||||||
max: number,
|
max: number,
|
||||||
skipZero: boolean = true,
|
skipZero: boolean = true,
|
||||||
) => {
|
) => {
|
||||||
|
if (formatPercent(value) == '-') return false;
|
||||||
if (skipZero && value === 0) {
|
if (skipZero && value === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,11 @@ export default defineConfig(({ mode = 'development' }) => {
|
|||||||
viteTsconfigPaths(),
|
viteTsconfigPaths(),
|
||||||
svgrPlugin(),
|
svgrPlugin(),
|
||||||
],
|
],
|
||||||
// server: {
|
server: {
|
||||||
// open: true,
|
open: true,
|
||||||
// port: configPort(env),
|
port: configPort(env),
|
||||||
// proxy: configProxy(env),
|
proxy: configProxy(env),
|
||||||
// },
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env': env,
|
'process.env': env,
|
||||||
},
|
},
|
||||||
|
@ -89,12 +89,12 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db-sbt:
|
db-sbt:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
# command: sh -c "chmod -R 777 /app; sleep 5; python manage.py collectstatic --no-input &&
|
command: sh -c "chmod -R 777 /app; sleep 5; python manage.py collectstatic --no-input &&
|
||||||
# python manage.py makemigrations &&
|
python manage.py makemigrations &&
|
||||||
# python manage.py migrate &&
|
python manage.py migrate &&
|
||||||
# python manage.py compilemessages &&
|
python manage.py compilemessages &&
|
||||||
# gunicorn fwd.asgi:application -k uvicorn.workers.UvicornWorker --timeout 300 -b 0.0.0.0:9000" # pre-makemigrations on prod
|
gunicorn fwd.asgi:application -k uvicorn.workers.UvicornWorker --timeout 300 -b 0.0.0.0:9000" # pre-makemigrations on prod
|
||||||
command: bash -c "tail -f > /dev/null"
|
# command: bash -c "tail -f > /dev/null"
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
@ -179,8 +179,8 @@ services:
|
|||||||
- ./cope2n-api:/app
|
- ./cope2n-api:/app
|
||||||
|
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
# command: sh -c "celery -A fwd_api.celery_worker.worker worker -l INFO -c 5"
|
command: sh -c "celery -A fwd_api.celery_worker.worker worker -l INFO -c 5"
|
||||||
command: bash -c "tail -f > /dev/null"
|
# command: bash -c "tail -f > /dev/null"
|
||||||
|
|
||||||
# Back-end persistent
|
# Back-end persistent
|
||||||
db-sbt:
|
db-sbt:
|
||||||
|
Loading…
Reference in New Issue
Block a user