import 'dart:ui'; import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/core/model/labs/LabOrderResult.dart'; import 'package:diplomaticquarterapp/core/model/labs/lab_result.dart'; import 'package:diplomaticquarterapp/core/model/labs/patient_lab_orders.dart'; import 'package:diplomaticquarterapp/core/model/labs/patient_lab_special_result.dart'; import 'package:diplomaticquarterapp/core/model/labs/request_patient_lab_special_result.dart'; import 'package:diplomaticquarterapp/core/model/labs/request_send_lab_report_email.dart'; import 'package:diplomaticquarterapp/core/service/base_service.dart'; import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; import 'package:diplomaticquarterapp/widgets/data_display/medical/LabResult/lab_result_graph.dart'; class LabsService extends BaseService { List patientLabOrdersList = []; String labReportPDF = ""; Future getPatientLabOrdersList() async { hasError = false; Map body = Map(); body['isDentalAllowedBackend'] = false; await baseAppClient.post(GET_Patient_LAB_ORDERS, onSuccess: (dynamic response, int statusCode) { patientLabOrdersList.clear(); response['ListPLO'].forEach((hospital) { patientLabOrdersList.add(PatientLabOrders.fromJson(hospital)); }); }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); } RequestPatientLabSpecialResult _requestPatientLabSpecialResult = RequestPatientLabSpecialResult(); List patientLabSpecialResult = []; List labResultList = []; List labOrdersResultsList = []; Future getLaboratoryResult( {String? projectID, int? clinicID, String? invoiceNo, String? invoiceType, String? orderNo, String? setupID, bool? isVidaPlus}) async { hasError = false; _requestPatientLabSpecialResult.projectID = projectID; _requestPatientLabSpecialResult.clinicID = clinicID; _requestPatientLabSpecialResult.invoiceNo = isVidaPlus! ? "0" : invoiceNo; _requestPatientLabSpecialResult.invoiceNoVP = isVidaPlus ? invoiceNo : "0"; _requestPatientLabSpecialResult.invoiceType = invoiceType ?? ""; _requestPatientLabSpecialResult.orderNo = orderNo; _requestPatientLabSpecialResult.setupID = setupID; await baseAppClient.post(GET_Patient_LAB_SPECIAL_RESULT, onSuccess: (dynamic response, int statusCode) { patientLabSpecialResult.clear(); response['ListPLSR'].forEach((hospital) { patientLabSpecialResult.add(PatientLabSpecialResult.fromJson(hospital)); }); }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: _requestPatientLabSpecialResult.toJson()); } Future getPatientLabResult( {PatientLabOrders? patientLabOrder, bool? isVidaPlus}) async { hasError = false; Map body = Map(); body['InvoiceNo_VP'] = isVidaPlus! ? patientLabOrder!.invoiceNo : "0"; body['InvoiceNo'] = isVidaPlus ? "0" : patientLabOrder!.invoiceNo; body['InvoiceType'] = patientLabOrder!.invoiceType; body['OrderNo'] = patientLabOrder!.orderNo; body['isDentalAllowedBackend'] = false; body['SetupID'] = patientLabOrder.setupID; body['ProjectID'] = patientLabOrder.projectID; body['ClinicID'] = patientLabOrder.clinicID; await baseAppClient.post(GET_Patient_LAB_RESULT, onSuccess: (dynamic response, int statusCode) { patientLabSpecialResult.clear(); labResultList.clear(); response['ListPLR'].forEach((lab) { labResultList.add( LabResult.fromJson(lab), ); }); }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); } Future generateCovidLabReport( LabResult covidLabResult, String isOutsideKSA) async { hasError = false; Map body = Map(); body['To'] = user.emailAddress; body['OrderNo'] = covidLabResult.orderNo; body['OrderLineItemNo'] = covidLabResult.orderLineItemNo; body['LineItemNo'] = covidLabResult.resultValueBasedLineItemNo; body['CertificateFormat'] = 5; body['GeneratedBy'] = 102; body['ShowPassportNumber'] = isOutsideKSA; body['isDentalAllowedBackend'] = false; body['SetupID'] = covidLabResult.setupID; body['ProjectID'] = covidLabResult.projectID; dynamic localRes; await baseAppClient.post(SEND_COVID_LAB_RESULT_EMAIL, onSuccess: (dynamic response, int statusCode) { localRes = response; }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); return Future.value(localRes); } Future updateCovidPassportNumber(String passportNumber) async { hasError = false; Map body = Map(); body['PassportNo'] = passportNumber; dynamic localRes; await baseAppClient.post(COVID_PASSPORT_UPDATE, onSuccess: (dynamic response, int statusCode) { localRes = response; }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); return Future.value(localRes); } Future updateWorkplaceName( String workplaceName, String workplaceNameAR, String occupation, String occupationAR, int requestNumber, String setupID, int projectID) async { // Future updateWorkplaceName(String workplaceName, String workplaceNameAR, int requestNumber, String setupID, int projectID) async { hasError = false; Map body = Map(); body['Placeofwork'] = workplaceName; body['PlaceofworkAr'] = workplaceNameAR; body['Occupation'] = occupation; body['OccupationAr'] = occupationAR; body['Req_ID'] = requestNumber; body['TargetSetupID'] = setupID; body['ProjectID'] = projectID; body['UserID'] = 102; body['RequestType'] = 1; dynamic localRes; await baseAppClient.post(UPDATE_WORKPLACE_NAME, onSuccess: (dynamic response, int statusCode) { localRes = response; }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); return Future.value(localRes); } Future getCovidPassportNumber() async { hasError = false; Map body = Map(); dynamic localRes; await baseAppClient.post(GET_PATIENT_PASSPORT_NUMBER, onSuccess: (dynamic response, int statusCode) { localRes = response; }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); return Future.value(localRes); } Future getSickLeaveStatusByAdmissionNo(int projectID, int admissionNo) async { hasError = false; Map body = Map(); body['AdmissionNo'] = admissionNo; body['ProjectID'] = projectID; dynamic localRes; await baseAppClient.post(GET_SICKLEAVE_STATUS_ADMISSION_NO, onSuccess: (dynamic response, int statusCode) { localRes = response; }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); return Future.value(localRes); } Future getPatientLabOrdersResults( {PatientLabOrders? patientLabOrder, String? procedure, bool isVidaPlus = false}) async { hasError = false; Map body = Map(); body['InvoiceNo_VP'] = isVidaPlus ? patientLabOrder!.invoiceNo : "0"; body['InvoiceNo'] = isVidaPlus ? "0" : patientLabOrder!.invoiceNo; body['OrderNo'] = patientLabOrder!.orderNo; body['isDentalAllowedBackend'] = false; body['SetupID'] = patientLabOrder!.setupID; body['ProjectID'] = patientLabOrder.projectID; body['ClinicID'] = patientLabOrder.clinicID; body['Procedure'] = procedure; body['Procedure'] = procedure; body['LanguageID'] = 1; await baseAppClient.post(GET_Patient_LAB_ORDERS_RESULT, onSuccess: (dynamic response, int statusCode) { labOrdersResultsList.clear(); response['ListPLR'].forEach((lab) { labOrdersResultsList.add(LabOrderResult.fromJson(lab)); }); }, onFailure: (String error, int statusCode) { labOrdersResultsList.clear(); hasError = true; super.error = error; }, body: body); } RequestSendLabReportEmail _requestSendLabReportEmail = RequestSendLabReportEmail(); Future sendLabReportEmail( {PatientLabOrders? patientLabOrder, AuthenticatedUser? userObj, bool isVidaPlus = false, bool isDownload = false, int languageID = 1}) async { _requestSendLabReportEmail.projectID = patientLabOrder!.projectID; _requestSendLabReportEmail.invoiceNo = isVidaPlus ? "0" : patientLabOrder.invoiceNo; _requestSendLabReportEmail.invoiceNoVP = isVidaPlus ? patientLabOrder.invoiceNo : "0"; _requestSendLabReportEmail.invoiceType = patientLabOrder.invoiceType; _requestSendLabReportEmail.doctorName = patientLabOrder.doctorName; _requestSendLabReportEmail.clinicName = patientLabOrder.clinicDescription; _requestSendLabReportEmail.patientName = userObj!.firstName! + " " + userObj.lastName!; _requestSendLabReportEmail.patientIditificationNum = userObj.patientIdentificationNo; _requestSendLabReportEmail.dateofBirth = userObj.dateofBirth; _requestSendLabReportEmail.to = userObj.emailAddress; _requestSendLabReportEmail.orderDate = '${patientLabOrder.orderDate!.year}-${patientLabOrder.orderDate!.month}-${patientLabOrder.orderDate!.day}'; _requestSendLabReportEmail.patientMobileNumber = userObj.mobileNumber; _requestSendLabReportEmail.projectName = patientLabOrder.projectName; _requestSendLabReportEmail.setupID = patientLabOrder.setupID; _requestSendLabReportEmail.orderNo = patientLabOrder.orderNo; _requestSendLabReportEmail.orderNo = patientLabOrder.orderNo; _requestSendLabReportEmail.isDownload = isDownload; _requestSendLabReportEmail.doctorID = patientLabOrder.doctorID; _requestSendLabReportEmail.languageID = languageID; // await baseAppClient.post(SEND_LAB_RESULT_EMAIL, onSuccess: (dynamic response, int statusCode) { await baseAppClient .post(isVidaPlus ? SEND_LAB_RESULT_EMAIL : SEND_LAB_RESULT_EMAIL_NEW, onSuccess: (dynamic response, int statusCode) { if (isDownload) { labReportPDF = isVidaPlus ? response['LabReportsPDFContent'] : response['PdfContent']; } }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: _requestSendLabReportEmail.toJson()); } List sortByFlagAndValue(List original) { const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH']; int getFlagPriority(String? flag) { if (flag == null) return priorityOrder.length; final index = priorityOrder.indexOf(flag); return index == -1 ? priorityOrder.length : index; } double parseResultValue(String? value) { if (value == null) return double.nan; return double.tryParse(value) ?? double.nan; } final copy = List.from(original); copy.sort((a, b) { final aFlagPriority = getFlagPriority(a.calculatedResultFlag); final bFlagPriority = getFlagPriority(b.calculatedResultFlag); if (aFlagPriority != bFlagPriority) { return aFlagPriority.compareTo(bFlagPriority); } final aValue = parseResultValue(a.resultValue); final bValue = parseResultValue(b.resultValue); return aValue.compareTo(bValue); }); return copy; } Map mapFirstItemByPriority( List sortedResults) { final Map priorityMap = {}; const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH']; for (final result in sortedResults) { final priority = result.calculatedResultFlag?.trim(); if (priority != null && priorityOrder.contains(priority) && !priorityMap.containsKey(priority)) { priorityMap[priority] = result; } // Early exit if all priorities are found if (priorityMap.length == priorityOrder.length) break; } print("the map of priority is :\n $priorityMap"); return priorityMap; } List getMostRecentThree(List original) { DateTime? parseVerifiedDate(String? raw) { if (raw == null) return null; final regex = RegExp(r'\/Date\((\d+)\)\/'); final match = regex.firstMatch(raw); if (match != null) { print("the match iss not null"); final millis = int.tryParse(match.group(1)!); if (millis != null) { print( "the data and time is ${DateTime.fromMillisecondsSinceEpoch(millis)}"); return DateTime.fromMillisecondsSinceEpoch(millis); } } return null; } final copy = List.from(original); copy.sort((a, b) { final aDate = DateUtil.convertStringToDate(a.verifiedOnDateTime); final bDate = DateUtil.convertStringToDate(b.verifiedOnDateTime); if (aDate == null && bDate == null) return 0; if (aDate == null) return 1; if (bDate == null) return -1; return bDate.compareTo(aDate); // descending }); print("the copied item are $copy"); labOrdersResultsList = copy; return copy.take(3).toList(); } List buildThresholdList(List topResults) { var mapOfPriority = mapFirstItemByPriority(topResults); return mapResultToThreshold(topResults, mapOfPriority); } List mapResultToThreshold( List results, Map mapOfPriority) { // Extract valid numeric results List actualValues = results .map((e) => double.tryParse(e.resultValue ?? '')) .where((v) => v != null) .cast() .toList(); if (actualValues.isEmpty) return []; actualValues.sort(); double min = actualValues.first; double max = actualValues.last; double baseRange = max - min; // Handle single value or equal values if (baseRange == 0) { baseRange = max * 0.2; // ±10% min = max - baseRange / 2; max = max + baseRange / 2; } // Adjust range scale based on number of actual values // >5 = compress thresholds; <5 = expand thresholds int valueCount = actualValues.length; double scalingFactor = valueCount >= 5 ? 1.0 : (5 / valueCount); double adjustedRange = baseRange * scalingFactor; // Recalculate thresholds based on adjusted range // double criticalLow = min; // double low = min + 0.25 * adjustedRange; // double normal = min + 0.5 * adjustedRange; // double high = min + 0.75 * adjustedRange; // double criticalHigh = min + adjustedRange; const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH']; var mapOfValues = inferThresholds(mapOfPriority); var item = results.first; String? realCriticalLow = (item.criticalLow == "0") ? null : item.criticalLow; String? realReferenceHigh = (item.referenceHigh == "0") ? null : item.referenceHigh; String? realCriticalHigh = (item.criticalHigh == "0") ? null : item.criticalHigh; String? realReferenceLow = (item.referenceLow == "0") ? null : item.referenceLow; final adjustedValues = adjustValues( criticalLow: mapOfValues['criticalLow'], low: mapOfValues['low'], normal: mapOfValues['normal'], high: mapOfValues['high'], criticalHigh: mapOfValues['criticalHigh'], ); return [ ThresholdRange( label: 'Critical Low', value: adjustedValues["criticalLow"]!, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4), actualValue: realCriticalLow), ThresholdRange( label: 'Low', value: adjustedValues['low']!, color: Color(0xFFf2fbf5), lineColor: Color(0xFFefc481), actualValue: realReferenceLow), ThresholdRange( label: 'Normal', value: adjustedValues['normal']!, color: Color(0xFFf2fbf5), lineColor: Color(0xFF5dc36b)), ThresholdRange( label: 'High', value: adjustedValues['high']!, color: Color(0xffffffff), lineColor: Color(0xFFefc481), actualValue: realReferenceHigh), ThresholdRange( label: 'Critical High', value: adjustedValues['criticalHigh']!, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4), actualValue: realCriticalHigh), ]; } Map inferThresholds( Map mapOfPriority) { double? parse(String? v) { final parsed = double.tryParse(v ?? ''); return (parsed == null || parsed < 0) ? null : parsed; } // Parse inputs double? criticalLow = parse( mapOfPriority['LCL']?.resultValue ?? mapOfPriority['CL']?.resultValue); double? low = parse(mapOfPriority['L']?.resultValue); double? normal = parse(mapOfPriority['N']?.resultValue); double? high = parse(mapOfPriority['H']?.resultValue); double? criticalHigh = parse( mapOfPriority['CH']?.resultValue ?? mapOfPriority['HCH']?.resultValue); const step = 5.0; List values = [criticalLow, low, normal, high, criticalHigh]; // Find the index of the known value (priority: central -> left -> right) int anchorIndex = values.indexWhere((v) => v != null); double anchorValue = values[anchorIndex] ?? 50; // Infer all values around the anchor List inferred = List.generate(5, (i) { double v = anchorValue + (i - anchorIndex) * step; return v < 0 ? 0 : v; }); var mapresult = { 'criticalLow': values[0]??-1, 'low': values[1]??-1, 'normal': values[2]??-1, 'high': values[3]??-1, 'criticalHigh': values[4]??-1, }; print("the result is $mapresult"); return mapresult; } Map adjustValues({ double? criticalLow, double? low, double? normal, double? high, double? criticalHigh, double step = 10, }) { bool criticalLowHasValue = true; bool lowHasValue = true; bool normaHasValue = true; bool highHasValue = true; bool criticalHighHasValue = true; if (criticalLow == null || criticalLow == -1) criticalLowHasValue = false; if (low == null || low == -1) lowHasValue = false; if (normal == null ||normal ==-1) normaHasValue = false; if (high == null ||high ==-1) highHasValue = false; if (criticalHigh == null ||criticalHigh ==-1) criticalHighHasValue = false; print("the values arre $criticalLowHasValue $lowHasValue $normaHasValue $highHasValue $criticalHighHasValue"); if (!criticalLowHasValue) { criticalLow = 0; if (lowHasValue) { low = low! - step; } if (normaHasValue && (criticalLow!= 0 || criticalLow != -1)) { low = normal! - step * 2; } if (highHasValue && (criticalLow!= 0 || criticalLow != -1)) { low = high! - step * 3; } if (criticalHighHasValue && (criticalLow!= 0 || criticalLow != -1)) { low = criticalHigh! - step * 4; } } if (!lowHasValue) { print("the low value is not set"); low = 0; if (criticalLowHasValue && (low != 0 || low != -1)) { low = criticalLow! + step; } if (normaHasValue && (low != 0 || low != -1)) { low = normal! - step; } if (highHasValue && (low != 0 || low != -1)) { low = high! - step * 2; } if (criticalHighHasValue && (low != 0 || low != -1)) { low = criticalHigh! - step * 3; } } if (!normaHasValue) { normal = 0; if (criticalLowHasValue && (normal != 0 || normal != -1)) { normal = criticalLow! + step * 2; } if (lowHasValue) { normal = low! + step; } if (highHasValue) { normal = high! - step; } if (criticalHighHasValue) { normal = criticalHigh! - step * 2; } } if (!highHasValue) { high = 0; if (criticalLowHasValue) { high = criticalLow! + step * 3; } if (lowHasValue) { high = low! + step * 2; } if (normaHasValue) { high = normal! + step; } if (criticalHighHasValue) { high = criticalHigh! - step; } } if (!criticalHighHasValue) { criticalHigh = 0; if (criticalLowHasValue) { criticalHigh = criticalLow! + step * 4; } if (lowHasValue) { criticalHigh = low! + step * 3; } if (normaHasValue) { criticalHigh = normal! + step * 2; } if (highHasValue) { criticalHigh = high! + step; } } if(((criticalLow??0)<0) == true){ var mod = ((low ?? 0) + (normal??0) )/2; criticalLow = mod; } if(((low??0)<0) == true){ var mod = ((criticalLow ?? 0) + (normal??0) )/2; low = mod; } if(((normal??0)<0) == true){ var mod = ((low ?? 0) + (high??0) )/2; normal = mod; } if(((high??0)<0) == true){ var mod = ((normal ?? 0) + (criticalHigh??0) )/2; high = mod; } if(((criticalHigh??0)<0) == true){ criticalHigh = (high??0)+step; } Map values = { 'criticalLow':criticalLow , 'low': low, 'normal': normal , 'high': high , 'criticalHigh': criticalHigh, }; print("thee adjusted values `are $values"); // // Find the first known value // int firstKnownIndex = values.values.toList().indexWhere((v) => v != null); // // if (firstKnownIndex == -1) { // // No values at all, start from 0 // values = { // 'criticalLow': 0, // 'low': step, // 'normal': step * 2, // 'high': step * 3, // 'criticalHigh': step * 4, // }; // } else { // // Fill backward // for (int i = firstKnownIndex - 1; i >= 0; i--) { // values[values.keys.elementAt(i)] = // (values.values.elementAt(i + 1)! - step).clamp(0, double.infinity); // } // // // Fill forward // for (int i = firstKnownIndex + 1; i < values.length; i++) { // values[values.keys.elementAt(i)] = // values.values.elementAt(i - 1)! + step; // } // } // // // Ensure strictly increasing sequence // double prev = -double.infinity; // values.forEach((key, val) { // if (val! <= prev) { // values[key] = prev + step; // } // prev = values[key]!; // }); return values.map((k, v) => MapEntry(k, v!)); } void adjustLabOrderResults(List results) { const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH']; // Sort results by priority order results.sort((a, b) => priorityOrder .indexOf(a.calculatedResultFlag ?? '') .compareTo(priorityOrder.indexOf(b.calculatedResultFlag ?? ''))); // Extract values List values = results.map((r) { double? v = double.tryParse(r.resultValue ?? ''); return (v != null && v >= 0) ? v : null; }).toList(); // Find first non-null index int? firstIdx = values.indexWhere((v) => v != null); if (firstIdx == -1) return; // All values missing // Fill before first known value (decreasing with step 5, not less than 0) for (int i = firstIdx - 1; i >= 0; i--) { values[i] = (values[i + 1]! - 5).clamp(0, double.infinity); } // Fill after first known value (increasing with step 5) for (int i = firstIdx + 1; i < values.length; i++) { if (values[i] == null) { values[i] = values[i - 1]! + 5; } } // For gaps in the middle, interpolate for (int i = 0; i < values.length; i++) { if (values[i] == null) { // find next non-null int j = i + 1; while (j < values.length && values[j] == null) j++; if (j < values.length) { double start = values[i - 1]!; double end = values[j]!; double step = (end - start) / (j - i + 1); for (int k = i; k < j; k++) { values[k] = start + step * (k - i + 1); } } } } // Update results list directly for (int i = 0; i < results.length; i++) { results[i].resultValue = values[i]?.toStringAsFixed(2) ?? '0.00'; } } double transformValueInRange(double inputValue, String flag) { // Define range boundaries double rangeStart, rangeEnd; switch (flag) { case 'CL': rangeStart = 0.0; rangeEnd = 19.0; break; case 'L': rangeStart = 20.0; rangeEnd = 39.0; break; case 'N': rangeStart = 40.0; rangeEnd = 59.0; break; case 'H': rangeStart = 60.0; rangeEnd = 79.0; break; case 'CH': rangeStart = 80.0; rangeEnd = 100.0; break; default: throw ArgumentError('Invalid flag: $flag'); } // Clamp input value to 0-100 and map it to the range bounds final clampedValue = inputValue.clamp(0.0, 100.0); final normalizedValue = clampedValue / 100.0; // Normalize input to 0-1 // Map the normalized value to the target range bounds final transformedValue = rangeStart + (normalizedValue * (rangeEnd - rangeStart)); return transformedValue; } List getThresholdValue() { return [ ThresholdRange( label: 'criticalLow', value: 0, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4)), ThresholdRange( label: 'low', value: 20, color: Color(0xFFf2fbf5), lineColor: Color(0xFFeecd94)), ThresholdRange( label: 'normal', value: 40, color: Color(0xFFf2fbf5), lineColor: Color(0xFF5dc36b)), ThresholdRange( label: 'high', value: 60, color: Color(0xffffffff), lineColor: Color(0xFFeecd94)), ThresholdRange( label: 'criticalHigh', value: 80, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4)), ]; } }