You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			443 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Dart
		
	
			
		
		
	
	
			443 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Dart
		
	
| import 'dart:core';
 | |
| 
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:hmg_patient_app_new/core/app_assets.dart';
 | |
| import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
 | |
| import 'package:hmg_patient_app_new/core/utils/date_util.dart';
 | |
| import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
 | |
| import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
 | |
| import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
 | |
| import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
 | |
| import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart';
 | |
| import 'package:hmg_patient_app_new/services/error_handler_service.dart';
 | |
| import 'package:hmg_patient_app_new/services/navigation_service.dart';
 | |
| import 'package:hmg_patient_app_new/theme/colors.dart';
 | |
| import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
 | |
| import 'package:intl/intl.dart' show DateFormat;
 | |
| import 'package:logger/logger.dart';
 | |
| 
 | |
| class LabViewModel extends ChangeNotifier {
 | |
|   bool isLabOrdersLoading = false;
 | |
|   bool isLabResultsLoading = false;
 | |
| 
 | |
|   LabRepo labRepo;
 | |
|   ErrorHandlerService errorHandlerService;
 | |
|   NavigationService navigationService;
 | |
| 
 | |
|   List<PatientLabOrdersResponseModel> patientLabOrders = [];
 | |
|   List<PatientLabOrdersResponseModel> filteredLabOrders = [];
 | |
|   List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
 | |
| 
 | |
|   List<LabResult> mainLabResults = [];
 | |
|   List<DataPoint> mainGraphPoints = [];
 | |
|   List<DataPoint> filteredGraphValues = [];
 | |
|   List months = [
 | |
|     'Jan',
 | |
|     'Feb',
 | |
|     'Mar',
 | |
|     'April',
 | |
|     'May',
 | |
|     'Jun',
 | |
|     'July',
 | |
|     'Aug',
 | |
|     'Sep',
 | |
|     'Oct',
 | |
|     'Nov',
 | |
|     'Dec'
 | |
|   ];
 | |
| 
 | |
|   late List<String> _labSuggestionsList = [];
 | |
| 
 | |
|   List<String> get labSuggestions => _labSuggestionsList;
 | |
| 
 | |
|    Set<TestDetails> uniqueTests = {};
 | |
| 
 | |
|   double maxYForThreeDots = 0.0;
 | |
| 
 | |
|   LabViewModel(
 | |
|       {required this.labRepo,
 | |
|       required this.errorHandlerService,
 | |
|       required this.navigationService});
 | |
| 
 | |
|   initLabProvider() {
 | |
|     patientLabOrders.clear();
 | |
|     filteredLabOrders.clear();
 | |
|     isLabOrdersLoading = true;
 | |
|     isLabResultsLoading = true;
 | |
|     getPatientLabOrders();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
 | |
|     final result = await labRepo.getPatientLabOrders();
 | |
| 
 | |
|     result.fold(
 | |
|           (failure) async => await errorHandlerService.handleError(failure: failure),
 | |
|           (apiResponse) {
 | |
|         if (apiResponse.messageStatus == 2) {
 | |
|           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
 | |
|         } else if (apiResponse.messageStatus == 1) {
 | |
|           patientLabOrders = apiResponse.data!;
 | |
|           filteredLabOrders = List.from(patientLabOrders);
 | |
|           tempLabOrdersList = apiResponse.data!;
 | |
|           isLabOrdersLoading = false;
 | |
|           isLabResultsLoading = false;
 | |
|           filterSuggestions();
 | |
|           getUniqueTestDescription();
 | |
|           notifyListeners();
 | |
|           if (onSuccess != null) {
 | |
|             onSuccess(apiResponse);
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   filterSuggestions() {
 | |
|     final List<String> labels = patientLabOrders
 | |
|         .expand((order) => order.testDetails!)
 | |
|         .map((detail) => detail.description)
 | |
|         .whereType<String>()
 | |
|         .toList();
 | |
|     _labSuggestionsList = labels.toSet().toList();
 | |
| 
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   filterLabReports(String query) {
 | |
|     if (query.isEmpty) {
 | |
|       filteredLabOrders = List.from(patientLabOrders); // reset
 | |
|     } else {
 | |
|       filteredLabOrders = patientLabOrders.where((order) {
 | |
|         final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? [];
 | |
|         return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase()));
 | |
|       }).toList();
 | |
|       patientLabOrders = filteredLabOrders;
 | |
|     }
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   getUniqueTestDescription() {
 | |
| 
 | |
|      uniqueTests = {
 | |
|       for (var item in patientLabOrders)
 | |
|         if (item.testDetails != null)
 | |
|           ...?item.testDetails?.map<TestDetails>((test) => TestDetails(
 | |
|               description: test.description.toString(),
 | |
|               testCode: test.testCode.toString(),
 | |
|               testID: test.testID,
 | |
|               createdOn: item.createdOn,
 | |
|               model: item))
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   Future<void> getPatientLabResult(
 | |
|       PatientLabOrdersResponseModel laborder, String procedureName) async {
 | |
|     LoaderBottomSheet.showLoader();
 | |
|     mainLabResults.clear();
 | |
|     filteredGraphValues.clear();
 | |
| 
 | |
|     final result = await labRepo.getPatientLabResults(
 | |
|         laborder,
 | |
|         Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
 | |
|         procedureName);
 | |
| 
 | |
|     result.fold(
 | |
|       (failure) async {
 | |
|         LoaderBottomSheet.hideLoader();
 | |
|         await errorHandlerService.handleError(failure: failure);
 | |
|       },
 | |
|       (apiResponse) {
 | |
|         LoaderBottomSheet.hideLoader();
 | |
|         if (apiResponse.messageStatus == 2) {
 | |
|         } else if (apiResponse.messageStatus == 1) {
 | |
|           var sortedResponse = sortByFlagAndValue(apiResponse.data!);
 | |
|           var recentThree = sort(sortedResponse);
 | |
|           mainLabResults = recentThree;
 | |
| 
 | |
|           double counter = 1;
 | |
|           recentThree.reversed.forEach((element) {
 | |
|             try {
 | |
|               var dateTime =
 | |
|                   DateUtil.convertStringToDate(element.verifiedOnDateTime!);
 | |
|               if (double.parse(element.resultValue!) > maxYForThreeDots) {
 | |
|                 maxYForThreeDots = double.parse(element.resultValue!);
 | |
|               }
 | |
|               filteredGraphValues.add(DataPoint(
 | |
|                   value: transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""),
 | |
|                   actualValue:element.resultValue!,
 | |
|                   label: formatDateAsMMYY(dateTime),
 | |
|                displayTime: resultDate(dateTime),
 | |
|                time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
 | |
|                   refernceValue: element.calculatedResultFlag ?? "",
 | |
| 
 | |
|               ));
 | |
|               counter++;
 | |
|             } catch (e) {}
 | |
|           });
 | |
|           LabResult recentResult = recentThree.first;
 | |
|           recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
 | |
|           navigationService.push(MaterialPageRoute(
 | |
|               builder: (_) =>
 | |
|                   LabResultDetails(recentLabResult: recentResult)));
 | |
|           notifyListeners();
 | |
|         }
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   String resultDate(DateTime date){
 | |
| 
 | |
| 
 | |
|     return '${date.day} ${months[date.month-1]},${date.year}';
 | |
|   }
 | |
| 
 | |
|   double transformValueInRange(double inputValue, String flag) {
 | |
|     // Define range boundaries
 | |
|     double rangeStart, rangeEnd;
 | |
| 
 | |
|     switch (flag) {
 | |
|       case'LCL':
 | |
|       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 'HCH':
 | |
|       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;
 | |
|   }
 | |
|   void getSelectedDateRange(DateTime? start, DateTime? end) {
 | |
|     if(start == null && end == null) {
 | |
|       print("the dates are null");
 | |
|       mainLabResults.forEach((element) {
 | |
|         final time = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
 | |
|         try{
 | |
|           filteredGraphValues.add(DataPoint(
 | |
|             value: transformValueInRange(double.parse(element.resultValue!),
 | |
|                 element.calculatedResultFlag ?? ""),
 | |
|             actualValue: element.resultValue!,
 | |
|             label: formatDateAsMMYY(time),
 | |
|             displayTime: resultDate(time),
 | |
|             time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
 | |
|             refernceValue: element.calculatedResultFlag ?? "",
 | |
|           ));
 | |
|         }catch(e){
 | |
| 
 | |
|         }
 | |
|       });
 | |
| 
 | |
|     }else {
 | |
|       filteredGraphValues.clear();
 | |
| 
 | |
|       mainLabResults.forEach((element) {
 | |
|         try {
 | |
|           var dateTime =
 | |
|               DateUtil.convertStringToDate(element.verifiedOnDateTime!);
 | |
|           if (start != null && end == null) {
 | |
|             if (dateTime.isAtSameMomentAs(start)) {
 | |
| 
 | |
|               filteredGraphValues.add(DataPoint(
 | |
|                   value: transformValueInRange(
 | |
|                       double.parse(element.resultValue!),
 | |
|                       element.calculatedResultFlag ?? ""),
 | |
|                   actualValue: element.resultValue!,
 | |
|                   label: formatDateAsMMYY(dateTime),
 | |
|                   displayTime: resultDate(dateTime),
 | |
|                   time:
 | |
|                       DateUtil.convertStringToDate(element.verifiedOnDateTime),
 | |
|                   refernceValue: element.calculatedResultFlag ?? ""));
 | |
|             }
 | |
|           } else if (start != null && end != null) {
 | |
|             if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) {
 | |
| 
 | |
|               filteredGraphValues.add(DataPoint(
 | |
|                   value: transformValueInRange(
 | |
|                       double.parse(element.resultValue!),
 | |
|                       element.calculatedResultFlag ?? ""),
 | |
|                   actualValue: element.resultValue!,
 | |
|                   label: formatDateAsMMYY(dateTime),
 | |
|                   displayTime: resultDate(dateTime),
 | |
|                   time:
 | |
|                       DateUtil.convertStringToDate(element.verifiedOnDateTime),
 | |
|                   refernceValue: element.calculatedResultFlag ?? ""));
 | |
|             }
 | |
|           }
 | |
|         } catch (e) {}
 | |
|       });
 | |
|     }
 | |
|     filteredGraphValues = sortFilteredList(filteredGraphValues).reversed.toList();
 | |
| 
 | |
| 
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   String formatDateAsMMYY(DateTime date) {
 | |
| 
 | |
|     String year = date.year.toString().substring(2);
 | |
|     return '${months[date.month-1]},$year';
 | |
|   }
 | |
| 
 | |
| 
 | |
|   List<LabResult> sortByFlagAndValue(List<LabResult> 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<LabResult>.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;
 | |
|   }
 | |
| 
 | |
|   List<LabResult> sort(List<LabResult> original) {
 | |
|     DateTime? parseVerifiedDate(String? raw) {
 | |
|       if (raw == null) return null;
 | |
|       final regex = RegExp(r'\/Date\((\d+)\)\/');
 | |
|       final match = regex.firstMatch(raw);
 | |
|       if (match != null) {
 | |
|         final millis = int.tryParse(match.group(1)!);
 | |
|         if (millis != null) {
 | |
| 
 | |
|           return DateTime.fromMillisecondsSinceEpoch(millis);
 | |
|         }
 | |
|       }
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     final copy = List<LabResult>.from(original);
 | |
|     copy.sort((a, b) {
 | |
|       final aDate = DateUtil.convertStringToDate(a.verifiedOnDateTime);
 | |
|       final bDate = DateUtil.convertStringToDate(b.verifiedOnDateTime);
 | |
|       final now = DateTime.now();
 | |
|       if (aDate == now && bDate == now) return 0;
 | |
|       if (aDate == now) return 1;
 | |
|       if (bDate == now) return -1;
 | |
|       return bDate.compareTo(aDate); // descending
 | |
|     });
 | |
|     return copy.toList();
 | |
|   }
 | |
| 
 | |
|   List<DataPoint> sortFilteredList(List<DataPoint> original) {
 | |
| 
 | |
| 
 | |
|     final copy = List<DataPoint>.from(original);
 | |
|     copy.sort((a, b) {
 | |
|       final aDate =a.time;
 | |
|       final bDate = a.time;
 | |
|       final now = DateTime.now();
 | |
|       if (aDate == now && bDate == now) return 0;
 | |
|       if (aDate == now) return 1;
 | |
|       if (bDate == now) return -1;
 | |
|       return bDate.compareTo(aDate); // descending
 | |
|     });
 | |
|     return copy.toList();
 | |
|   }
 | |
| 
 | |
|   Color getColor(String flag) {
 | |
|     switch (flag) {
 | |
|       case 'LCL':
 | |
|         return AppColors.criticalLowAndHigh;
 | |
|       case 'CL':
 | |
|         return AppColors.criticalLowAndHigh;
 | |
|       case 'L':
 | |
|         return AppColors.highAndLow;
 | |
|       case 'N':
 | |
|         return AppColors.bgGreenColor;
 | |
|       case 'H':
 | |
|         return AppColors.highAndLow;
 | |
|       case 'CH':
 | |
|         return AppColors.criticalLowAndHigh;
 | |
|       case 'HCH':
 | |
|         return AppColors.criticalLowAndHigh;
 | |
|       default:
 | |
|         return Colors.grey;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   String getFormattedDate(DateTime date){
 | |
|     return DateFormat('EEEE, dd MMMM. yyyy').format(date);
 | |
|   }
 | |
| 
 | |
|   String getAssetUrlWRTResult(String refernceValue) {
 | |
|     switch (refernceValue) {
 | |
|       case 'CL':
 | |
|       case 'LCL':
 | |
|         return AppAssets.ic_critical_low_result;
 | |
|       case 'L':
 | |
|         return AppAssets.ic_low_result;
 | |
|       case 'N':
 | |
|         return AppAssets.ic_normal_result;
 | |
|       case 'H':
 | |
|          return AppAssets.ic_low_result;
 | |
|       case 'CH':
 | |
|       case 'HCH':
 | |
|         return AppAssets.ic_critical_low_result;
 | |
|       default:
 | |
|         return AppAssets.ic_normal_result;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool getRotationWRTResult(String refernceValue) {
 | |
|     switch (refernceValue) {
 | |
|       case 'CL':
 | |
|       case 'LCL':
 | |
|       case 'L':
 | |
|       case 'N':
 | |
|         return false;
 | |
|       case 'H':
 | |
|       case 'CH':
 | |
|       case 'HCH':
 | |
|         return true;
 | |
|       default:
 | |
|         return true;
 | |
|     }
 | |
|   }
 | |
| }
 |