From baeaecba6e4dfd204f3c4e52ff1526922b669ffe Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 21:21:43 +0900 Subject: [PATCH 1/6] Fix issue with insurance ean --- AmiKoDesitin/EPrescription/EPrescription.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/AmiKoDesitin/EPrescription/EPrescription.m b/AmiKoDesitin/EPrescription/EPrescription.m index a7964a4..5534381 100644 --- a/AmiKoDesitin/EPrescription/EPrescription.m +++ b/AmiKoDesitin/EPrescription/EPrescription.m @@ -188,7 +188,7 @@ - (ZurRosePrescription *)toZurRosePrescription { ZurRosePrescription *prescription = [[ZurRosePrescription alloc] init]; prescription.issueDate = self.date; - prescription.prescriptionNr = self.prescriptionId; + prescription.prescriptionNr = [NSString stringWithFormat:@"%09d", arc4random_uniform(1000000000)]; prescription.remark = self.rmk; prescription.validity = self.valDt; // ??? @@ -226,11 +226,13 @@ - (ZurRosePrescription *)toZurRosePrescription { : [self.patientLang.lowercaseString hasPrefix:@"fr"] ? 2 : [self.patientLang.lowercaseString hasPrefix:@"it"] ? 3 : 1; + patient.coverCardId = @""; + patient.patientNr = @""; + NSString *insuranceEan = nil; for (EPrescriptionPatientId *pid in self.patientIds) { if ([pid.type isEqual:@(1)]) { - patient.coverCardId = pid.value; - patient.patientNr = pid.value; // ??? + insuranceEan = pid.value; } } @@ -252,6 +254,7 @@ - (ZurRosePrescription *)toZurRosePrescription { product.quantity = medi.nbPack.intValue; // ??? product.remark = medi.appInstr; product.insuranceBillingType = 1; + product.insuranceEanId = insuranceEan; BOOL repetition = NO; NSMutableArray *poses = [NSMutableArray array]; From a113e6bca34c88f3a79eb801676afe42b57a905b Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 21:44:28 +0900 Subject: [PATCH 2/6] Prevent duplicating patient when scanning QRCode --- AmiKoDesitin/Persistence/MLPersistenceManager.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AmiKoDesitin/Persistence/MLPersistenceManager.m b/AmiKoDesitin/Persistence/MLPersistenceManager.m index 7c8e801..1e0b6b4 100644 --- a/AmiKoDesitin/Persistence/MLPersistenceManager.m +++ b/AmiKoDesitin/Persistence/MLPersistenceManager.m @@ -492,6 +492,9 @@ - (NSString *)upsertPatient:(Patient *)patient withTimestamp:(NSDate*)date updat NSError *error = nil; if (patient.uniqueId.length) { PatientModel *p = [self getPatientModelWithUniqueID:patient.uniqueId]; + if (p == nil) { + p = [self findSimilarPatientModelByNameAndDOB:patient]; + } if (p != nil) { p.weightKg = patient.weightKg; p.heightCm = patient.heightCm; @@ -581,6 +584,16 @@ - (Patient *) getPatientWithUniqueID:(NSString *)uniqueID { return [[self getPatientModelWithUniqueID:uniqueID] toPatient]; } +- (PatientModel *)findSimilarPatientModelByNameAndDOB:(Patient *)patient { + NSError *error = nil; + NSManagedObjectContext *context = [[self coreDataContainer] viewContext]; + NSFetchRequest *req = [PatientModel fetchRequest]; + req.predicate = [NSPredicate predicateWithFormat:@"familyName == %@ AND givenName == %@ AND birthDate == %@", patient.familyName, patient.givenName, patient.birthDate]; + req.fetchLimit = 1; + NSArray *patientModels = [context executeFetchRequest:req error:&error]; + return [patientModels firstObject]; +} + # pragma mark - Favourites - (NSURL *)favouritesFile { From bafa3103edf542e6e3f4389feaa77e39118fd166 Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 22:30:34 +0900 Subject: [PATCH 3/6] Detect AHV number from insurance card --- .../PatientViewController+smartCard.h | 3 +- .../PatientViewController+smartCard.m | 72 +++++++++++-------- AmiKoDesitin/PreviewView.h | 4 +- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/AmiKoDesitin/PatientViewController+smartCard.h b/AmiKoDesitin/PatientViewController+smartCard.h index eddf1d6..0ed12eb 100644 --- a/AmiKoDesitin/PatientViewController+smartCard.h +++ b/AmiKoDesitin/PatientViewController+smartCard.h @@ -8,7 +8,7 @@ #import "PatientViewController.h" -#define NUMBER_OF_BOXES_FOR_OCR 4 +#define NUMBER_OF_BOXES_FOR_OCR 5 #import "CameraViewController.h" @@ -19,6 +19,7 @@ struct scannedResults { NSString *dateString; NSString *sexString; NSString *bagNumber; + NSString *ahvNumber; }; #pragma mark - class extension diff --git a/AmiKoDesitin/PatientViewController+smartCard.m b/AmiKoDesitin/PatientViewController+smartCard.m index fd9c4c6..027eb2b 100644 --- a/AmiKoDesitin/PatientViewController+smartCard.m +++ b/AmiKoDesitin/PatientViewController+smartCard.m @@ -200,7 +200,7 @@ - (NSArray *)visionDetectTextBoundingBoxes:(CIImage*)image NSArray *langs = [VNRecognizeTextRequest supportedRecognitionLanguagesForTextRecognitionLevel:VNRequestTextRecognitionLevelAccurate revision:VNRecognizeTextRequestRevision1 error:&error]; - NSLog(@"%@", langs); +// NSLog(@"%@", langs); VNRecognizeTextRequest *textRequest = [VNRecognizeTextRequest new]; textRequest.recognitionLevel = VNRequestTextRecognitionLevelAccurate; @@ -276,13 +276,22 @@ - (NSArray *)visionDetectTextBoundingBoxes:(CIImage*)image if (box.origin.x > thresholdX) #endif { + BOOL shouldSkip = YES; // Handle BAG number, which is not at the right side of the card // 5 digit and all number if (s.length == 5 && [[NSString stringWithFormat:@"%05d",[s intValue]] isEqual:s]) { - if (box.origin.x > 0.9) { - continue; + if (box.origin.x < 0.9) { + shouldSkip = NO; } - } else { + } else if (s.length == 16) { + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]{3}\\.[0-9]{4}\\.[0-9]{4}\\.[0-9]{2}$" options:0 error:&error]; + NSTextCheckingResult *match = [regex firstMatchInString:s options:0 range:NSMakeRange(0, s.length)]; + if (match) { + shouldSkip = NO; + } + } + if (shouldSkip) { continue; } } @@ -333,22 +342,6 @@ - (NSArray *)visionDetectTextBoundingBoxes:(CIImage*)image - (NSArray *)analyzeVisionBoxedWords:(NSArray *)allBoxes { - // Always put BAG number at last - NSInteger bagNumberIndex = -1; - for (NSInteger i = 0; i < allBoxes.count; i++) { - if ([allBoxes[i][@"text"] length] == 5) { - bagNumberIndex = i; - break; - } - } - if (bagNumberIndex >= 0) { - NSMutableArray *mAllBoxes = [allBoxes mutableCopy]; - NSDictionary *bagNumber = mAllBoxes[bagNumberIndex]; - [mAllBoxes removeObjectAtIndex:bagNumberIndex]; - [mAllBoxes addObject:bagNumber]; - allBoxes = mAllBoxes; - } - NSUInteger n = [allBoxes count]; if (n < NUMBER_OF_BOXES_FOR_OCR) { return allBoxes; @@ -397,18 +390,35 @@ - (NSArray *)analyzeVisionBoxedWords:(NSArray *)allBoxes return p1.origin.x >= p2.origin.x; #else - if ([obj1[@"text"] length] == 5) { - return NSOrderedDescending; - } - if ([obj2[@"text"] length] == 5) { - return NSOrderedAscending; - } if (p1.origin.y == p2.origin.y) return NSOrderedSame; return p1.origin.y < p2.origin.y; #endif }]; + + // At this point, the first one should be name, last one should be date string, + // but we are not sure about the middle ones, at they are layed out horizontally + NSDictionary *name = [boxedWords firstObject]; + NSDictionary *dateString = [boxedWords lastObject]; + + boxedWords = [boxedWords subarrayWithRange:NSMakeRange(1, NUMBER_OF_BOXES_FOR_OCR - 2)]; + boxedWords = [boxedWords sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) { + CGRect p1 = [obj1[@"box"] CGRectValue]; + CGRect p2 = [obj2[@"box"] CGRectValue]; +#ifdef VN_BOXES_NEED_XY_SWAP + if (p1.origin.y == p2.origin.y) + return NSOrderedSame; + + return p1.origin.y >= p2.origin.y; +#else + if (p1.origin.x == p2.origin.x) + return NSOrderedSame; + + return p1.origin.x >= p2.origin.x; +#endif + }]; + boxedWords = [@[name] arrayByAddingObjectsFromArray:[boxedWords arrayByAddingObject:dateString]]; return boxedWords; } @@ -463,9 +473,12 @@ - (BOOL)validateOcrResults:(NSArray *)ocrResults if (![_NumericOnly isSupersetOfSet: myStringSet]) { return NO; } + + NSString *bagNumber = ocrResults[2][@"text"]; + NSString *ahvNumber = ocrResults[3][@"text"]; // Validate third line ///////////////////////////////////////////////////// - d = ocrResults[2]; + d = ocrResults[4]; s = d[@"text"]; NSArray *line2Array = [s componentsSeparatedByString:@" "]; if ([line2Array count] < 2) { @@ -486,8 +499,6 @@ - (BOOL)validateOcrResults:(NSArray *)ocrResults { return NO; } - - NSString *bagNumber = ocrResults[3][@"text"]; savedOcr.familyName = familyName; savedOcr.givenName = givenName; @@ -495,6 +506,7 @@ - (BOOL)validateOcrResults:(NSArray *)ocrResults savedOcr.dateString = dateString; savedOcr.sexString = sexString; savedOcr.bagNumber = bagNumber; + savedOcr.ahvNumber = ahvNumber; return YES; } @@ -518,6 +530,8 @@ - (void)lastVideoFrame:(NSNotification *)notification incompletePatient.gender = KEY_AMK_PAT_GENDER_M; else if ([savedOcr.sexString isEqualToString:@"F"]) incompletePatient.gender = KEY_AMK_PAT_GENDER_F; + + NSLog(@"ahv %@", savedOcr.ahvNumber); #ifdef TAP_TO_END_CARD_OCR [self resetAllFields]; diff --git a/AmiKoDesitin/PreviewView.h b/AmiKoDesitin/PreviewView.h index a71f461..75e9a97 100644 --- a/AmiKoDesitin/PreviewView.h +++ b/AmiKoDesitin/PreviewView.h @@ -28,11 +28,11 @@ // Following 4 defines in the range 0..1 #define cardROI_X 0.0f #define cardROI_Y (cardIgnoreTop_mm / cardHeight_mm) -#define cardROI_W (60.0/cardWidth_mm) +#define cardROI_W (cardWidth_mm/cardWidth_mm) // We need the full width for AHV number #define cardROI_H ((cardHeight_mm - cardIgnoreTop_mm)/cardHeight_mm) #ifdef CROP_IMAGE_TO_CARD_ROI -#define rejectBoxWidthFraction 0.157f // observed threshold +#define rejectBoxWidthFraction 0.047f // observed threshold #else #define rejectBoxWidthFraction 0.047f // observed threshold #endif From f80a420f02f74cc82d07d50f5f41882eb0940f4b Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 22:42:08 +0900 Subject: [PATCH 4/6] Add ahv number to patient --- AmiKoDesitin/Model.xcdatamodeld/Model.xcdatamodel/contents | 1 + AmiKoDesitin/Patient.h | 2 ++ AmiKoDesitin/Patient.m | 5 ++++- AmiKoDesitin/PatientViewController+smartCard.m | 1 + PatientModel+CoreDataClass.m | 2 ++ PatientModel+CoreDataProperties.h | 1 + PatientModel+CoreDataProperties.m | 1 + 7 files changed, 12 insertions(+), 1 deletion(-) diff --git a/AmiKoDesitin/Model.xcdatamodeld/Model.xcdatamodel/contents b/AmiKoDesitin/Model.xcdatamodeld/Model.xcdatamodel/contents index e80aa09..10340a0 100644 --- a/AmiKoDesitin/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/AmiKoDesitin/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,6 +1,7 @@ + diff --git a/AmiKoDesitin/Patient.h b/AmiKoDesitin/Patient.h index 64ef688..963d24e 100644 --- a/AmiKoDesitin/Patient.h +++ b/AmiKoDesitin/Patient.h @@ -25,6 +25,7 @@ #define KEY_AMK_PAT_EMAIL @"email_address" #define KEY_AMK_PAT_HEALTH_CARD_NUMBER @"health_card_number" #define KEY_AMK_PAT_INSURANCE_GLN @"insurance_gln" +#define KEY_AMK_PAT_INSURANCE_AHV_NUMBER @"ahv_number" @interface Patient : NSObject @@ -43,6 +44,7 @@ @property (atomic, copy) NSString *emailAddress; @property (atomic, copy) NSString *healthCardNumber; @property (atomic, copy) NSString *insuranceGLN; +@property (atomic, copy) NSString *ahvNumber; // Only available when patient is read from database @property (nonatomic, strong, nullable) NSDate *timestamp; diff --git a/AmiKoDesitin/Patient.m b/AmiKoDesitin/Patient.m index 3d7f9c9..7716a83 100644 --- a/AmiKoDesitin/Patient.m +++ b/AmiKoDesitin/Patient.m @@ -26,6 +26,7 @@ @implementation Patient @synthesize emailAddress; @synthesize healthCardNumber; @synthesize insuranceGLN; +@synthesize ahvNumber; - (void)importFromDict:(NSDictionary *)dict { @@ -44,6 +45,7 @@ - (void)importFromDict:(NSDictionary *)dict emailAddress = [self getString:KEY_AMK_PAT_EMAIL orNilFromDict:dict]; healthCardNumber = [self getString:KEY_AMK_PAT_HEALTH_CARD_NUMBER orNilFromDict:dict]; insuranceGLN = [self getString:KEY_AMK_PAT_INSURANCE_GLN orNilFromDict:dict]; + ahvNumber = [self getString:KEY_AMK_PAT_INSURANCE_AHV_NUMBER orNilFromDict:dict]; } - (NSDictionary *)dictionaryRepresentation { @@ -62,7 +64,8 @@ - (void)importFromDict:(NSDictionary *)dict [patientDict setObject:self.phoneNumber ?: @"" forKey:KEY_AMK_PAT_PHONE]; [patientDict setObject:self.emailAddress ?: @"" forKey:KEY_AMK_PAT_EMAIL]; [patientDict setObject:self.healthCardNumber ?: @"" forKey:KEY_AMK_PAT_HEALTH_CARD_NUMBER]; - [patientDict setObject:self.insuranceGLN forKey:KEY_AMK_PAT_INSURANCE_GLN]; + [patientDict setObject:self.insuranceGLN ?: @"" forKey:KEY_AMK_PAT_INSURANCE_GLN]; + [patientDict setObject:self.ahvNumber ?: @"" forKey:KEY_AMK_PAT_INSURANCE_AHV_NUMBER]; return patientDict; } diff --git a/AmiKoDesitin/PatientViewController+smartCard.m b/AmiKoDesitin/PatientViewController+smartCard.m index 027eb2b..8c435ef 100644 --- a/AmiKoDesitin/PatientViewController+smartCard.m +++ b/AmiKoDesitin/PatientViewController+smartCard.m @@ -525,6 +525,7 @@ - (void)lastVideoFrame:(NSNotification *)notification incompletePatient.uniqueId = [incompletePatient generateUniqueID]; incompletePatient.healthCardNumber = savedOcr.cardNumberString; incompletePatient.insuranceGLN = [self bagNumberToInsuranceGLN:savedOcr.bagNumber]; + incompletePatient.ahvNumber = savedOcr.ahvNumber; if ([savedOcr.sexString isEqualToString:@"M"]) incompletePatient.gender = KEY_AMK_PAT_GENDER_M; diff --git a/PatientModel+CoreDataClass.m b/PatientModel+CoreDataClass.m index b091026..fa194e6 100644 --- a/PatientModel+CoreDataClass.m +++ b/PatientModel+CoreDataClass.m @@ -28,6 +28,7 @@ - (void)importFromPatient:(Patient *)p timestamp:(NSDate *)timestamp { self.emailAddress = p.emailAddress; self.healthCardNumber = p.healthCardNumber; self.insuranceGLN = p.insuranceGLN; + self.ahvNumber = p.ahvNumber; } - (Patient *)toPatient { @@ -48,6 +49,7 @@ - (Patient *)toPatient { p.timestamp = self.timestamp; p.healthCardNumber = self.healthCardNumber; p.insuranceGLN = self.insuranceGLN; + p.ahvNumber = self.ahvNumber; return p; } diff --git a/PatientModel+CoreDataProperties.h b/PatientModel+CoreDataProperties.h index ecd2ebe..e65312f 100644 --- a/PatientModel+CoreDataProperties.h +++ b/PatientModel+CoreDataProperties.h @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable, nonatomic, copy) NSString *zipCode; @property (nullable, nonatomic, copy) NSString *healthCardNumber; @property (nullable, nonatomic, copy) NSString *insuranceGLN; +@property (nullable, nonatomic, copy) NSString *ahvNumber; @end NS_ASSUME_NONNULL_END diff --git a/PatientModel+CoreDataProperties.m b/PatientModel+CoreDataProperties.m index 2efc1e6..cd4e5e0 100644 --- a/PatientModel+CoreDataProperties.m +++ b/PatientModel+CoreDataProperties.m @@ -31,5 +31,6 @@ @implementation PatientModel (CoreDataProperties) @dynamic zipCode; @dynamic healthCardNumber; @dynamic insuranceGLN; +@dynamic ahvNumber; @end From 0717b98aa8da1f11341a0ef8e42c1ce0b7af42a0 Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 22:42:24 +0900 Subject: [PATCH 5/6] Show AHV number in UI --- .../Base.lproj/PatientViewController.xib | 77 ++++++++++++------- AmiKoDesitin/PatientViewController.h | 1 + AmiKoDesitin/PatientViewController.m | 9 ++- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/AmiKoDesitin/Base.lproj/PatientViewController.xib b/AmiKoDesitin/Base.lproj/PatientViewController.xib index 3fdf8b7..4bf563c 100644 --- a/AmiKoDesitin/Base.lproj/PatientViewController.xib +++ b/AmiKoDesitin/Base.lproj/PatientViewController.xib @@ -11,6 +11,7 @@ + @@ -53,85 +54,91 @@ + - + @@ -166,7 +173,7 @@ - + @@ -178,7 +185,7 @@ - + @@ -190,7 +197,7 @@ - + @@ -202,7 +209,7 @@ - + @@ -214,7 +221,7 @@ - + @@ -223,14 +230,14 @@ - + - + @@ -242,7 +249,7 @@ - + @@ -254,7 +261,7 @@ - + @@ -266,7 +273,7 @@ - + @@ -278,7 +285,7 @@ - + @@ -290,7 +297,7 @@ - + @@ -301,6 +308,18 @@ + + + + + + + + + + + + @@ -334,7 +353,7 @@ - + diff --git a/AmiKoDesitin/PatientViewController.h b/AmiKoDesitin/PatientViewController.h index b71d1ab..3261586 100644 --- a/AmiKoDesitin/PatientViewController.h +++ b/AmiKoDesitin/PatientViewController.h @@ -32,6 +32,7 @@ IBOutlet UITextField *mEmail; IBOutlet UITextField *mHealthCardNumber; IBOutlet UITextField *mInsuranceGLN; + IBOutlet UITextField *mAHVNumber; IBOutlet UISegmentedControl *mSex; } diff --git a/AmiKoDesitin/PatientViewController.m b/AmiKoDesitin/PatientViewController.m index 6fd9fd3..0798f60 100644 --- a/AmiKoDesitin/PatientViewController.m +++ b/AmiKoDesitin/PatientViewController.m @@ -215,7 +215,8 @@ - (void) resetFieldsColors mSex.backgroundColor = mEmail.backgroundColor = mHealthCardNumber.backgroundColor = - mInsuranceGLN.backgroundColor = [UIColor secondarySystemBackgroundColor]; + mInsuranceGLN.backgroundColor = + mAHVNumber.backgroundColor = [UIColor secondarySystemBackgroundColor]; } - (UIColor *)getInvalidFieldColor @@ -266,6 +267,7 @@ - (void) resetAllFields [mEmail setText:@""]; [mHealthCardNumber setText:@""]; [mInsuranceGLN setText:@""]; + [mAHVNumber setText:@""]; [mSex setSelectedSegmentIndex:UISegmentedControlNoSegment]; mPatientUUID = nil; @@ -318,6 +320,10 @@ - (void) setAllFields:(Patient *)p if (p.insuranceGLN) { [mInsuranceGLN setText:p.insuranceGLN]; } + + if (p.ahvNumber) { + [mAHVNumber setText:p.ahvNumber]; + } if (p.uniqueId) mPatientUUID = p.uniqueId; @@ -363,6 +369,7 @@ - (Patient *) getAllFields patient.emailAddress = [mEmail text]; patient.healthCardNumber = [mHealthCardNumber text]; patient.insuranceGLN = [mInsuranceGLN text]; + patient.ahvNumber = [mAHVNumber text]; switch ([mSex selectedSegmentIndex]) { case UISegmentedControlNoSegment: From fffe6792e1d3638c4dadbb7480683de89b859c2f Mon Sep 17 00:00:00 2001 From: b123400 Date: Sat, 28 Dec 2024 22:42:33 +0900 Subject: [PATCH 6/6] Pass patient number to XML --- AmiKoDesitin/Prescription.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmiKoDesitin/Prescription.m b/AmiKoDesitin/Prescription.m index cba7f05..1d72751 100644 --- a/AmiKoDesitin/Prescription.m +++ b/AmiKoDesitin/Prescription.m @@ -239,7 +239,7 @@ - (ZurRosePrescription *)toZurRosePrescription { patient.email = self.patient.emailAddress; patient.langCode = [[MLConstants databaseLanguage] isEqual:@"de"] ? 1 : 2; patient.coverCardId = self.patient.healthCardNumber ?: @""; - patient.patientNr = self.patient.insuranceGLN; + patient.patientNr = self.patient.ahvNumber; NSMutableArray *products = [NSMutableArray array]; for (Product *m in self.medications) {