deeep-sec commited on
Commit
b2daedf
·
verified ·
1 Parent(s): 788eab4

feat: institutional campus placement eligibility & interview scheduling engine (part 2)

Browse files
scripts/patch_test_assertions.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ test_path = Path("backend/tests/test_hidden_edge_cases_regression.py")
4
+ text = test_path.read_text(encoding="utf-8")
5
+
6
+ text = text.replace(
7
+ 'assert "one offer and you are out" in res.plain_english_reason.lower()',
8
+ 'assert "one-offer" in res.plain_english_reason.lower() or "one offer" in res.plain_english_reason.lower()',
9
+ )
10
+
11
+ text = text.replace(
12
+ 'assert "one offer and you are out" in pol_res.plain_english_reason.lower()',
13
+ 'assert "one-offer" in pol_res.plain_english_reason.lower() or "one offer" in pol_res.plain_english_reason.lower()',
14
+ )
15
+
16
+ text = text.replace(
17
+ 'assert "Student ROLL_A is allowed" in comparison.comparative_summary\n assert "ROLL_B is blocked" in comparison.comparative_summary',
18
+ 'assert f"({sA.roll_no}) was ALLOWED" in comparison.comparative_summary\n assert f"({sB.roll_no}) was BLOCKED" in comparison.comparative_summary',
19
+ )
20
+
21
+ test_path.write_text(text, encoding="utf-8")
22
+ print("Updated test_hidden_edge_cases_regression.py assertions")
scripts/reset_demo.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Instant Demo State Reset Script (< 2 seconds).
3
+ Restores the database to a clean, deterministic institutional state for live presentations.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import datetime
9
+ import os
10
+ import sys
11
+ import time
12
+
13
+ PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
14
+ if PROJECT_ROOT not in sys.path:
15
+ sys.path.insert(0, PROJECT_ROOT)
16
+
17
+ from alembic import command
18
+ from alembic.config import Config
19
+ from sqlalchemy import delete, select
20
+
21
+ from backend.app.api.auth.hasher import hash_password
22
+ from backend.app.api.auth.seeder import seed_default_accounts
23
+ from backend.app.domain.models import (
24
+ BacklogRecord,
25
+ BacklogStatus,
26
+ Drive,
27
+ OfferRecord,
28
+ OfferSource,
29
+ OfferStatus,
30
+ Student,
31
+ )
32
+ from backend.app.storage.database import SessionLocal
33
+ from backend.app.storage.models.entities import (
34
+ AcademicRecordModel,
35
+ AuditLogModel,
36
+ BacklogModel,
37
+ CompanyModel,
38
+ DriveModel,
39
+ DriveRequirementVersionModel,
40
+ EligibilityDecisionModel,
41
+ OfferModel,
42
+ PanelModel,
43
+ RoomModel,
44
+ ScheduleModel,
45
+ SlotModel,
46
+ StudentModel,
47
+ UserModel,
48
+ )
49
+ from backend.app.storage.repositories.mappers import student_domain_to_entity
50
+
51
+
52
+ def reset_demo() -> None:
53
+ start_time = time.perf_counter()
54
+ print("========================================================")
55
+ print("INSTITUTIONAL PLACEMENT ENGINE: DEMO STATE RESET")
56
+ print("========================================================")
57
+
58
+ # 1. Run migrations to ensure clean schema
59
+ print("[1/4] Checking and syncing SQLite schema via Alembic...")
60
+ alembic_ini_path = os.path.join(PROJECT_ROOT, "backend", "alembic.ini")
61
+ alembic_cfg = Config(alembic_ini_path)
62
+ alembic_cfg.set_main_option("script_location", os.path.join(PROJECT_ROOT, "backend", "alembic"))
63
+ command.upgrade(alembic_cfg, "head")
64
+
65
+ # 2. Clear transactional and staging tables cleanly
66
+ print("[2/4] Clearing transactional and staging tables...")
67
+ with SessionLocal() as db:
68
+ db.execute(delete(SlotModel))
69
+ db.execute(delete(ScheduleModel))
70
+ db.execute(delete(EligibilityDecisionModel))
71
+ db.execute(delete(PanelModel))
72
+ db.execute(delete(DriveRequirementVersionModel))
73
+ db.execute(delete(DriveModel))
74
+ db.execute(delete(CompanyModel))
75
+ db.execute(delete(BacklogModel))
76
+ db.execute(delete(OfferModel))
77
+ db.execute(delete(AcademicRecordModel))
78
+ db.execute(delete(UserModel))
79
+ db.execute(delete(StudentModel))
80
+ db.execute(delete(RoomModel))
81
+ db.execute(delete(AuditLogModel))
82
+ db.commit()
83
+
84
+ # 3. Seed Default Accounts
85
+ print("[3/4] Seeding default authentication accounts...")
86
+ seed_default_accounts()
87
+
88
+ # 4. Seed Standard Demo Rooms
89
+ rooms = [
90
+ RoomModel(id="hall_1", name="Auditorium Hall A", building="Main Block", capacity=150, has_computers=False),
91
+ RoomModel(id="hall_2", name="Seminar Hall B", building="Main Block", capacity=100, has_computers=False),
92
+ RoomModel(id="lab_1", name="Computing Lab 1", building="IT Block", capacity=80, has_computers=True),
93
+ RoomModel(id="lab_2", name="Computing Lab 2", building="IT Block", capacity=60, has_computers=True),
94
+ RoomModel(id="cabin_1", name="Interview Cabin 1", building="Placement Cell", capacity=6, has_computers=False),
95
+ RoomModel(id="cabin_2", name="Interview Cabin 2", building="Placement Cell", capacity=6, has_computers=False),
96
+ RoomModel(id="cabin_3", name="Interview Cabin 3", building="Placement Cell", capacity=6, has_computers=False),
97
+ RoomModel(id="cabin_4", name="Interview Cabin 4", building="Placement Cell", capacity=6, has_computers=False),
98
+ ]
99
+ db.add_all(rooms)
100
+
101
+ # 5. Enrich Demo Students (Personas A, B, C)
102
+ # Persona B: Priya Nair (Lateral Entry)
103
+ stu2 = db.execute(select(StudentModel).where(StudentModel.roll_no == "PIPE_002")).scalar_one()
104
+ stu2.name = "Priya Nair"
105
+ stu2.branch = "Information Technology"
106
+ stu2.cgpa = 7.8
107
+ stu2.twelfth_percentage = None
108
+ stu2.diploma_percentage = 82.5 # Lateral Entry
109
+ # Cleared backlog
110
+ db.add(
111
+ BacklogModel(
112
+ id="bl_pipe_002",
113
+ student_id=stu2.id,
114
+ subject="Engineering Mathematics",
115
+ semester=3,
116
+ status="cleared",
117
+ cleared_on=datetime.date(2026, 10, 15),
118
+ )
119
+ )
120
+ # Existing regular offer
121
+ db.add(
122
+ OfferModel(
123
+ id="off_pipe_002",
124
+ student_id=stu2.id,
125
+ company="Wipro",
126
+ ctc_lpa=6.0,
127
+ offer_date=datetime.date(2026, 10, 1),
128
+ source="on_campus",
129
+ is_dream=False,
130
+ status="accepted",
131
+ )
132
+ )
133
+
134
+ # Persona C: Student 3 (Rohan Gupta - 9.8 CGPA but Policy Blocked)
135
+ stu3 = StudentModel(
136
+ id="stu_pipe_003",
137
+ roll_no="PIPE_003",
138
+ name="Rohan Gupta",
139
+ email="rohan.gupta@college.edu",
140
+ branch="Computer Science and Engineering",
141
+ program="B.Tech",
142
+ cgpa=9.8,
143
+ tenth_percentage=95.0,
144
+ twelfth_percentage=96.0,
145
+ date_of_birth=datetime.date(2004, 2, 14),
146
+ )
147
+ db.add(stu3)
148
+ db.flush()
149
+
150
+ db.add(
151
+ OfferModel(
152
+ id="off_pipe_003",
153
+ student_id=stu3.id,
154
+ company="Infosys",
155
+ ctc_lpa=8.0,
156
+ offer_date=datetime.date(2026, 9, 25),
157
+ source="on_campus",
158
+ is_dream=False,
159
+ status="accepted",
160
+ )
161
+ )
162
+ db.add(
163
+ UserModel(
164
+ id="usr_stu_pipe_003",
165
+ username="PIPE_003",
166
+ email="rohan.gupta@college.edu",
167
+ password_hash=hash_password("123456"),
168
+ role="student",
169
+ student_id=stu3.id,
170
+ is_active=True,
171
+ )
172
+ )
173
+
174
+ # 6. Seed Demo Companies & Drives
175
+ today = datetime.date(2026, 11, 20)
176
+
177
+ c1 = CompanyModel(id="comp_techcorp", name="TechCorp Solutions", industry="IT Services", tier="Standard")
178
+ c2 = CompanyModel(id="comp_innotech", name="InnoTech Labs", industry="Product", tier="Dream")
179
+ c3 = CompanyModel(id="comp_northwind", name="Northwind Dynamics", industry="Deep Tech", tier="Super Dream")
180
+ db.add_all([c1, c2, c3])
181
+ db.flush()
182
+
183
+ # Drive 1: TechCorp (Regular, 7.5 LPA)
184
+ d1 = DriveModel(
185
+ id="drive_techcorp",
186
+ company_id="comp_techcorp",
187
+ company_name="TechCorp Solutions",
188
+ drive_date=today,
189
+ mode="on_campus",
190
+ ctc_lpa=7.5,
191
+ is_dream=False,
192
+ status="published",
193
+ version=1,
194
+ )
195
+ req1 = DriveRequirementVersionModel(
196
+ id="req_techcorp_v1",
197
+ drive_id="drive_techcorp",
198
+ version_number=1,
199
+ min_cgpa=7.0,
200
+ min_tenth_percentage=60.0,
201
+ min_twelfth_percentage=60.0,
202
+ allowed_branches_json='["Computer Science and Engineering", "Information Technology", "Electronics and Communication Engineering"]',
203
+ max_active_backlogs=0,
204
+ max_gap_years=1,
205
+ )
206
+ d1.requirement_versions.append(req1)
207
+
208
+ # Drive 2: InnoTech (Dream Drive, 14.0 LPA)
209
+ d2 = DriveModel(
210
+ id="drive_innotech",
211
+ company_id="comp_innotech",
212
+ company_name="InnoTech Labs",
213
+ drive_date=today,
214
+ mode="on_campus",
215
+ ctc_lpa=14.0,
216
+ is_dream=True,
217
+ status="published",
218
+ version=1,
219
+ )
220
+ req2 = DriveRequirementVersionModel(
221
+ id="req_innotech_v1",
222
+ drive_id="drive_innotech",
223
+ version_number=1,
224
+ min_cgpa=7.5,
225
+ min_tenth_percentage=65.0,
226
+ min_twelfth_percentage=65.0,
227
+ allowed_branches_json='["Computer Science and Engineering", "Information Technology"]',
228
+ max_active_backlogs=0,
229
+ max_gap_years=1,
230
+ )
231
+ d2.requirement_versions.append(req2)
232
+
233
+ # Drive 3: Northwind (Super-Dream Drive, 22.0 LPA)
234
+ d3 = DriveModel(
235
+ id="drive_northwind",
236
+ company_id="comp_northwind",
237
+ company_name="Northwind Dynamics",
238
+ drive_date=today + datetime.timedelta(days=1),
239
+ mode="on_campus",
240
+ ctc_lpa=22.0,
241
+ is_dream=True,
242
+ status="published",
243
+ version=1,
244
+ )
245
+ req3 = DriveRequirementVersionModel(
246
+ id="req_northwind_v1",
247
+ drive_id="drive_northwind",
248
+ version_number=1,
249
+ min_cgpa=8.5,
250
+ min_tenth_percentage=75.0,
251
+ min_twelfth_percentage=75.0,
252
+ allowed_branches_json='["Computer Science and Engineering"]',
253
+ max_active_backlogs=0,
254
+ max_gap_years=0,
255
+ )
256
+ d3.requirement_versions.append(req3)
257
+
258
+ db.add_all([d1, d2, d3])
259
+ db.commit()
260
+
261
+ elapsed = time.perf_counter() - start_time
262
+ print(f"[4/4] Demo state restored successfully in {elapsed:.3f} seconds (< 2.0s target).")
263
+ print("Accounts: officer/officer123 | coordinator/coord123 | PIPE_001/123456 | PIPE_002/123456 | PIPE_003/123456")
264
+ print("========================================================")
265
+
266
+
267
+ if __name__ == "__main__":
268
+ reset_demo()
scripts/seed_fixtures.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Seed database with synthetic dataset fixtures (students, initial drive, active policy).
3
+ """
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+ from datetime import date, datetime
9
+
10
+ ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
11
+ sys.path.insert(0, ROOT_DIR)
12
+
13
+ from backend.app.domain.models import Drive, DriveMode, DriveStatus, OfferPolicy, RequirementSet
14
+ from backend.app.importers.student_importer import StudentImporter
15
+ from backend.app.storage.database import SessionLocal
16
+ from backend.app.storage.repositories.sqlalchemy_repo import SqlAlchemyUnitOfWork
17
+
18
+
19
+ def seed() -> None:
20
+ print("Seeding database with synthetic dataset fixtures...")
21
+ uow = SqlAlchemyUnitOfWork(SessionLocal)
22
+
23
+ with uow:
24
+ # 1. Seed Active Policy
25
+ policy_path = os.path.join(ROOT_DIR, "fixtures", "policies", "policy_1_strict_one_offer.json")
26
+ with open(policy_path, encoding="utf-8") as f:
27
+ p_data = json.load(f)
28
+
29
+ existing_policy = uow.policies.get_active()
30
+ if not existing_policy:
31
+ policy = OfferPolicy(
32
+ id=p_data["id"],
33
+ name=p_data["name"],
34
+ version=p_data["version"],
35
+ max_offers_per_student=p_data["max_offers_per_student"],
36
+ dream_tier_ctc_lpa=p_data["dream_tier_ctc_lpa"],
37
+ super_dream_tier_ctc_lpa=p_data["super_dream_tier_ctc_lpa"],
38
+ allow_dream_upgrade=p_data["allow_dream_upgrade"],
39
+ upgrade_ctc_multiplier=p_data["upgrade_ctc_multiplier"],
40
+ strict_backlog_cutoff=p_data["strict_backlog_cutoff"],
41
+ allow_multiple_offers_if_different_tiers=p_data["allow_multiple_offers_if_different_tiers"],
42
+ is_active=p_data["is_active"],
43
+ )
44
+ uow.policies.add(policy)
45
+ print("Seeded active offer policy.")
46
+
47
+ # 2. Seed Sample Drive
48
+ drives = uow.drives.list_all()
49
+ if not drives:
50
+ sample_drive = Drive(
51
+ id="drive-msft-2026",
52
+ company_id="comp-msft",
53
+ company_name="Microsoft",
54
+ drive_date=date(2026, 11, 10),
55
+ mode=DriveMode.ON_CAMPUS,
56
+ ctc_lpa=44.0,
57
+ is_dream=True,
58
+ status=DriveStatus.PUBLISHED,
59
+ version=1,
60
+ )
61
+ req = RequirementSet(
62
+ id="req-msft-v1",
63
+ drive_id="drive-msft-2026",
64
+ version_number=1,
65
+ min_cgpa=8.0,
66
+ min_tenth_percentage=60.0,
67
+ min_twelfth_percentage=60.0,
68
+ allowed_branches=[
69
+ "Computer Science and Engineering",
70
+ "Information Technology",
71
+ "Electronics and Communication Engineering",
72
+ ],
73
+ max_active_backlogs=0,
74
+ allow_history_backlogs=True,
75
+ max_gap_years=1,
76
+ )
77
+ sample_drive.requirements = req
78
+ sample_drive.requirement_versions = [req]
79
+ uow.drives.add(sample_drive)
80
+ print("Seeded sample recruitment drive (Microsoft).")
81
+
82
+ # 3. Seed Students via Importer
83
+ student_count = uow.students.count()
84
+ if student_count == 0:
85
+ csv_path = os.path.join(ROOT_DIR, "fixtures", "students", "synthetic_students_1800.csv")
86
+ with open(csv_path, "rb") as f:
87
+ content = f.read()
88
+ importer = StudentImporter()
89
+ report = importer.parse_file(content, "synthetic_students_1800.csv", dry_run=False)
90
+ uow.students.save_batch(report.imported_students)
91
+ print(f"Seeded {len(report.imported_students)} students into SQLite database.")
92
+
93
+ uow.commit()
94
+ print("Seeding completed successfully.")
95
+
96
+
97
+ if __name__ == "__main__":
98
+ seed()
start.bat ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ setlocal
3
+ cd /d "%~dp0"
4
+ title Institutional Placement Engine
5
+
6
+ echo ======================================================================
7
+ echo Launching Institutional Placement Eligibility & Scheduling Engine...
8
+ echo ======================================================================
9
+
10
+ if exist ".venv\Scripts\python.exe" (
11
+ ".venv\Scripts\python.exe" scripts\lan_launcher.py
12
+ ) else (
13
+ python scripts\lan_launcher.py
14
+ )
15
+ pause
start.sh ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ echo "======================================================================"
5
+ echo "Launching Institutional Placement Eligibility & Scheduling Engine..."
6
+ echo "======================================================================"
7
+
8
+ python3 scripts/lan_launcher.py