medicafe 0.250725.8__tar.gz → 0.250725.10__tar.gz

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.

Files changed (59) hide show
  1. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot.py +1 -35
  2. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_UI.py +326 -408
  3. {medicafe-0.250725.8 → medicafe-0.250725.10}/PKG-INFO +1 -1
  4. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/PKG-INFO +1 -1
  5. {medicafe-0.250725.8 → medicafe-0.250725.10}/setup.py +1 -1
  6. {medicafe-0.250725.8 → medicafe-0.250725.10}/LICENSE +0 -0
  7. {medicafe-0.250725.8 → medicafe-0.250725.10}/MANIFEST.in +0 -0
  8. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot.bat +0 -0
  9. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_Charges.py +0 -0
  10. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_Crosswalk_Library.py +0 -0
  11. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_Post.py +0 -0
  12. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_Preprocessor.py +0 -0
  13. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_Preprocessor_lib.py +0 -0
  14. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_dataformat_library.py +0 -0
  15. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/MediBot_docx_decoder.py +0 -0
  16. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/PDF_to_CSV_Cleaner.py +0 -0
  17. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/__init__.py +0 -0
  18. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/update_json.py +0 -0
  19. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediBot/update_medicafe.py +0 -0
  20. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink.py +0 -0
  21. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_837p_cob_library.py +0 -0
  22. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_837p_encoder.py +0 -0
  23. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_837p_encoder_library.py +0 -0
  24. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_837p_utilities.py +0 -0
  25. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_API_Generator.py +0 -0
  26. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_API_v2.py +0 -0
  27. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_API_v3.py +0 -0
  28. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_APIs.py +0 -0
  29. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Azure.py +0 -0
  30. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_ClaimStatus.py +0 -0
  31. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_ConfigLoader.py +0 -0
  32. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_DataMgmt.py +0 -0
  33. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Decoder.py +0 -0
  34. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Deductible.py +0 -0
  35. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Deductible_Validator.py +0 -0
  36. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Down.py +0 -0
  37. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Gmail.py +0 -0
  38. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_GraphQL.py +0 -0
  39. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Mailer.py +0 -0
  40. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Parser.py +0 -0
  41. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Scan.py +0 -0
  42. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Scheduler.py +0 -0
  43. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_UI.py +0 -0
  44. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_Up.py +0 -0
  45. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/MediLink_batch.bat +0 -0
  46. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/Soumit_api.py +0 -0
  47. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/__init__.py +0 -0
  48. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/openssl.cnf +0 -0
  49. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/test.py +0 -0
  50. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/test_cob_library.py +0 -0
  51. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/test_validation.py +0 -0
  52. {medicafe-0.250725.8 → medicafe-0.250725.10}/MediLink/webapp.html +0 -0
  53. {medicafe-0.250725.8 → medicafe-0.250725.10}/README.md +0 -0
  54. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/SOURCES.txt +0 -0
  55. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/dependency_links.txt +0 -0
  56. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/not-zip-safe +0 -0
  57. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/requires.txt +0 -0
  58. {medicafe-0.250725.8 → medicafe-0.250725.10}/medicafe.egg-info/top_level.txt +0 -0
  59. {medicafe-0.250725.8 → medicafe-0.250725.10}/setup.cfg +0 -0
@@ -1,5 +1,5 @@
1
1
  #MediBot.py
2
- import subprocess, os, tempfile, traceback, re, sys, time, msvcrt, ctypes
2
+ import subprocess, os, tempfile, traceback, re, sys, time
3
3
  from collections import OrderedDict
4
4
  import MediBot_dataformat_library
5
5
  import MediBot_Preprocessor
@@ -12,35 +12,7 @@ sys.path.append(project_dir)
12
12
 
13
13
  from MediLink import MediLink_ConfigLoader
14
14
 
15
- def flush_console_input_buffer():
16
- """
17
- Remove any keystrokes already sitting in the console input buffer.
18
- Necessary on Windows XP where a stray CR/LF can remain after the
19
- script is launched.
20
- """
21
- if sys.platform != "win32":
22
- return # no-op on non-Windows
23
15
 
24
- try:
25
- kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
26
- STD_INPUT_HANDLE = -10
27
- h_stdin = kernel32.GetStdHandle(STD_INPUT_HANDLE)
28
- kernel32.FlushConsoleInputBuffer(h_stdin)
29
- except Exception:
30
- # Fallback: drain any leading newlines manually
31
- while msvcrt.kbhit():
32
- msvcrt.getch()
33
-
34
- def discard_leading_blank():
35
- """
36
- Alternative fallback that uses non-blocking msvcrt to drain any available input.
37
- """
38
- if sys.platform != "win32":
39
- return # no-op on non-Windows
40
-
41
- # Use non-blocking msvcrt approach to drain any available input
42
- while msvcrt.kbhit():
43
- msvcrt.getch()
44
16
 
