sonuprasad23 commited on
Commit
1da4d9a
·
1 Parent(s): d0208e8

Almost Done

Browse files
Files changed (2) hide show
  1. server.py +9 -10
  2. worker.py +49 -31
server.py CHANGED
@@ -134,11 +134,9 @@ def run_automation_process(session_id):
134
  data = session_data.get(session_id, {}); patient_data = data.get('patient_data'); workflow = data.get('workflow')
135
  if not patient_data: raise ValueError("No patient data prepared.")
136
  socketio.emit('initial_stats', {'total': len(patient_data)})
137
- if workflow == 'void':
138
- results = bot_instance.process_void_list(patient_data)
139
- elif workflow == 'refund':
140
- start_date = data.get('start_date'); end_date = data.get('end_date')
141
- results = bot_instance.process_refund_list(patient_data, start_date, end_date)
142
  is_terminated = bot_instance.termination_event.is_set()
143
  except Exception as e:
144
  print(f"[Server Log] Fatal error in automation thread: {e}"); is_crash = True
@@ -198,7 +196,7 @@ def status_page():
198
  def handle_connect():
199
  print(f'Frontend connected.')
200
  emit('email_list', {'emails': get_email_list()})
201
-
202
  @socketio.on('get_email_list')
203
  def handle_get_email_list():
204
  emit('email_list', {'emails': get_email_list()})
@@ -214,17 +212,18 @@ def handle_init(data):
214
  })
215
  try:
216
  app_b64 = data.get('app_data_b64'); quantum_b64 = data.get('quantum_data_b64')
 
 
217
  if not app_b64 or not quantum_b64:
218
  emit('error', {'message': 'Both data files are required.'}); return
219
 
220
  def load_df_from_b64(b64str, filename):
221
- raw = base64.b64decode(b64str)
222
- bio = io.BytesIO(raw)
223
  if filename.endswith('.xlsx'): return pd.read_excel(bio)
224
  return pd.read_csv(bio)
225
 
226
- df_app = load_df_from_b64(app_b64, "app_data.xlsx") # Filename doesn't matter, only extension
227
- df_quantum = load_df_from_b64(quantum_b64, "quantum_data.csv")
228
 
229
  if 'Patient Name' not in df_app.columns or 'PRN' not in df_app.columns:
230
  emit('error', {'message': "App Data must contain 'Patient Name' and 'PRN' columns."}); return
 
134
  data = session_data.get(session_id, {}); patient_data = data.get('patient_data'); workflow = data.get('workflow')
135
  if not patient_data: raise ValueError("No patient data prepared.")
136
  socketio.emit('initial_stats', {'total': len(patient_data)})
137
+
138
+ results = bot_instance.process_patient_list(patient_data, workflow, data)
139
+
 
 
140
  is_terminated = bot_instance.termination_event.is_set()
141
  except Exception as e:
142
  print(f"[Server Log] Fatal error in automation thread: {e}"); is_crash = True
 
196
  def handle_connect():
197
  print(f'Frontend connected.')
198
  emit('email_list', {'emails': get_email_list()})
199
+
200
  @socketio.on('get_email_list')
201
  def handle_get_email_list():
202
  emit('email_list', {'emails': get_email_list()})
 
212
  })
213
  try:
214
  app_b64 = data.get('app_data_b64'); quantum_b64 = data.get('quantum_data_b64')
215
+ app_filename = data.get('app_data_filename', '').lower()
216
+ quantum_filename = data.get('quantum_data_filename', '').lower()
217
  if not app_b64 or not quantum_b64:
218
  emit('error', {'message': 'Both data files are required.'}); return
219
 
220
  def load_df_from_b64(b64str, filename):
221
+ raw = base64.b64decode(b64str); bio = io.BytesIO(raw)
 
222
  if filename.endswith('.xlsx'): return pd.read_excel(bio)
223
  return pd.read_csv(bio)
224
 
225
+ df_app = load_df_from_b64(app_b64, app_filename)
226
+ df_quantum = load_df_from_b64(quantum_b64, quantum_filename)
227
 
228
  if 'Patient Name' not in df_app.columns or 'PRN' not in df_app.columns:
229
  emit('error', {'message': "App Data must contain 'Patient Name' and 'PRN' columns."}); return
worker.py CHANGED
@@ -2,6 +2,8 @@ import time
2
  import threading
3
  import subprocess
4
  import pandas as pd
 
 
5
  from selenium import webdriver
6
  from selenium.webdriver.chrome.service import Service as ChromeService
7
  from selenium.webdriver.chrome.options import Options as ChromeOptions
