sonuprasad23 commited on
Commit
df5dc85
·
1 Parent(s): 47657a7

Fixing this

Browse files
Files changed (2) hide show
  1. server.py +36 -44
  2. worker.py +34 -23
server.py CHANGED
@@ -165,41 +165,6 @@ def extract_patient_name(raw_name):
165
  name_only = raw_name.split('DOB')[0].strip()
166
  return re.sub(r'[:\d\-\s]+$', '', name_only).strip()
167
 
168
- @app.route('/process_files_for_automation', methods=['POST'])
169
- def handle_file_processing_and_init():
170
- session_id = 'user_session'
171
- try:
172
- session_data_from_header = json.loads(request.headers.get('X-Session-Data'))
173
- session_data[session_id] = session_data_from_header
174
-
175
- if 'app_data' not in request.files or 'quantum_data' not in request.files:
176
- return jsonify({"error": "Both files are required."}), 400
177
-
178
- df_app = pd.read_excel(request.files['app_data']) if request.files['app_data'].filename.endswith('.xlsx') else pd.read_csv(request.files['app_data'])
179
- df_quantum = pd.read_excel(request.files['quantum_data']) if request.files['quantum_data'].filename.endswith('.xlsx') else pd.read_csv(request.files['quantum_data'])
180
-
181
- if 'Patient Name' not in df_app.columns or 'PRN' not in df_app.columns: raise ValueError("App Data must contain 'Patient Name' and 'PRN' columns.")
182
- if 'Name' not in df_quantum.columns: raise ValueError("Quantum Data must contain a 'Name' column.")
183
-
184
- df_app_filtered = df_app.dropna(subset=['PRN']); df_app_filtered = df_app_filtered[df_app_filtered['PRN'].astype(str).str.strip() != '']
185
- prn_lookup_dict = {extract_patient_name(row['Patient Name']): row['PRN'] for _, row in df_app_filtered.iterrows()}
186
-
187
- df_quantum['PRN'] = df_quantum['Name'].apply(lambda name: prn_lookup_dict.get(name, ""))
188
-
189
- master_df = df_quantum.copy()
190
- master_df['Status'] = ''
191
-
192
- # We save the full, merged dataframe for the final report
193
- session_data[session_id]['patient_data_for_report'] = master_df
194
- # We only process records that have a PRN
195
- session_data[session_id]['patient_data'] = master_df[master_df['PRN'] != ''].to_dict('records')
196
-
197
- socketio.emit('data_processed')
198
- return jsonify({"message": "Data processed successfully."})
199
- except Exception as e:
200
- print(f"[Server Log] ERROR during file processing: {e}")
201
- return jsonify({"error": str(e)}), 500
202
-
203
  @socketio.on('connect')
204
  def handle_connect():
205
  print(f'Frontend connected.')
@@ -208,24 +173,51 @@ def handle_connect():
208
  @socketio.on('initialize_session')
209
  def handle_init(data):
210
  session_id = 'user_session'
211
- session_data[session_id] = data # Store mode, dates, emails etc.
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  global bot_instance
214
  if bot_instance: bot_instance.shutdown()
215
  bot_instance = QuantumBot(socketio, app)
216
  is_success, error_message = bot_instance.initialize_driver()
217
  if is_success:
218
- emit('bot_initialized')
 
 
219
  else:
220
  emit('error', {'message': f'Failed to initialize bot: {error_message}'})
221
 
222
- @socketio.on('start_login')
223
- def handle_login(credentials):
224
- if not bot_instance: return emit('error', {'message': 'Bot not initialized.'})
225
- is_success, error_message = bot_instance.login(credentials['username'], credentials['password'])
226
- if is_success: emit('otp_required')
227
- else: emit('error', {'message': f'Login failed: {error_message}'})
228
-
229
  @socketio.on('submit_otp')
230
  def handle_otp(data):
231
  if not bot_instance: return emit('error', {'message': 'Bot not initialized.'})
 
165
  name_only = raw_name.split('DOB')[0].strip()
166
  return re.sub(r'[:\d\-\s]+$', '', name_only).strip()
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  @socketio.on('connect')
169
  def handle_connect():