45
17
  try:
46
18
  from MediBot_Crosswalk_Library import crosswalk_update
@@ -430,9 +402,6 @@ if __name__ == "__main__":
430
402
  sys.stdout.flush()
431
403
  time.sleep(0.2) # Increased delay for Windows XP
432
404
 
433
- # Flush console input buffer to remove any stray CR/LF
434
- flush_console_input_buffer()
435
-
436
405
  # Use input() for more reliable input on Windows XP
437
406
  proceed = input().lower().strip() in ['yes', 'y']
438
407
  else:
@@ -441,9 +410,6 @@ if __name__ == "__main__":
441
410
  sys.stdout.flush()
442
411
  time.sleep(0.2) # Increased delay for Windows XP
443
412
 
444
- # Flush console input buffer to remove any stray CR/LF
445
- flush_console_input_buffer()
446
-
447
413
  # Use input() for more reliable input on Windows XP
448
414
  proceed = input().lower().strip() in ['yes', 'y']
449
415
 
@@ -1,409 +1,327 @@
1
- #MediBot_UI.py
2
- import ctypes, time, re, os, sys, msvcrt
3
- from ctypes import wintypes
4
- from sys import exit
5
- project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
6
- if project_dir not in sys.path:
7
- sys.path.append(project_dir)
8
-
9
- try:
10
- from MediLink import MediLink_ConfigLoader
11
- except ImportError:
12
- import MediLink_ConfigLoader
13
-
14
- # Load configuration
15
- config, crosswalk = MediLink_ConfigLoader.load_configuration()
16
-
17
- # Function to check if a specific key is pressed
18
- VK_END = int(config.get('VK_END', ""), 16) # Try F12 (7B). Virtual key code for 'End' (23)
19
- VK_PAUSE = int(config.get('VK_PAUSE', ""), 16) # Try F11 (7A). Virtual-key code for 'Home' (24)
20
-
21
- def flush_console_input_buffer():
22
- """
23
- Remove any keystrokes already sitting in the console input buffer.
24
- Necessary on Windows XP where a stray CR/LF can remain after the
25
- script is launched.
26
- """
27
- if sys.platform != "win32":
28
- return # no-op on non-Windows
29
-
30
- print("DEBUG_FLUSH: Starting flush_console_input_buffer()")
31
-
32
- # Check what's in the buffer before flushing
33
- chars_in_buffer = []
34
- while msvcrt.kbhit():
35
- ch = msvcrt.getch()
36
- chars_in_buffer.append(ch)
37
-
38
- if chars_in_buffer:
39
- print("DEBUG_FLUSH: Found {} chars in buffer before flush: {}".format(len(chars_in_buffer), chars_in_buffer))
40
- else:
41
- print("DEBUG_FLUSH: No chars found in buffer before flush")
42
-
43
- try:
44
- kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
45
- STD_INPUT_HANDLE = -10
46
- h_stdin = kernel32.GetStdHandle(STD_INPUT_HANDLE)
47
- result = kernel32.FlushConsoleInputBuffer(h_stdin)
48
- print("DEBUG_FLUSH: FlushConsoleInputBuffer API call result: {}".format(result))
49
- except Exception as e:
50
- print("DEBUG_FLUSH: FlushConsoleInputBuffer API failed: {}".format(e))
51
- # Fallback: drain any leading newlines manually
52
- fallback_chars = []
53
- while msvcrt.kbhit():
54
- ch = msvcrt.getch()
55
- fallback_chars.append(ch)
56
- if fallback_chars:
57
- print("DEBUG_FLUSH: Fallback removed {} chars: {}".format(len(fallback_chars), fallback_chars))
58
- else:
59
- print("DEBUG_FLUSH: Fallback found no chars to remove")
60
-
61
- print("DEBUG_FLUSH: flush_console_input_buffer() completed")
62
-
63
- def robust_input(prompt="", max_retries=3):
64
- """
65
- Robust input function that handles Windows XP console timing issues.
66
- Automatically retries if the first input returns empty.
67
- """
68
- print("DEBUG_ROBUST: Starting robust_input with prompt: '{}'".format(prompt))
69
-
70
- for attempt in range(max_retries):
71
- if attempt > 0:
72
- print("DEBUG_ROBUST: Retry attempt {} for input".format(attempt + 1))
73
- flush_console_input_buffer()
74
-
75
- # Print prompt only on first attempt, or if it's a retry
76
- if attempt == 0:
77
- print("DEBUG_ROBUST: First attempt - calling input(prompt)")
78
- response = input(prompt).strip()
79
- else:
80
- print("DEBUG_ROBUST: Retry attempt - calling input() without prompt")
81
- response = input().strip()
82
-
83
- print("DEBUG_ROBUST: Attempt {} got: '{}' (length: {})".format(attempt + 1, response, len(response)))
84
-
85
- if response:
86
- print("DEBUG_ROBUST: Success! Returning: '{}'".format(response))
87
- return response
88
-
89
- # If all attempts failed, return empty string
90
- print("DEBUG_ROBUST: All {} attempts failed, returning empty string".format(max_retries))
91
- return ""
92
-
93
- def discard_leading_blank():
94
- """
95
- Alternative fallback that uses non-blocking msvcrt to drain any available input.
96
- """
97
- if sys.platform != "win32":
98
- return # no-op on non-Windows
99
-
100
- # Use non-blocking msvcrt approach to drain any available input
101
- while msvcrt.kbhit():
102
- msvcrt.getch()
103
-
104
- class AppControl:
105
- def __init__(self):
106
- self.script_paused = False
107
- self.mapat_med_path = ''
108
- self.medisoft_shortcut = ''
109
- # PERFORMANCE FIX: Add configuration caching to reduce lookup overhead
110
- self._config_cache = {} # Cache for Medicare vs Private configuration lookups
111
- # Load initial paths from config when instance is created
112
- self.load_paths_from_config()
113
-
114
- def get_pause_status(self):
115
- return self.script_paused
116
-
117
- def set_pause_status(self, status):
118
- self.script_paused = status
119
-
120
- def get_mapat_med_path(self):
121
- return self.mapat_med_path
122
-
123
- def set_mapat_med_path(self, path):
124
- self.mapat_med_path = path
125
-
126
- def get_medisoft_shortcut(self):
127
- return self.medisoft_shortcut
128
-
129
- def set_medisoft_shortcut(self, path):
130
- self.medisoft_shortcut = path
131
-
132
- def load_paths_from_config(self, medicare=False):
133
- # Assuming `config` is a module or a globally accessible configuration dictionary
134
- # TODO Is this where the MAINS paths should also be set?
135
-
136
- # PERFORMANCE FIX: Cache configuration lookups to reduce Medicare vs Private overhead
137
- cache_key = 'medicare' if medicare else 'private'
138
-
139
- if cache_key not in self._config_cache:
140
- # Build cache entry for this configuration type
141
- if medicare:
142
- cached_config = {
143
- 'mapat_path': config.get('MEDICARE_MAPAT_MED_PATH', ""),
144
- 'shortcut': config.get('MEDICARE_SHORTCUT', "")
145
- }
146
- else:
147
- cached_config = {
148
- 'mapat_path': config.get('MAPAT_MED_PATH', ""),
149
- 'shortcut': config.get('PRIVATE_SHORTCUT', "")
150
- }
151
- self._config_cache[cache_key] = cached_config
152
-
153
- # Use cached values to avoid repeated config lookups
154
- cached = self._config_cache[cache_key]
155
- self.mapat_med_path = cached['mapat_path']
156
- self.medisoft_shortcut = cached['shortcut']
157
-
158
- app_control = AppControl()
159
-
160
- def is_key_pressed(key_code):
161
- user32 = ctypes.WinDLL('user32', use_last_error=True)
162
- user32.GetAsyncKeyState.restype = wintypes.SHORT
163
- user32.GetAsyncKeyState.argtypes = [wintypes.INT]
164
- return user32.GetAsyncKeyState(key_code) & 0x8000 != 0
165
-
166
- def manage_script_pause(csv_data, error_message, reverse_mapping):
167
- user_action = 0 # initialize as 'continue'
168
-
169
- if not app_control.get_pause_status() and is_key_pressed(VK_PAUSE):
170
- app_control.set_pause_status(True)
171
- print("Script paused. Opening menu...")
172
- interaction_mode = 'normal' # Assuming normal interaction mode for script pause
173
- user_action = user_interaction(csv_data, interaction_mode, error_message, reverse_mapping)
174
-
175
- while app_control.get_pause_status():
176
- if is_key_pressed(VK_END):
177
- app_control.set_pause_status(False)
178
- print("Continuing...")
179
- elif is_key_pressed(VK_PAUSE):
180
- user_action = user_interaction(csv_data, 'normal', error_message, reverse_mapping)
181
- time.sleep(0.1)
182
-
183
- return user_action
184
-
185
- # Menu Display & User Interaction
186
- def display_patient_selection_menu(csv_data, reverse_mapping, proceed_as_medicare):
187
- selected_patient_ids = []
188
- selected_indices = []
189
-
190
- def display_menu_header(title):
191
- print("\n" + "-" * 60)
192
- print(title)
193
- print("-" * 60)
194
-
195
- def display_patient_list(csv_data, reverse_mapping, medicare_filter=False, exclude_medicare=False):
196
- medicare_policy_pattern = r"^[a-zA-Z0-9]{11}$" # Regex pattern for 11 alpha-numeric characters
197
- primary_policy_number_header = reverse_mapping.get('Primary Policy Number', 'Primary Policy Number')
198
- primary_insurance_header = reverse_mapping.get('Primary Insurance', 'Primary Insurance') # Adjust field name as needed
199
-
200
- displayed_indices = []
201
- displayed_patient_ids = []
202
-
203
- for index, row in enumerate(csv_data):
204
- policy_number = row.get(primary_policy_number_header, "")
205
- primary_insurance = row.get(primary_insurance_header, "").upper()
206
-
207
- if medicare_filter and (not re.match(medicare_policy_pattern, policy_number) or "MEDICARE" not in primary_insurance):
208
- continue
209
- if exclude_medicare and re.match(medicare_policy_pattern, policy_number) and "MEDICARE" in primary_insurance:
210
- continue
211
-
212
- patient_id_header = reverse_mapping['Patient ID #2']
213
- patient_name_header = reverse_mapping['Patient Name']
214
- patient_id = row.get(patient_id_header, "N/A")
215
- patient_name = row.get(patient_name_header, "Unknown")
216
- surgery_date = row.get('Surgery Date', "Unknown Date") # Access 'Surgery Date' as string directly from the row
217
-
218
- print("{0:03d}: {3:%m-%d} (ID: {2}) {1} ".format(index+1, patient_name, patient_id, surgery_date))
219
-
220
- displayed_indices.append(index)
221
- displayed_patient_ids.append(patient_id)
222
-
223
- return displayed_indices, displayed_patient_ids
224
-
225
- if proceed_as_medicare:
226
- display_menu_header("MEDICARE Patient Selection for Today's Data Entry")
227
- selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping, medicare_filter=True)
228
- else:
229
- display_menu_header("PRIVATE Patient Selection for Today's Data Entry")
230
- selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping, exclude_medicare=True)
231
-
232
- print("-" * 60)
233
- print("\nDo you want to proceed with the selected patients? (yes/no): ", end='', flush=True)
234
- # Force flush and wait for Windows XP console buffer synchronization
235
- sys.stdout.flush()
236
- time.sleep(0.2) # Increased delay for Windows XP
237
-
238
- # Flush console input buffer to remove any stray CR/LF
239
- flush_console_input_buffer()
240
-
241
- # Use input() for more reliable input on Windows XP
242
- proceed = input().lower().strip() in ['yes', 'y']
243
-
244
- if not proceed:
245
- display_menu_header("Patient Selection for Today's Data Entry")
246
- selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping)
247
- print("-" * 60)
248
-
249
- while True:
250
- while True:
251
- print("\nEnter the number(s) of the patients you wish to proceed with \n(e.g., 1,3,5): ", end='', flush=True)
252
- # Force flush and wait for Windows XP console buffer synchronization
253
- sys.stdout.flush()
254
- time.sleep(0.2) # Increased delay for Windows XP
255
-
256
- # Flush console input buffer to remove any stray CR/LF
257
- flush_console_input_buffer()
258
-
259
- # Use input() for more reliable input on Windows XP
260
- selection = input().strip()
261
- if not selection:
262
- print("Invalid entry. Please provide at least one number.")
263
- continue
264
-
265
- selection = selection.replace('.', ',') # Replace '.' with ',' in the user input just in case
266
- selected_indices = [int(x.strip()) - 1 for x in selection.split(',') if x.strip().isdigit()]
267
-
268
- if not selected_indices:
269
- print("Invalid entry. Please provide at least one integer.")
270
- continue
271
-
272
- proceed = True
273
- break
274
-
275
- if not selection:
276
- print("Invalid entry. Please provide at least one number.")
277
- continue
278
-
279
- selection = selection.replace('.', ',') # Replace '.' with ',' in the user input just in case
280
- selected_indices = [int(x.strip()) - 1 for x in selection.split(',') if x.strip().isdigit()]
281
-
282
- if not selected_indices:
283
- print("Invalid entry. Please provide at least one integer.")
284
- continue
285
-
286
- proceed = True
287
- break
288
-
289
- patient_id_header = reverse_mapping['Patient ID #2']
290
- selected_patient_ids = [csv_data[i][patient_id_header] for i in selected_indices if i < len(csv_data)]
291
-
292
- return proceed, selected_patient_ids, selected_indices
293
-
294
- def display_menu_header(title):
295
- print("\n" + "-" * 60)
296
- print(title)
297
- print("-" * 60)
298
- # Force flush for Windows XP compatibility
299
- sys.stdout.flush()
300
-
301
- def handle_user_interaction(interaction_mode, error_message):
302
- # Import here to avoid circular imports
303
- try:
304
- from MediBot import current_patient_context
305
- except ImportError:
306
- current_patient_context = None
307
-
308
- while True:
309
- # If interaction_mode is neither 'triage' nor 'error', then it's normal mode.
310
- title = "Error Occurred" if interaction_mode == 'error' else "Data Entry Options"
311
- display_menu_header(title)
312
-
313
- if interaction_mode == 'error':
314
- print("\nERROR: ", error_message)
315
-
316
- # PERFORMANCE FIX: Display patient context to address "won't be obvious anymore" issue
317
- # Show user which patient and field they're working with for better F11 menu usability
318
- if current_patient_context:
319
- patient_name = current_patient_context.get('patient_name', 'Unknown Patient')
320
- surgery_date = current_patient_context.get('surgery_date', 'Unknown Date')
321
- last_field = current_patient_context.get('last_field', 'Unknown Field')
322
- print("\nCurrent Context:")
323
- print(" Patient: {}".format(patient_name))
324
- print(" Surgery Date: {}".format(surgery_date))
325
- print(" Last Field: {}".format(last_field))
326
- print("")
327
-
328
- # Menu options with improved context
329
- print("1: Retry last entry")
330
- print("2: Skip to next patient and continue")
331
- print("3: Go back two patients and redo")
332
- print("4: Exit script")
333
- print("-" * 60)
334
- print("Enter your choice (1/2/3/4): ", end='', flush=True)
335
- # Force flush and wait for Windows XP console buffer synchronization
336
- sys.stdout.flush()
337
- time.sleep(0.2) # Increased delay for Windows XP
338
-
339
- # Flush console input buffer to remove any stray CR/LF
340
- flush_console_input_buffer()
341
-
342
- # Use input() for more reliable input on Windows XP
343
- choice = input().strip()
344
-
345
- if choice == '1':
346
- print("Selected: 'Retry last entry'. Please press 'F12' to continue.")
347
- return -1
348
- elif choice == '2':
349
- print("Selected: 'Skip to next patient and continue'. Please press 'F12' to continue.")
350
- return 1
351
- elif choice == '3':
352
- print("Selected: 'Go back two patients and redo'. Please press 'F12' to continue.")
353
- # Returning a specific value to indicate the action of going back two patients
354
- # but we might run into a problem if we stop mid-run on the first row?
355
- return -2
356
- elif choice == '4':
357
- print("Exiting the script.")
358
- exit()
359
- else:
360
- print("Invalid choice. Please enter a valid number.")
361
-
362
- def user_interaction(csv_data, interaction_mode, error_message, reverse_mapping):
363
- global app_control # Use the instance of AppControl
364
- selected_patient_ids = []
365
- selected_indices = []
366
-
367
- if interaction_mode == 'triage':
368
- display_menu_header(" =(^.^)= Welcome to MediBot! =(^.^)=")
369
-
370
- # Force flush for Windows XP compatibility
371
- sys.stdout.flush()
372
-
373
- # Flush console input buffer to remove any stray CR/LF from script launch
374
- flush_console_input_buffer()
375
-
376
- while True:
377
- try:
378
- print("DEBUG_MAIN: About to call robust_input() - waiting for user...")
379
- # Use robust_input() for more reliable input on Windows XP
380
- response = robust_input("\nAm I processing Medicare patients? (yes/no): ").lower()
381
-
382
- # Debug: Print what we actually got
383
- print("DEBUG_MAIN: Final response: '{}' (length: {})".format(response, len(response)))
384
- print("DEBUG_MAIN: Response type: {}, repr: {}".format(type(response), repr(response)))
385
-
386
- if response:
387
- if response in ['yes', 'y']:
388
- app_control.load_paths_from_config(medicare=True)
389
- break
390
- elif response in ['no', 'n']:
391
- app_control.load_paths_from_config(medicare=False)
392
- break
393
- else:
394
- print("Invalid entry. Please enter 'yes' or 'no'.")
395
- else:
396
- print("A response is required. Please try again.")
397
- except KeyboardInterrupt:
398
- print("\nOperation cancelled by user. Exiting script.")
399
- exit()
400
-
401
- fixed_values = config.get('fixed_values', {}) # Get fixed values from config json
402
- if response in ['yes', 'y']:
403
- medicare_added_fixed_values = config.get('medicare_added_fixed_values', {})
404
- fixed_values.update(medicare_added_fixed_values) # Add any medicare-specific fixed values from config
405
-
406
- proceed, selected_patient_ids, selected_indices = display_patient_selection_menu(csv_data, reverse_mapping, response in ['yes', 'y'])
407
- return proceed, selected_patient_ids, selected_indices, fixed_values
408
-
1
+ #MediBot_UI.py
2
+ import ctypes, time, re, os, sys, msvcrt
3
+ from ctypes import wintypes
4
+ from sys import exit
5
+ project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
6
+ if project_dir not in sys.path:
7
+ sys.path.append(project_dir)
8
+
9
+ try:
10
+ from MediLink import MediLink_ConfigLoader
11
+ except ImportError:
12
+ import MediLink_ConfigLoader
13
+
14
+ # Load configuration
15
+ config, crosswalk = MediLink_ConfigLoader.load_configuration()
16
+
17
+ # Function to check if a specific key is pressed
18
+ VK_END = int(config.get('VK_END', ""), 16) # Try F12 (7B). Virtual key code for 'End' (23)
19
+ VK_PAUSE = int(config.get('VK_PAUSE', ""), 16) # Try F11 (7A). Virtual-key code for 'Home' (24)
20
+
21
+
22
+
23
+ def robust_input(prompt="", max_retries=3):
24
+ """
25
+ Simple input function with retry logic for Windows XP console issues.
26
+ """
27
+ for attempt in range(max_retries):
28
+ if attempt > 0:
29
+ # Clear any leftover input on retry
30
+ while msvcrt.kbhit():
31
+ msvcrt.getch()
32
+
33
+ response = input(prompt if attempt == 0 else "").strip()
34
+
35
+ if response:
36
+ return response
37
+
38
+ return ""
39
+
40
+
41
+
42
+ class AppControl:
43
+ def __init__(self):
44
+ self.script_paused = False
45
+ self.mapat_med_path = ''
46
+ self.medisoft_shortcut = ''
47
+ # PERFORMANCE FIX: Add configuration caching to reduce lookup overhead
48
+ self._config_cache = {} # Cache for Medicare vs Private configuration lookups
49
+ # Load initial paths from config when instance is created
50
+ self.load_paths_from_config()
51
+
52
+ def get_pause_status(self):
53
+ return self.script_paused
54
+
55
+ def set_pause_status(self, status):
56
+ self.script_paused = status
57
+
58
+ def get_mapat_med_path(self):
59
+ return self.mapat_med_path
60
+
61
+ def set_mapat_med_path(self, path):
62
+ self.mapat_med_path = path
63
+
64
+ def get_medisoft_shortcut(self):
65
+ return self.medisoft_shortcut
66
+
67
+ def set_medisoft_shortcut(self, path):
68
+ self.medisoft_shortcut = path
69
+
70
+ def load_paths_from_config(self, medicare=False):
71
+ # Assuming `config` is a module or a globally accessible configuration dictionary
72
+ # TODO Is this where the MAINS paths should also be set?
73
+
74
+ # PERFORMANCE FIX: Cache configuration lookups to reduce Medicare vs Private overhead
75
+ cache_key = 'medicare' if medicare else 'private'
76
+
77
+ if cache_key not in self._config_cache:
78
+ # Build cache entry for this configuration type
79
+ if medicare:
80
+ cached_config = {
81
+ 'mapat_path': config.get('MEDICARE_MAPAT_MED_PATH', ""),
82
+ 'shortcut': config.get('MEDICARE_SHORTCUT', "")
83
+ }
84
+ else:
85
+ cached_config = {
86
+ 'mapat_path': config.get('MAPAT_MED_PATH', ""),
87
+ 'shortcut': config.get('PRIVATE_SHORTCUT', "")
88
+ }
89
+ self._config_cache[cache_key] = cached_config
90
+
91
+ # Use cached values to avoid repeated config lookups
92
+ cached = self._config_cache[cache_key]
93
+ self.mapat_med_path = cached['mapat_path']
94
+ self.medisoft_shortcut = cached['shortcut']
95
+
96
+ app_control = AppControl()
97
+
98
+ def is_key_pressed(key_code):
99
+ user32 = ctypes.WinDLL('user32', use_last_error=True)
100
+ user32.GetAsyncKeyState.restype = wintypes.SHORT
101
+ user32.GetAsyncKeyState.argtypes = [wintypes.INT]
102
+ return user32.GetAsyncKeyState(key_code) & 0x8000 != 0
103
+
104
+ def manage_script_pause(csv_data, error_message, reverse_mapping):
105
+ user_action = 0 # initialize as 'continue'
106
+
107
+ if not app_control.get_pause_status() and is_key_pressed(VK_PAUSE):
108
+ app_control.set_pause_status(True)
109
+ print("Script paused. Opening menu...")
110
+ interaction_mode = 'normal' # Assuming normal interaction mode for script pause
111
+ user_action = user_interaction(csv_data, interaction_mode, error_message, reverse_mapping)
112
+
113
+ while app_control.get_pause_status():
114
+ if is_key_pressed(VK_END):
115
+ app_control.set_pause_status(False)
116
+ print("Continuing...")
117
+ elif is_key_pressed(VK_PAUSE):
118
+ user_action = user_interaction(csv_data, 'normal', error_message, reverse_mapping)
119
+ time.sleep(0.1)
120
+
121
+ return user_action
122
+
123
+ # Menu Display & User Interaction
124
+ def display_patient_selection_menu(csv_data, reverse_mapping, proceed_as_medicare):
125
+ selected_patient_ids = []
126
+ selected_indices = []
127
+
128
+ def display_menu_header(title):
129
+ print("\n" + "-" * 60)
130
+ print(title)
131
+ print("-" * 60)
132
+
133
+ def display_patient_list(csv_data, reverse_mapping, medicare_filter=False, exclude_medicare=False):
134
+ medicare_policy_pattern = r"^[a-zA-Z0-9]{11}$" # Regex pattern for 11 alpha-numeric characters
135
+ primary_policy_number_header = reverse_mapping.get('Primary Policy Number', 'Primary Policy Number')
136
+ primary_insurance_header = reverse_mapping.get('Primary Insurance', 'Primary Insurance') # Adjust field name as needed
137
+
138
+ displayed_indices = []
139
+ displayed_patient_ids = []
140
+
141
+ for index, row in enumerate(csv_data):
142
+ policy_number = row.get(primary_policy_number_header, "")
143
+ primary_insurance = row.get(primary_insurance_header, "").upper()
144
+
145
+ if medicare_filter and (not re.match(medicare_policy_pattern, policy_number) or "MEDICARE" not in primary_insurance):
146
+ continue
147
+ if exclude_medicare and re.match(medicare_policy_pattern, policy_number) and "MEDICARE" in primary_insurance:
148
+ continue
149
+
150
+ patient_id_header = reverse_mapping['Patient ID #2']
151
+ patient_name_header = reverse_mapping['Patient Name']
152
+ patient_id = row.get(patient_id_header, "N/A")
153
+ patient_name = row.get(patient_name_header, "Unknown")
154
+ surgery_date = row.get('Surgery Date', "Unknown Date") # Access 'Surgery Date' as string directly from the row
155
+
156
+ print("{0:03d}: {3:%m-%d} (ID: {2}) {1} ".format(index+1, patient_name, patient_id, surgery_date))
157
+
158
+ displayed_indices.append(index)
159
+ displayed_patient_ids.append(patient_id)
160
+
161
+ return displayed_indices, displayed_patient_ids
162
+
163
+ if proceed_as_medicare:
164
+ display_menu_header("MEDICARE Patient Selection for Today's Data Entry")
165
+ selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping, medicare_filter=True)
166
+ else:
167
+ display_menu_header("PRIVATE Patient Selection for Today's Data Entry")
168
+ selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping, exclude_medicare=True)
169
+
170
+ print("-" * 60)
171
+ print("\nDo you want to proceed with the selected patients? (yes/no): ", end='', flush=True)
172
+ # Force flush and wait for Windows XP console buffer synchronization
173
+ sys.stdout.flush()
174
+ time.sleep(0.2) # Increased delay for Windows XP
175
+
176
+ # Use input() for more reliable input on Windows XP
177
+ proceed = input().lower().strip() in ['yes', 'y']
178
+
179
+ if not proceed:
180
+ display_menu_header("Patient Selection for Today's Data Entry")
181
+ selected_indices, selected_patient_ids = display_patient_list(csv_data, reverse_mapping)
182
+ print("-" * 60)
183
+
184
+ while True:
185
+ while True:
186
+ print("\nEnter the number(s) of the patients you wish to proceed with \n(e.g., 1,3,5): ", end='', flush=True)
187
+ # Force flush and wait for Windows XP console buffer synchronization
188
+ sys.stdout.flush()
189
+ time.sleep(0.2) # Increased delay for Windows XP
190
+
191
+ # Use input() for more reliable input on Windows XP
192
+ selection = input().strip()
193
+ if not selection:
194
+ print("Invalid entry. Please provide at least one number.")
195
+ continue
196
+
197
+ selection = selection.replace('.', ',') # Replace '.' with ',' in the user input just in case
198
+ selected_indices = [int(x.strip()) - 1 for x in selection.split(',') if x.strip().isdigit()]
199
+
200
+ if not selected_indices:
201
+ print("Invalid entry. Please provide at least one integer.")
202
+ continue
203
+
204
+ proceed = True
205
+ break
206
+
207
+ if not selection:
208
+ print("Invalid entry. Please provide at least one number.")
209
+ continue
210
+
211
+ selection = selection.replace('.', ',') # Replace '.' with ',' in the user input just in case
212
+ selected_indices = [int(x.strip()) - 1 for x in selection.split(',') if x.strip().isdigit()]
213
+
214
+ if not selected_indices:
215
+ print("Invalid entry. Please provide at least one integer.")
216
+ continue
217
+
218
+ proceed = True
219
+ break
220
+
221
+ patient_id_header = reverse_mapping['Patient ID #2']
222
+ selected_patient_ids = [csv_data[i][patient_id_header] for i in selected_indices if i < len(csv_data)]
223
+
224
+ return proceed, selected_patient_ids, selected_indices
225
+
226
+ def display_menu_header(title):
227
+ print("\n" + "-" * 60)
228
+ print(title)
229
+ print("-" * 60)
230
+ # Force flush for Windows XP compatibility
231
+ sys.stdout.flush()
232
+
233
+ def handle_user_interaction(interaction_mode, error_message):
234
+ # Import here to avoid circular imports
235
+ try:
236
+ from MediBot import current_patient_context
237
+ except ImportError:
238
+ current_patient_context = None
239
+
240
+ while True:
241
+ # If interaction_mode is neither 'triage' nor 'error', then it's normal mode.
242
+ title = "Error Occurred" if interaction_mode == 'error' else "Data Entry Options"
243
+ display_menu_header(title)
244
+
245
+ if interaction_mode == 'error':
246
+ print("\nERROR: ", error_message)
247
+
248
+ # PERFORMANCE FIX: Display patient context to address "won't be obvious anymore" issue
249
+ # Show user which patient and field they're working with for better F11 menu usability
250
+ if current_patient_context:
251
+ patient_name = current_patient_context.get('patient_name', 'Unknown Patient')
252
+ surgery_date = current_patient_context.get('surgery_date', 'Unknown Date')
253
+ last_field = current_patient_context.get('last_field', 'Unknown Field')
254
+ print("\nCurrent Context:")
255
+ print(" Patient: {}".format(patient_name))
256
+ print(" Surgery Date: {}".format(surgery_date))
257
+ print(" Last Field: {}".format(last_field))
258
+ print("")
259
+
260
+ # Menu options with improved context
261
+ print("1: Retry last entry")
262
+ print("2: Skip to next patient and continue")
263
+ print("3: Go back two patients and redo")
264
+ print("4: Exit script")
265
+ print("-" * 60)
266
+ print("Enter your choice (1/2/3/4): ", end='', flush=True)
267
+ # Force flush and wait for Windows XP console buffer synchronization
268
+ sys.stdout.flush()
269
+ time.sleep(0.2) # Increased delay for Windows XP
270
+
271
+ # Use input() for more reliable input on Windows XP
272
+ choice = input().strip()
273
+
274
+ if choice == '1':
275
+ print("Selected: 'Retry last entry'. Please press 'F12' to continue.")
276
+ return -1
277
+ elif choice == '2':
278
+ print("Selected: 'Skip to next patient and continue'. Please press 'F12' to continue.")
279
+ return 1
280
+ elif choice == '3':
281
+ print("Selected: 'Go back two patients and redo'. Please press 'F12' to continue.")
282
+ # Returning a specific value to indicate the action of going back two patients
283
+ # but we might run into a problem if we stop mid-run on the first row?
284
+ return -2
285
+ elif choice == '4':
286
+ print("Exiting the script.")
287
+ exit()
288
+ else:
289
+ print("Invalid choice. Please enter a valid number.")
290
+
291
+ def user_interaction(csv_data, interaction_mode, error_message, reverse_mapping):
292
+ global app_control # Use the instance of AppControl
293
+ selected_patient_ids = []
294
+ selected_indices = []
295
+
296
+ if interaction_mode == 'triage':
297
+ display_menu_header(" =(^.^)= Welcome to MediBot! =(^.^)=")
298
+
299
+ # Force flush for Windows XP compatibility
300
+ sys.stdout.flush()
301
+
302
+ while True:
303
+ try:
304
+ response = robust_input("\nAm I processing Medicare patients? (yes/no): ").lower()
305
+
306
+ if response in ['yes', 'y']:
307
+ app_control.load_paths_from_config(medicare=True)
308
+ break
309
+ elif response in ['no', 'n']:
310
+ app_control.load_paths_from_config(medicare=False)
311
+ break
312
+ else:
313
+ print("Invalid entry. Please enter 'yes' or 'no'.")
314
+ except KeyboardInterrupt:
315
+ print("\nOperation cancelled by user. Exiting script.")
316
+ exit()
317
+
318
+
319
+ fixed_values = config.get('fixed_values', {}) # Get fixed values from config json
320
+ if response in ['yes', 'y']:
321
+ medicare_added_fixed_values = config.get('medicare_added_fixed_values', {})
322
+ fixed_values.update(medicare_added_fixed_values) # Add any medicare-specific fixed values from config
323
+
324
+ proceed, selected_patient_ids, selected_indices = display_patient_selection_menu(csv_data, reverse_mapping, response in ['yes', 'y'])
325
+ return proceed, selected_patient_ids, selected_indices, fixed_values
326
+
409
327
  return handle_user_interaction(interaction_mode, error_message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250725.8
3
+ Version: 0.250725.10
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250725.8
3
+ Version: 0.250725.10
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='medicafe',
5
- version="0.250725.8",
5
+ version="0.250725.10",
6
6
  description='MediCafe',
7
7
  long_description="""
8
8
  # Project Overview: MediCafe
File without changes
File without changes
File without changes