@@ -75,7 +77,7 @@ class QuantumBot:
75
  except Exception as e:
76
  error_message = f"Error during OTP submission: {str(e)}"; print(f"[Bot Log] ERROR during OTP submission: {error_message}")
77
  return False, error_message
78
-
79
  def _get_calendar_months(self):
80
  try:
81
  titles = self.driver.find_elements(By.XPATH, "//div[contains(@class, 'vc-title')]")
@@ -108,41 +110,30 @@ class QuantumBot:
108
  self._select_date_in_calendar(end_date); time.sleep(1)
109
  self.driver.find_element(By.TAG_NAME, "body").click()
110
  time.sleep(3)
111
-
112
  def process_patient_list(self, patient_data, workflow, date_range=None):
 
 
 
 
 
 
 
113
  results = []
114
- records_to_process = list(patient_data)
115
- for index, record in enumerate(records_to_process):
116
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
117
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
118
  if not patient_prn or not str(patient_prn).strip():
119
- status = 'Skipped - No PRN'
120
- self.micro_status(f"Skipping '{patient_name}' (No PRN).")
121
- time.sleep(0.2)
122
  else:
123
- self.micro_status(f"Processing '{patient_name}' ({index + 1}/{len(records_to_process)})...")
124
- if workflow == 'void':
125
- status = self._process_single_void(patient_name, patient_prn)
126
- elif workflow == 'refund':
127
- status = self._process_single_refund(patient_name, patient_prn, date_range['start_date'], date_range['end_date'])
128
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
129
  with self.app.app_context():
130
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
131
- self.socketio.emit('stats_update', {
132
- 'processed': len(results),
133
- 'remaining': len(records_to_process) - len(results),
134
- 'status': status
135
- })
136
  return results
137
 
138
- def _force_clear_and_fill(self, locator, text_to_fill):
139
- element = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable(locator))
140
- element.click()
141
- element.send_keys(Keys.CONTROL + "a")
142
- element.send_keys(Keys.BACK_SPACE)
143
- if text_to_fill:
144
- element.send_keys(str(text_to_fill))
145
-
146
  def _process_single_void(self, patient_name, patient_prn):
147
  try:
148
  self.micro_status(f"Navigating to Void page for '{patient_name}'")
@@ -166,8 +157,11 @@ class QuantumBot:
166
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='modal-footer']//button/span[normalize-space()='Confirm']"))).click()
167
  try:
168
  self.micro_status("Verifying success and saving...")
169
- self._force_clear_and_fill((By.NAME, "company_name"), patient_name)
170
- self._force_clear_and_fill((By.NAME, "company_contact"), patient_prn)
 
 
 
171
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
172
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
173
  time.sleep(5); return 'Done'
@@ -178,12 +172,33 @@ class QuantumBot:
178
  except Exception as e:
179
  print(f"An error occurred while processing {patient_name}: {e}"); return 'Error'
180
 
181
- def _process_single_refund(self, patient_name, patient_prn, start_date, end_date):
 
182
  try:
183
  self.micro_status(f"Navigating to Refund page...")
184
  self.driver.get("https://gateway.quantumepay.com/credit-card/refund")
185
  self._set_date_range(start_date, end_date)
186
- self.micro_status(f"Searching for '{patient_name}'...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  search_box = WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//input[@placeholder='Search']")))
188
  search_box.click(); time.sleep(0.5); search_box.clear(); time.sleep(0.5)
189
  search_box.send_keys(patient_name)
@@ -196,8 +211,11 @@ class QuantumBot:
196
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='modal-footer']//button/span[normalize-space()='Confirm']"))).click()
197
  try:
198
  self.micro_status("Verifying success and saving...")
199
- self._force_clear_and_fill((By.NAME, "company_name"), patient_name)
200
- self._force_clear_and_fill((By.NAME, "company_contact"), patient_prn)
 
 
 
201
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
202
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
203
  time.sleep(5); return 'Done'
 
2
  import threading
3
  import subprocess
4
  import pandas as pd
5
+ import os
6
+ from datetime import datetime
7
  from selenium import webdriver
8
  from selenium.webdriver.chrome.service import Service as ChromeService
9
  from selenium.webdriver.chrome.options import Options as ChromeOptions
 
77
  except Exception as e:
78
  error_message = f"Error during OTP submission: {str(e)}"; print(f"[Bot Log] ERROR during OTP submission: {error_message}")
79
  return False, error_message
80
+
81
  def _get_calendar_months(self):
82
  try:
