338 lines
12 KiB
Python
Executable File
338 lines
12 KiB
Python
Executable File
#!/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()
|
|
|