170
  print(f'Frontend connected.')
 
173
  @socketio.on('initialize_session')
174
  def handle_init(data):
175
  session_id = 'user_session'
176
+ session_data[session_id] = data
177
 
178
+ mode = data.get('mode')
179
+
180
+ if mode == 'void':
181
+ try:
182
+ app_data_content = base64.b64decode(data['app_data_content']).decode('utf-8')
183
+ quantum_data_content = base64.b64decode(data['quantum_data_content']).decode('utf-8')
184
+
185
+ df_app = pd.read_excel(io.BytesIO(base64.b64decode(data['app_data_content']))) if data['app_data_filename'].endswith('.xlsx') else pd.read_csv(io.StringIO(app_data_content))
186
+ df_quantum = pd.read_excel(io.BytesIO(base64.b64decode(data['quantum_data_content']))) if data['quantum_data_filename'].endswith('.xlsx') else pd.read_csv(io.StringIO(quantum_data_content))
187
+
188
+ if 'Patient Name' not in df_app.columns or 'PRN' not in df_app.columns: raise ValueError("App Data must contain 'Patient Name' and 'PRN' columns.")
189
+ if 'Name' not in df_quantum.columns: raise ValueError("Quantum Data must contain a 'Name' column.")
190
+
191
+ df_app_filtered = df_app.dropna(subset=['PRN']); df_app_filtered = df_app_filtered[df_app_filtered['PRN'].astype(str).str.strip() != '']
192
+ prn_lookup_dict = {extract_patient_name(row['Patient Name']): row['PRN'] for _, row in df_app_filtered.iterrows()}
193
+
194
+ df_quantum['PRN'] = df_quantum['Name'].apply(lambda name: prn_lookup_dict.get(name, ""))
195
+
196
+ master_df = df_quantum.copy(); master_df['Status'] = ''
197
+ session_data[session_id]['patient_data_for_report'] = master_df
198
+ session_data[session_id]['patient_data'] = master_df.to_dict('records')
199
+
200
+ emit('data_processed')
201
+ except Exception as e:
202
+ print(f"[Server Log] ERROR during file processing: {e}")
203
+ emit('error', {'message': str(e)})
204
+ else: # For refund mode, we just need to initialize the bot
205
+ emit('data_processed')
206
+
207
+
208
+ @socketio.on('start_login')
209
+ def handle_login(credentials):
210
  global bot_instance
211
  if bot_instance: bot_instance.shutdown()
212
  bot_instance = QuantumBot(socketio, app)
213
  is_success, error_message = bot_instance.initialize_driver()
214
  if is_success:
215
+ is_login_success, login_error = bot_instance.login(credentials['username'], credentials['password'])
216
+ if is_login_success: emit('otp_required')
217
+ else: emit('error', {'message': f'Login failed: {login_error}'})
218
  else:
219
  emit('error', {'message': f'Failed to initialize bot: {error_message}'})
220
 
 
 
 
 
 
 
 
221
  @socketio.on('submit_otp')
222
  def handle_otp(data):
223
  if not bot_instance: return emit('error', {'message': 'Bot not initialized.'})
worker.py CHANGED
@@ -12,7 +12,6 @@ from selenium.webdriver.common.keys import Keys
12
  from selenium.webdriver.support.ui import WebDriverWait
13
  from selenium.webdriver.support import expected_conditions as EC
14
  from selenium.common.exceptions import TimeoutException
15
- from webdriver_manager.chrome import ChromeDriverManager
16
 
17
  class QuantumBot:
18
  def __init__(self, socketio, app):
@@ -22,16 +21,18 @@ class QuantumBot:
22
 
23
  def initialize_driver(self):
24
  try:
25
- self.micro_status("Initializing browser...")
26
- options = ChromeOptions()
27
- options.add_argument("--start-maximized")
28
- options.add_argument('--disable-blink-features=AutomationControlled')
29
- options.add_experimental_option("excludeSwitches", ["enable-automation"])
30
- options.add_experimental_option('useAutomationExtension', False)
 
 
31
  os.makedirs(self.download_dir, exist_ok=True)
32
  prefs = {"download.default_directory": self.download_dir}
