#!/usr/bin/env python3 """ Systematic Student Processing Script This script processes multiple students from an Excel file: 1. Reads student credentials from Excel 2. For each student: - Logs in - Handles password reset (sets to Admin@123) - Completes profile to 100% - Generates synthetic data report 3. Tracks progress and generates summary report """ import sys import time import json from pathlib import Path from datetime import datetime import pandas as pd project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) from selenium.webdriver.common.by import By from pages.login_page import LoginPage from pages.mandatory_reset_page import MandatoryResetPage from pages.profile_incomplete_page import ProfileIncompletePage from pages.profile_editor_page import ProfileEditorPage from utils.driver_manager import DriverManager from config.config import ENVIRONMENT, BASE_URL, TEST_NEW_PASSWORD class StudentProcessor: """Process students systematically""" def __init__(self, excel_path, headless=False): """ Initialize Student Processor Args: excel_path: Path to Excel file with student data headless: Run browser in headless mode """ self.excel_path = Path(excel_path) self.headless = headless self.results = [] self.driver = None def load_students(self): """Load students from Excel file""" try: df = pd.read_excel(self.excel_path) print(f"✅ Loaded {len(df)} students from {self.excel_path}") return df.to_dict('records') except Exception as e: print(f"❌ Error loading Excel file: {e}") return [] def process_student(self, student_data): """ Process a single student Args: student_data: Dictionary with student information Returns: dict: Processing result """ cpid = student_data.get('Student CPID', '') password = student_data.get('Password', '') first_name = student_data.get('First Name', '') last_name = student_data.get('Last Name', '') result = { 'cpid': cpid, 'first_name': first_name, 'last_name': last_name, 'status': 'pending', 'error': None, 'steps_completed': [], 'timestamp': datetime.now().isoformat() } try: print(f"\n{'='*80}") print(f"Processing: {first_name} {last_name} ({cpid})") print(f"{'='*80}") # Initialize driver driver_manager = DriverManager() self.driver = driver_manager.get_driver(headless=self.headless) # Step 1: Login print("\n[STEP 1] Logging in...") login_page = LoginPage(self.driver) login_page.login(identifier=cpid, password=password) time.sleep(2) result['steps_completed'].append('login') # Step 2: Handle Password Reset print("\n[STEP 2] Checking for Password Reset...") reset_page = MandatoryResetPage(self.driver) if reset_page.is_modal_present(): print(" ✅ Password reset modal found") print(" Automatically handling password reset...") reset_page.reset_password( current_password=password, new_password=TEST_NEW_PASSWORD, confirm_password=TEST_NEW_PASSWORD ) print(" ✅ Password reset completed") result['steps_completed'].append('password_reset') # Update password for future use password = TEST_NEW_PASSWORD else: print(" ✅ No password reset required") # Step 3: Handle Profile Incomplete Modal print("\n[STEP 3] Checking for Profile Incomplete Modal...") profile_incomplete = ProfileIncompletePage(self.driver) if profile_incomplete.is_modal_present(): progress = profile_incomplete.get_progress_value() print(f" Profile incomplete - Current Progress: {progress}") print(" Clicking Complete Profile button...") profile_incomplete.click_complete() time.sleep(3) result['steps_completed'].append('profile_modal_handled') # Step 4: Complete Profile print("\n[STEP 4] Completing Profile...") profile_editor = ProfileEditorPage(self.driver) profile_editor.wait_for_page_load() # Fill profile with student data self._fill_profile(profile_editor, student_data) # Save profile print(" Saving profile...") profile_editor.click_save() time.sleep(3) # Verify completion progress = profile_editor.get_progress_value() print(f" Profile completion: {progress}") if "100%" in progress: result['steps_completed'].append('profile_completed') result['status'] = 'success' print(" ✅ Profile completed successfully!") else: result['status'] = 'partial' result['error'] = f"Profile not 100% complete: {progress}" print(f" ⚠️ Profile not fully complete: {progress}") except Exception as e: result['status'] = 'error' result['error'] = str(e) print(f" ❌ Error: {e}") finally: if self.driver: self.driver.quit() self.driver = None return result def _fill_profile(self, profile_editor, student_data): """Fill profile with student data""" print(" Filling profile fields...") # Step 1: Personal Information profile_editor.fill_personal_information( first_name=student_data.get('First Name'), last_name=student_data.get('Last Name'), gender=student_data.get('Gender'), dob=student_data.get('Date of Birth'), roll_number=str(student_data.get('Roll Number', '')), nationality=student_data.get('Nationality') ) # Fill additional personal info fields if available if student_data.get('Language'): profile_editor.send_keys(profile_editor.LANGUAGE_INPUT, student_data['Language']) if student_data.get('Student ID Number'): profile_editor.send_keys(profile_editor.STUDENT_ID_INPUT, str(student_data['Student ID Number'])) if student_data.get('Student CPID'): profile_editor.send_keys(profile_editor.STUDENT_CPID_INPUT, student_data['Student CPID']) # Navigate to next step profile_editor.click_next() time.sleep(1) # Step 2: Contact Information profile_editor.fill_contact_information( email=student_data.get('Email'), phone=str(student_data.get('Phone', '')), address=student_data.get('Address'), city=student_data.get('City'), state=student_data.get('State'), zip_code=str(student_data.get('Pin code', '')), native_state=student_data.get('Native State') ) profile_editor.click_next() time.sleep(1) # Step 3: Parent/Guardian profile_editor.fill_parent_guardian( father_name=student_data.get('Father Name'), mother_name=student_data.get('Mother Name'), guardian_name=student_data.get('Guardian Name'), guardian_phone=str(student_data.get('Guardian Phone', '')) ) profile_editor.click_next() time.sleep(1) # Step 4: Education Details profile_editor.fill_education_details( full_name=student_data.get('Full Name'), current_grade=str(student_data.get('Class', '')), section=student_data.get('Section'), board_stream=student_data.get('Board/Stream', 'CBSE') ) # Steps 5-9: Use synthetic data or defaults # For now, we'll navigate through and let user fill manually # Or use synthetic data generator print(" ✅ Profile fields filled") def process_all(self, start_index=0, end_index=None): """ Process all students Args: start_index: Start processing from this index end_index: End processing at this index (None = all) """ students = self.load_students() if not students: print("❌ No students to process") return if end_index is None: end_index = len(students) students_to_process = students[start_index:end_index] print(f"\n{'='*80}") print(f"PROCESSING {len(students_to_process)} STUDENTS") print(f"{'='*80}") print(f"Start Index: {start_index}") print(f"End Index: {end_index}") print(f"{'='*80}\n") for idx, student in enumerate(students_to_process, start=start_index): print(f"\n[{idx+1}/{len(students_to_process)}] Processing student...") result = self.process_student(student) self.results.append(result) # Save progress after each student self._save_progress() # Small delay between students time.sleep(2) # Generate final report self._generate_report() def _save_progress(self): """Save progress to JSON file""" report_dir = Path(__file__).parent.parent / "reports" / "student_processing" report_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") report_file = report_dir / f"progress_{timestamp}.json" with open(report_file, 'w') as f: json.dump(self.results, f, indent=2) def _generate_report(self): """Generate final processing report""" total = len(self.results) successful = len([r for r in self.results if r['status'] == 'success']) partial = len([r for r in self.results if r['status'] == 'partial']) errors = len([r for r in self.results if r['status'] == 'error']) print(f"\n{'='*80}") print("PROCESSING SUMMARY") print(f"{'='*80}") print(f"Total Students: {total}") print(f"✅ Successful: {successful}") print(f"⚠️ Partial: {partial}") print(f"❌ Errors: {errors}") print(f"{'='*80}") if errors > 0: print("\nErrors:") for result in self.results: if result['status'] == 'error': print(f" - {result['cpid']}: {result['error']}") def main(): """Main entry point""" import argparse parser = argparse.ArgumentParser(description='Process students from Excel file') parser.add_argument('--excel', type=str, required=True, help='Path to Excel file with student data') parser.add_argument('--headless', action='store_true', help='Run in headless mode') parser.add_argument('--start', type=int, default=0, help='Start index (default: 0)') parser.add_argument('--end', type=int, default=None, help='End index (default: all)') parser.add_argument('--single', type=str, default=None, help='Process single student by CPID') args = parser.parse_args() processor = StudentProcessor(args.excel, headless=args.headless) if args.single: # Process single student students = processor.load_students() student = next((s for s in students if s.get('Student CPID') == args.single), None) if student: result = processor.process_student(student) print(f"\nResult: {result}") else: print(f"❌ Student with CPID {args.single} not found") else: # Process all students processor.process_all(start_index=args.start, end_index=args.end) if __name__ == "__main__": main()