medicafe 0.240419.2__py3-none-any.whl → 0.240517.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of medicafe might be problematic. Click here for more details.

@@ -0,0 +1,280 @@
1
+ import json
2
+ import sys
3
+ import os
4
+
5
+ # Add parent directory of the project to the Python path
6
+ import sys
7
+
8
+ project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
9
+ sys.path.append(project_dir)
10
+
11
+ try:
12
+ import MediLink_ConfigLoader
13
+ except ImportError:
14
+ from MediLink import MediLink_ConfigLoader
15
+
16
+ try:
17
+ from MediLink_API_v2 import fetch_payer_name_from_api
18
+ except ImportError:
19
+ from MediLink import MediLink_API_v2
20
+ fetch_payer_name_from_api = MediLink_API_v2.fetch_payer_name_from_api
21
+
22
+ try:
23
+ from MediBot import MediBot_Preprocessor_lib
24
+ except ImportError:
25
+ import MediBot_Preprocessor_lib
26
+
27
+ def check_and_initialize_crosswalk(config):
28
+ """
29
+ Checks if the 'payer_id' key exists in the crosswalk. If not, prompts the user
30
+ to initialize the crosswalk.
31
+
32
+ Args:
33
+ config (dict): Configuration settings.
34
+
35
+ Returns:
36
+ boolean: True if succeeded.
37
+ """
38
+ # Reload for safety
39
+ config, crosswalk = MediLink_ConfigLoader.load_configuration(None, config.get('crosswalkPath', 'crosswalk.json'))
40
+
41
+ try:
42
+ # Attempt to access the 'payer_id' key to ensure it exists
43
+ if 'payer_id' not in crosswalk:
44
+ raise KeyError("Missing 'payer_id' key in crosswalk.")
45
+ except KeyError:
46
+ error_message = "The 'payer_id' key does not exist in the crosswalk configuration. \n" \
47
+ "This could be because the crosswalk is not initialized. \n" \
48
+ "Consider running the Crosswalk initializer."
49
+ print(error_message)
50
+ MediLink_ConfigLoader.log(error_message, config, level="ERROR")
51
+
52
+ # Prompt user to initialize crosswalk
53
+ initialize_choice = input("\nADVANCED OPTION: The crosswalk may not be initialized. \nType 'yes' to initialize it now: ").strip().lower()
54
+ if initialize_choice == 'yes':
55
+ initialize_crosswalk_from_mapat()
56
+ _, crosswalk = MediLink_ConfigLoader.load_configuration() # Reload crosswalk
57
+ MediLink_ConfigLoader.log("Crosswalk reloaded successfully.", config, level="INFO")
58
+ else:
59
+ raise KeyError(error_message)
60
+
61
+ return True
62
+
63
+ def validate_and_correct_payer_ids(crosswalk, config):
64
+ """Validates payer IDs via API and handles invalid IDs through user intervention."""
65
+ for payer_id in list(crosswalk['payer_id'].keys()):
66
+ try:
67
+ fetch_payer_name_from_api(payer_id, config, primary_endpoint=None)
68
+ MediLink_ConfigLoader.log("Payer ID {} validated successfully.".format(payer_id), config, level="INFO")
69
+ except Exception as e:
70
+ MediLink_ConfigLoader.log("Payer ID validation failed for {}: {}".format(payer_id, e), config, level="WARNING")
71
+
72
+ while True:
73
+ corrected_payer_id = input("WWARNING: Invalid Payer ID {}. Enter the correct Payer ID for replacement or type 'FORCE' to continue with the unresolved Payer ID: ".format(payer_id))
74
+
75
+ if corrected_payer_id.strip().upper() == 'FORCE':
76
+ MediLink_ConfigLoader.log("User opted to force-continue with unresolved Payer ID {}. Warning: This may indicate an underlying issue.".format(payer_id), config, level="WARNING")
77
+ break
78
+
79
+ if corrected_payer_id:
80
+ try:
81
+ fetch_payer_name_from_api(corrected_payer_id, config, primary_endpoint=None)
82
+ MediLink_ConfigLoader.log("Corrected Payer ID {} validated successfully.".format(corrected_payer_id), config, level="INFO")
83
+
84
+ if update_crosswalk_with_corrected_payer_id(payer_id, corrected_payer_id, config, crosswalk):
85
+ if 'csv_replacements' not in crosswalk:
86
+ crosswalk['csv_replacements'] = {}
87
+ crosswalk['csv_replacements'][payer_id] = corrected_payer_id
88
+ MediLink_ConfigLoader.log("Added replacement filter: {} -> {}".format(payer_id, corrected_payer_id), config, level="INFO")
89
+ else:
90
+ print("Failed to update crosswalk with the corrected Payer ID {}.".format(corrected_payer_id))
91
+ MediLink_ConfigLoader.log("Failed to update crosswalk with the corrected Payer ID {}.".format(corrected_payer_id), config, level="ERROR")
92
+ break
93
+ except Exception as e:
94
+ print("Corrected Payer ID {} validation failed: {}".format(corrected_payer_id, e))
95
+ MediLink_ConfigLoader.log("Corrected Payer ID {} validation failed: {}".format(corrected_payer_id, e), config, level="ERROR")
96
+ else:
97
+ print("Exiting initialization. Please correct the Payer ID and retry.")
98
+ sys.exit(1)
99
+
100
+ def initialize_crosswalk_from_mapat():
101
+ """
102
+ Input: Historical Carol's CSVs and MAPAT data.
103
+
104
+ Process:
105
+ Extract mappings from Carol's old CSVs to identify Payer IDs and associated Patient IDs.
106
+ Use MAPAT to correlate these Patient IDs with Insurance IDs.
107
+ Compile these mappings into the crosswalk, setting Payer IDs as keys and corresponding Insurance IDs as values.
108
+
109
+ Output: A fully populated crosswalk.json file that serves as a baseline for future updates.
110
+ """
111
+ config, crosswalk = MediLink_ConfigLoader.load_configuration()
112
+
113
+ # Load historical mappings
114
+ try:
115
+ patient_id_to_insurance_id, payer_id_to_patient_ids = MediBot_Preprocessor_lib.load_data_sources(config, crosswalk)
116
+ except ValueError as e:
117
+ print(e)
118
+ sys.exit(1)
119
+
120
+ # Map Payer IDs to Insurance IDs
121
+ payer_id_to_details = MediBot_Preprocessor_lib.map_payer_ids_to_insurance_ids(patient_id_to_insurance_id, payer_id_to_patient_ids)
122
+
123
+ # Update the crosswalk for payer IDs only, retaining other mappings
124
+ crosswalk['payer_id'] = payer_id_to_details
125
+
126
+ # Validate payer IDs via API and handle invalid IDs
127
+ validate_and_correct_payer_ids(crosswalk, config)
128
+
129
+ # Save the initial crosswalk
130
+ if save_crosswalk(config['MediLink_Config']['crosswalkPath'], crosswalk):
131
+ message = "Crosswalk initialized with mappings for {} payers.".format(len(crosswalk.get('payer_id', {})))
132
+ print(message)
133
+ MediLink_ConfigLoader.log(message, config, level="INFO")
134
+ else:
135
+ print("Failed to save the crosswalk.")
136
+ sys.exit(1)
137
+ return payer_id_to_details
138
+
139
+ def crosswalk_update(config, crosswalk):
140
+ """
141
+ Updates the `crosswalk.json` file using mappings from MAINS, Z.dat, and Carol's CSV. This function integrates
142
+ user-defined insurance mappings from Z.dat with existing payer-to-insurance mappings in the crosswalk,
143
+ and validates these mappings using MAINS.
144
+
145
+ Steps:
146
+ 1. Load mappings from MAINS for translating insurance names to IDs.
147
+ 2. Load mappings from the latest Carol's CSV for new patient entries mapping Patient IDs to Payer IDs.
148
+ 3. Parse incremental data from Z.dat which contains recent user interactions mapping Patient IDs to Insurance Names.
149
+ 4. Update the crosswalk using the loaded and parsed data, ensuring each Payer ID maps to the correct Insurance IDs.
150
+ 5. Persist the updated mappings back to the crosswalk file.
151
+
152
+ Args:
153
+ config (dict): Configuration dictionary containing paths and other settings.
154
+ crosswalk (dict): Existing crosswalk mapping Payer IDs to sets of Insurance IDs.
155
+
156
+ Returns:
157
+ bool: True if the crosswalk was successfully updated and saved, False otherwise.
158
+ """
159
+ # Load insurance mappings from MAINS (Insurance Name to Insurance ID)
160
+ insurance_name_to_id = MediBot_Preprocessor_lib.load_insurance_data_from_mains(config)
161
+ MediLink_ConfigLoader.log("Loaded insurance data from MAINS...")
162
+
163
+ # Load new Patient ID to Payer ID mappings from Carol's CSV (if necessary)
164
+ # TODO This is a low performance strategy.
165
+ patient_id_to_payer_id = MediBot_Preprocessor_lib.load_historical_payer_to_patient_mappings(config)
166
+ MediLink_ConfigLoader.log("Loaded historical mappings...")
167
+
168
+ # Load incremental mapping data from Z.dat (Patient ID to Insurance Name)
169
+ # TODO This may be a redundant approach?
170
+ patient_id_to_insurance_name = MediBot_Preprocessor_lib.parse_z_dat(config['MediLink_Config']['Z_DAT_PATH'], config['MediLink_Config'])
171
+ MediLink_ConfigLoader.log("Parsed Z data...")
172
+
173
+ # Update the crosswalk with new or revised mappings
174
+ for patient_id, payer_id in patient_id_to_payer_id.items():
175
+ insurance_name = patient_id_to_insurance_name.get(patient_id)
176
+ if insurance_name and insurance_name in insurance_name_to_id:
177
+ insurance_id = insurance_name_to_id[insurance_name]
178
+
179
+ # Ensure payer ID is in the crosswalk and initialize if not
180
+ MediLink_ConfigLoader.log("Initializing payer_id key...")
181
+ if 'payer_id' not in crosswalk:
182
+ crosswalk['payer_id'] = {}
183
+ if payer_id not in crosswalk['payer_id']:
184
+ # TODO The OPTUMEDI default here should be gathered via API and not just a default. There are 2 of these defaults!!
185
+ crosswalk['payer_id'][payer_id] = {'endpoint': 'OPTUMEDI', 'medisoft_id': set(), 'medisoft_medicare_id': set()}
186
+
187
+ # Update the medisoft_id set, temporarily using a set to avoid duplicates
188
+ crosswalk['payer_id'][payer_id]['medisoft_id'].add(insurance_id)
189
+ MediLink_ConfigLoader.log("Added new insurance ID {} to payer ID {}".format(insurance_id, payer_id))
190
+
191
+ # Convert sets to lists just before saving
192
+ for payer_id in crosswalk['payer_id']:
193
+ if isinstance(crosswalk['payer_id'][payer_id]['medisoft_id'], set):
194
+ crosswalk['payer_id'][payer_id]['medisoft_id'] = list(crosswalk['payer_id'][payer_id]['medisoft_id'])
195
+ if isinstance(crosswalk['payer_id'][payer_id]['medisoft_medicare_id'], set):
196
+ crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = list(crosswalk['payer_id'][payer_id]['medisoft_medicare_id'])
197
+
198
+ # Save the updated crosswalk to the specified file
199
+ return save_crosswalk(config['MediLink_Config']['crosswalkPath'], crosswalk)
200
+
201
+ def update_crosswalk_with_corrected_payer_id(old_payer_id, corrected_payer_id, config, crosswalk):
202
+ """Updates the crosswalk with the corrected payer ID."""
203
+ # Update the payer_id section
204
+ if old_payer_id in crosswalk['payer_id']:
205
+ crosswalk['payer_id'][corrected_payer_id] = crosswalk['payer_id'].pop(old_payer_id)
206
+ MediLink_ConfigLoader.log("Crosswalk updated: replaced Payer ID {} with {}".format(old_payer_id, corrected_payer_id), config, level="INFO")
207
+ else:
208
+ MediLink_ConfigLoader.log("Failed to update crosswalk: could not find old Payer ID {}".format(old_payer_id), config, level="ERROR")
209
+ return False
210
+
211
+ # Update the csv_replacements section
212
+ if 'csv_replacements' not in crosswalk:
213
+ crosswalk['csv_replacements'] = {}
214
+ crosswalk['csv_replacements'][old_payer_id] = corrected_payer_id
215
+ MediLink_ConfigLoader.log("Crosswalk csv_replacements updated: added {} -> {}".format(old_payer_id, corrected_payer_id), config, level="INFO")
216
+
217
+ # Save the updated crosswalk
218
+ return save_crosswalk(config['MediLink_Config']['crosswalkPath'], crosswalk)
219
+
220
+ def update_crosswalk_with_new_payer_id(insurance_name, payer_id, config):
221
+ """Updates the crosswalk with a new payer ID."""
222
+ _, crosswalk = MediLink_ConfigLoader.load_configuration(None, config.get('crosswalkPath', 'crosswalk.json'))
223
+ medisoft_id = MediBot_Preprocessor_lib.load_insurance_data_from_mains(config).get(insurance_name)
224
+
225
+ if medisoft_id:
226
+ medisoft_id_str = str(medisoft_id)
227
+ if payer_id not in crosswalk['payer_id']:
228
+ crosswalk['payer_id'][payer_id] = {"medisoft_id": [medisoft_id_str], "medisoft_medicare_id": []}
229
+ else:
230
+ crosswalk['payer_id'][payer_id]['medisoft_id'].append(medisoft_id_str)
231
+ save_crosswalk(config['MediLink_Config']['crosswalkPath'], crosswalk)
232
+ MediLink_ConfigLoader.log("Updated crosswalk with new payer ID {} for insurance name {}".format(payer_id, insurance_name), config, level="INFO")
233
+ else:
234
+ message = "Failed to update crosswalk: Medisoft ID not found for insurance name {}".format(insurance_name)
235
+ print(message)
236
+ MediLink_ConfigLoader.log(message, config, level="ERROR")
237
+
238
+ def save_crosswalk(crosswalk_path, crosswalk):
239
+ """
240
+ Saves the updated crosswalk to a JSON file.
241
+ Args:
242
+ crosswalk_path (str): Path to the crosswalk.json file.
243
+ crosswalk (dict): The updated crosswalk data.
244
+ Returns:
245
+ bool: True if the file was successfully saved, False otherwise.
246
+ """
247
+ try:
248
+ # Initialize 'payer_id' key if not present
249
+ if 'payer_id' not in crosswalk:
250
+ print("save_crosswalk is initializing 'payer_id' key...")
251
+ crosswalk['payer_id'] = {}
252
+
253
+ # Convert all 'medisoft_id' fields from sets to lists if necessary
254
+ for k, v in crosswalk.get('payer_id', {}).items():
255
+ if isinstance(v.get('medisoft_id'), set):
256
+ v['medisoft_id'] = list(v['medisoft_id'])
257
+
258
+ with open(crosswalk_path, 'w') as file:
259
+ json.dump(crosswalk, file, indent=4) # Save the entire dictionary
260
+ return True
261
+
262
+ except KeyError as e:
263
+ # Log the KeyError with specific information about what was missing
264
+ print("Key Error: A required key is missing in the crosswalk data -", e)
265
+ return False
266
+
267
+ except TypeError as e:
268
+ # Handle data type errors (e.g., non-serializable types)
269
+ print("Type Error: There was a type issue with the data being saved in the crosswalk -", e)
270
+ return False
271
+
272
+ except IOError as e:
273
+ # Handle I/O errors related to file operations
274
+ print("I/O Error: An error occurred while writing to the crosswalk file -", e)
275
+ return False
276
+
277
+ except Exception as e:
278
+ # A general exception catch to log any other exceptions that may not have been anticipated
279
+ print("Unexpected crosswalk error:", e)
280
+ return False