33
  options.add_experimental_option("prefs", prefs)
34
- service = ChromeService(ChromeDriverManager().install())
35
  self.driver = webdriver.Chrome(service=service, options=options)
36
  self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
37
  'source': "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
@@ -87,8 +88,12 @@ class QuantumBot:
87
  for index, record in enumerate(patient_data):
88
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
89
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
90
- self.micro_status(f"Processing VOID for '{patient_name}' ({index + 1}/{len(patient_data)})...")
91
- status = self._process_single_void_or_refund(patient_name, patient_prn, "void")
 
 
 
 
92
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
93
  with self.app.app_context():
94
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
@@ -100,30 +105,36 @@ class QuantumBot:
100
  downloaded_df = self._download_report(start_date_str, end_date_str, "refund")
101
  if downloaded_df is None: raise Exception("Failed to download the refund report.")
102
 
 
 
 
103
  patient_data = downloaded_df.to_dict('records')
104
  self.socketio.emit('initial_stats', {'total': len(patient_data)})
105
 
106
  results = []
 
 
107
  for index, record in enumerate(patient_data):
108
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
109
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
110
  self.micro_status(f"Processing REFUND for '{patient_name}' ({index + 1}/{len(patient_data)})...")
111
- status = self._process_single_void_or_refund(patient_name, patient_prn, "refund", start_date_str, end_date_str, is_first_record=(index==0))
 
112
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
113
  with self.app.app_context():
114
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
115
  self.socketio.emit('stats_update', {'processed': index + 1, 'remaining': len(patient_data) - (index + 1), 'status': status})
116
  return results
117
 
118
- def _process_single_void_or_refund(self, patient_name, patient_prn, mode, start_date_str=None, end_date_str=None, is_first_record=False):
119
  try:
120
  page_url = f"https://gateway.quantumepay.com/credit-card/{mode}"
121
- self.micro_status(f"Navigating to {mode.title()} page for '{patient_name}'")
122
- self.driver.get(page_url)
123
- time.sleep(3)
124
-
125
- if mode == 'refund' and not is_first_record:
126
- self._set_date_range_on_page(start_date_str, end_date_str)
127
 
128
  search_successful = False
129
  for attempt in range(5):
@@ -149,11 +160,10 @@ class QuantumBot:
149
  self.micro_status("Verifying success and saving...")
150
  company_input = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "company_name")))
151
  company_input.clear(); company_input.send_keys(patient_name)
152
- if patient_prn and str(patient_prn).strip() and str(patient_prn).lower() != 'nan':
153
- self.micro_status(f"Entering PRN: {patient_prn}...")
154
- contact_input = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable((By.NAME, "company_contact")))
155
- contact_input.click(); contact_input.send_keys(Keys.CONTROL + "a"); contact_input.send_keys(Keys.BACK_SPACE)
156
- contact_input.send_keys(str(patient_prn))
157
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
158
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
159
  time.sleep(5); return 'Done'
@@ -231,5 +241,6 @@ class QuantumBot:
231
  if os.path.exists(self.download_dir):
232
  import shutil
233
  shutil.rmtree(self.download_dir)
 
234
  print("[Bot Log] Chrome session and downloads cleaned up.")
235
  except Exception as e: print(f"[Bot Log] Error during shutdown: {e}")
 
12
  from selenium.webdriver.support.ui import WebDriverWait
13
  from selenium.webdriver.support import expected_conditions as EC
14
  from selenium.common.exceptions import TimeoutException
 
15
 
16
  class QuantumBot:
17
  def __init__(self, socketio, app):
 
21
 
22
  def initialize_driver(self):
23
  try:
24
+ self.micro_status("Initializing headless browser...")
25
+ self._kill_chrome_processes()
26
+ options = ChromeOptions(); options.binary_location = "/usr/bin/chromium"
27
+ options.add_argument("--headless=new"); options.add_argument("--no-sandbox")
28
+ options.add_argument("--disable-dev-shm-usage"); options.add_argument("--disable-gpu")
29
+ options.add_argument("--window-size=1920,1080")
30
+ user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
31
+ options.add_argument(f"--user-agent={user_agent}")
32
  os.makedirs(self.download_dir, exist_ok=True)