83
  titles = self.driver.find_elements(By.XPATH, "//div[contains(@class, 'vc-title')]")
 
110
  self._select_date_in_calendar(end_date); time.sleep(1)
111
  self.driver.find_element(By.TAG_NAME, "body").click()
112
  time.sleep(3)
113
+
114
  def process_patient_list(self, patient_data, workflow, date_range=None):
115
+ if workflow == 'void':
116
+ return self.process_void_list(patient_data)
117
+ elif workflow == 'refund':
118
+ return self.process_refund_list(patient_data, date_range['start_date'], date_range['end_date'])
119
+ return []
120
+
121
+ def process_void_list(self, patient_data):
122
  results = []
123
+ for index, record in enumerate(patient_data):
 
124
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
125
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
126
  if not patient_prn or not str(patient_prn).strip():
127
+ status = 'Skipped - No PRN'; self.micro_status(f"Skipping '{patient_name}' (No PRN)."); time.sleep(0.2)
 
 
128
  else:
129
+ self.micro_status(f"Processing '{patient_name}' ({index + 1}/{len(patient_data)})...")
130
+ status = self._process_single_void(patient_name, patient_prn)
 
 
 
131
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
132
  with self.app.app_context():
133
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
134
+ self.socketio.emit('stats_update', {'processed': len(results), 'remaining': len(patient_data) - len(results), 'status': status})
 
 
 
 
135
  return results
136
 
 
 
 
 
 
 
 
 
137
  def _process_single_void(self, patient_name, patient_prn):
138
  try:
139
  self.micro_status(f"Navigating to Void page for '{patient_name}'")
 
157
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='modal-footer']//button/span[normalize-space()='Confirm']"))).click()
158
  try:
159
  self.micro_status("Verifying success and saving...")
160
+ company_input = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "company_name")))
161
+ company_input.clear(); company_input.send_keys(patient_name)
162
+ contact_input = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable((By.NAME, "company_contact")))
163
+ contact_input.click(); contact_input.send_keys(Keys.CONTROL + "a"); contact_input.send_keys(Keys.BACK_SPACE)
164
+ contact_input.send_keys(str(patient_prn))
165
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
166
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
167
  time.sleep(5); return 'Done'
 
172
  except Exception as e:
173
  print(f"An error occurred while processing {patient_name}: {e}"); return 'Error'
174
 
175
+ def process_refund_list(self, patient_data, start_date, end_date):
176
+ results = []
177
  try:
178
  self.micro_status(f"Navigating to Refund page...")
179
  self.driver.get("https://gateway.quantumepay.com/credit-card/refund")
180
  self._set_date_range(start_date, end_date)
181
+ for index, record in enumerate(patient_data):
182
+ if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
183
+ patient_name = record['Name']; patient_prn = record.get('PRN', '')
184
+ if not patient_prn or not str(patient_prn).strip():
185
+ status = 'Skipped - No PRN'
186
+ self.micro_status(f"Skipping '{patient_name}' (No PRN)."); time.sleep(0.2)
187
+ else:
188
+ self.micro_status(f"Processing '{patient_name}' ({index + 1}/{len(patient_data)})...")
189
+ status = self._process_single_refund(patient_name, patient_prn)
190
+ results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
191
+ with self.app.app_context():
192
+ self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
193
+ self.socketio.emit('stats_update', {'processed': len(results), 'remaining': len(patient_data) - len(results), 'status': status})
194
+ except Exception as e:
195
+ print(f"A fatal error occurred during the Refund workflow: {e}")
196
+ self.micro_status("A fatal error stopped the Refund process.")
197
+ return results
198
+
199
+ def _process_single_refund(self, patient_name, patient_prn):
200
+ try:
201
+ self.micro_status(f"Searching for '{patient_name}' on Refund page...")
202
  search_box = WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//input[@placeholder='Search']")))
203
  search_box.click(); time.sleep(0.5); search_box.clear(); time.sleep(0.5)
204
  search_box.send_keys(patient_name)
 
211
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='modal-footer']//button/span[normalize-space()='Confirm']"))).click()
212
  try:
213
  self.micro_status("Verifying success and saving...")
214
+ company_input = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "company_name")))
215
+ company_input.clear(); company_input.send_keys(patient_name)
216
+ contact_input = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable((By.NAME, "company_contact")))
217
+ contact_input.click(); contact_input.send_keys(Keys.CONTROL + "a"); contact_input.send_keys(Keys.BACK_SPACE)
218
+ contact_input.send_keys(str(patient_prn))
219
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
220
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
221
  time.sleep(5); return 'Done'