33
  prefs = {"download.default_directory": self.download_dir}
34
  options.add_experimental_option("prefs", prefs)
35
+ service = ChromeService(executable_path="/usr/bin/chromedriver")
36
  self.driver = webdriver.Chrome(service=service, options=options)
37
  self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
38
  'source': "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
 
88
  for index, record in enumerate(patient_data):
89
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
90
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
91
+ status = 'Skipped - No PRN'
92
+ if patient_prn and str(patient_prn).strip():
93
+ self.micro_status(f"Processing VOID for '{patient_name}' ({index + 1}/{len(patient_data)})...")
94
+ status = self._process_single_record(patient_name, patient_prn, "void")
95
+ else:
96
+ self.micro_status(f"Skipping '{patient_name}' (No PRN)."); time.sleep(0.5)
97
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
98
  with self.app.app_context():
99
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
 
105
  downloaded_df = self._download_report(start_date_str, end_date_str, "refund")
106
  if downloaded_df is None: raise Exception("Failed to download the refund report.")
107
 
108
+ if 'Name' not in downloaded_df.columns: raise Exception("Downloaded report is missing 'Name' column.")
109
+ if 'PRN' not in downloaded_df.columns: downloaded_df['PRN'] = ''
110
+
111
  patient_data = downloaded_df.to_dict('records')
112
  self.socketio.emit('initial_stats', {'total': len(patient_data)})
113
 
114
  results = []
115
+ # The first record sets the date range, subsequent records reuse the page
116
+ is_first_record = True
117
  for index, record in enumerate(patient_data):
118
  if self.termination_event.is_set(): print("[Bot Log] Termination detected."); break
119
  patient_name = record['Name']; patient_prn = record.get('PRN', '')
120
  self.micro_status(f"Processing REFUND for '{patient_name}' ({index + 1}/{len(patient_data)})...")
121
+ status = self._process_single_record(patient_name, patient_prn, "refund", start_date_str, end_date_str, is_first_record=is_first_record)
122
+ is_first_record = False # Only the first one needs to set the date
123
  results.append({'Name': patient_name, 'PRN': patient_prn, 'Status': status})
124
  with self.app.app_context():
125
  self.socketio.emit('log_update', {'name': patient_name, 'prn': patient_prn, 'status': status})
126
  self.socketio.emit('stats_update', {'processed': index + 1, 'remaining': len(patient_data) - (index + 1), 'status': status})
127
  return results
128
 
129
+ def _process_single_record(self, patient_name, patient_prn, mode, start_date_str=None, end_date_str=None, is_first_record=False):
130
  try:
131
  page_url = f"https://gateway.quantumepay.com/credit-card/{mode}"
132
+ if mode == 'void' or is_first_record:
133
+ self.micro_status(f"Navigating to {mode.title()} page for '{patient_name}'")
134
+ self.driver.get(page_url)
135
+ time.sleep(3)
136
+ if mode == 'refund':
137
+ self._set_date_range_on_page(start_date_str, end_date_str)
138
 
139
  search_successful = False
140
  for attempt in range(5):
 
160
  self.micro_status("Verifying success and saving...")
161
  company_input = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.NAME, "company_name")))
162
  company_input.clear(); company_input.send_keys(patient_name)
163
+ contact_input = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable((By.NAME, "company_contact")))
164
+ contact_input.click(); contact_input.send_keys(Keys.CONTROL + "a"); contact_input.send_keys(Keys.BACK_SPACE)
165
+ self.micro_status(f"Entering PRN: {patient_prn}...")
166
+ contact_input.send_keys(str(patient_prn))
 
167
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button/span[normalize-space()='Save Changes']"))).click()
168
  WebDriverWait(self.driver, self.DEFAULT_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[normalize-space()='Confirm']]"))).click()
169
  time.sleep(5); return 'Done'
 
241
  if os.path.exists(self.download_dir):
242
  import shutil
243
  shutil.rmtree(self.download_dir)
244
+ self._kill_chrome_processes()
245
  print("[Bot Log] Chrome session and downloads cleaned up.")
246
  except Exception as e: print(f"[Bot Log] Error during shutdown: {e}")