File size: 20,686 Bytes
788eab4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
"""

Fixture Generator for Placement Engine.

Generates:

  1. 1,800 synthetic student records across 9 branches (CSV and XLSX).

     Includes edge cases: cleared backlogs yesterday/last month, lateral entries, gaps, existing offers.

  2. 25 synthetic drive announcements in mixed formats:

     - Plain text (.txt)

     - Standard email (.eml)

     - PDF documents (.pdf)

     - Microsoft Word documents (.docx)

  3. 3 realistic college placement offer policies with documented ambiguities.

"""

from __future__ import annotations

import csv
import datetime
import json
import os
import random
from email.message import EmailMessage

import docx
import openpyxl
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas

# Deterministic seed for reproducible generation
RANDOM_SEED = 42
random.seed(RANDOM_SEED)

ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
FIXTURES_DIR = os.path.join(ROOT_DIR, "fixtures")
STUDENTS_DIR = os.path.join(FIXTURES_DIR, "students")
ANNOUNCEMENTS_DIR = os.path.join(FIXTURES_DIR, "announcements")
POLICIES_DIR = os.path.join(FIXTURES_DIR, "policies")

os.makedirs(STUDENTS_DIR, exist_ok=True)
os.makedirs(ANNOUNCEMENTS_DIR, exist_ok=True)
os.makedirs(POLICIES_DIR, exist_ok=True)

BRANCHES = [
    "Computer Science and Engineering",
    "Information Technology",
    "Electronics and Communication Engineering",
    "Electrical and Electronics Engineering",
    "Mechanical Engineering",
    "Civil Engineering",
    "Chemical Engineering",
    "Biotechnology",
    "Data Science",
]

FIRST_NAMES = [
    "Aarav", "Aditi", "Akash", "Ananya", "Aniket", "Ankita", "Arjun", "Bhavna", "Chirag", "Deepak",
    "Divya", "Gaurav", "Harsh", "Isha", "Ishaan", "Kavya", "Manish", "Meera", "Mohit", "Neha",
    "Nikhil", "Pooja", "Pranav", "Priya", "Rahul", "Rhea", "Rohan", "Sakshi", "Sameer", "Sanjay",
    "Shreya", "Siddharth", "Sneha", "Tanvi", "Tarun", "Varun", "Vikram", "Yash", "Zoya", "Kunal"
]

LAST_NAMES = [
    "Sharma", "Verma", "Patel", "Gupta", "Singh", "Kumar", "Mehta", "Shah", "Reddy", "Nair",
    "Iyer", "Rao", "Joshi", "Kulkarni", "Deshmukh", "Chopra", "Malhotra", "Bhatia", "Saxena", "Agarwal"
]


def generate_students(count: int = 1800) -> list[dict]:
    students = []
    base_roll = 20210000

    # Today's reference date: 2026-10-22
    ref_date = datetime.date(2026, 10, 22)

    for i in range(count):
        roll_no = f"BT21{i + 1:04d}"
        first_name = random.choice(FIRST_NAMES)
        last_name = random.choice(LAST_NAMES)
        name = f"{first_name} {last_name}"
        email = f"{first_name.lower()}.{last_name.lower()}{i + 1}@college.edu"
        branch = BRANCHES[i % len(BRANCHES)]

        # Realistic CGPA: Gaussian centered at 7.6, clamped between 5.2 and 9.9
        cgpa = min(9.95, max(5.2, round(random.gauss(7.6, 1.1), 2)))

        # 10th and 12th marks
        tenth = min(98.0, max(60.0, round(random.gauss(84.0, 8.0), 2)))

        # 10% lateral entry with diploma instead of 12th
        is_lateral = (i % 10 == 0)
        twelfth = None if is_lateral else min(98.0, max(55.0, round(random.gauss(80.0, 9.0), 2)))
        diploma = min(95.0, max(65.0, round(random.gauss(82.0, 6.0), 2))) if is_lateral else None

        # Age between 21 and 23
        birth_year = 2003 if not is_lateral else 2002
        birth_month = random.randint(1, 12)
        birth_day = random.randint(1, 28)
        dob = datetime.date(birth_year, birth_month, birth_day)

        # Backlog generation
        # ~70% zero backlogs, ~15% active, ~15% cleared
        backlog_status = "none"
        backlogs_active_count = 0
        backlogs_history_count = 0
        backlog_detail = ""

        if 70 <= (i % 100) < 85:
            # Active backlogs
            backlogs_active_count = random.randint(1, 2)
            backlogs_history_count = backlogs_active_count
            backlog_detail = f"Data Structures (active); Engineering Mathematics (active)" if backlogs_active_count > 1 else "Data Structures (active)"
        elif (i % 100) >= 85:
            # Cleared backlogs
            backlogs_active_count = 0
            backlogs_history_count = 1

            # KEY EDGE CASES:
            # i % 20 == 0: Cleared yesterday (1 day before reference date)
            # i % 20 == 1: Cleared last month (30 days before reference date)
            if i % 20 == 0:
                clear_date = ref_date - datetime.timedelta(days=1)
                backlog_detail = f"Engineering Mathematics (cleared {clear_date.isoformat()})"
            elif i % 20 == 1:
                clear_date = ref_date - datetime.timedelta(days=30)
                backlog_detail = f"Operating Systems (cleared {clear_date.isoformat()})"
            else:
                clear_date = ref_date - datetime.timedelta(days=random.randint(60, 300))
                backlog_detail = f"Computer Networks (cleared {clear_date.isoformat()})"

        # Gap year (10% of students have 1 gap year)
        gap_years = 1 if (i % 10 == 3) else 0

        # Existing offers
        # ~20% of students have an existing offer
        existing_offer_str = ""
        if i % 5 == 0:
            if i % 25 == 0:
                # Dream company offer
                existing_offer_str = "Microsoft: 32.0 LPA (accepted)"
            elif i % 15 == 0:
                existing_offer_str = "Infosys: 9.5 LPA (offered)"
            else:
                existing_offer_str = "TCS: 4.0 LPA (accepted)"

        # Protected demographics (stored for institutional analysis, never used in rules)
        gender = "Female" if (i % 3 == 0) else "Male"
        category = "General" if (i % 2 == 0) else ("OBC" if (i % 4 == 1) else "SC")

        students.append({
            "roll_no": roll_no,
            "name": name,
            "email": email,
            "branch": branch,
            "program": "B.Tech",
            "cgpa": cgpa,
            "tenth_percentage": tenth,
            "twelfth_percentage": twelfth if twelfth is not None else "",
            "diploma_percentage": diploma if diploma is not None else "",
            "date_of_birth": dob.isoformat(),
            "active_backlogs": backlogs_active_count,
            "history_backlogs": backlogs_history_count,
            "backlog_details": backlog_detail,
            "gap_years": gap_years,
            "existing_offers": existing_offer_str,
            "gender": gender,
            "category": category,
        })

    return students


def export_students_csv_and_xlsx(students: list[dict]) -> None:
    # CSV
    csv_path = os.path.join(STUDENTS_DIR, "synthetic_students_1800.csv")
    fieldnames = list(students[0].keys())
    with open(csv_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(students)
    print(f"Exported {len(students)} synthetic students to {csv_path}")

    # XLSX
    xlsx_path = os.path.join(STUDENTS_DIR, "synthetic_students_1800.xlsx")
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "Student Directory"
    ws.append(fieldnames)
    for s in students:
        ws.append([s[k] for k in fieldnames])
    wb.save(xlsx_path)
    print(f"Exported {len(students)} synthetic students to {xlsx_path}")


# ---------------------------------------------------------
# Announcements Generator (25 Mixed-Format Announcements)
# ---------------------------------------------------------

COMPANIES_CONFIG = [
    {"name": "Google", "ctc": 36.0, "cgpa": 8.5, "backlogs": "Strictly 0 (No active or historical backlogs)", "branches": ["CSE", "IT", "Data Science"], "mode": "Hybrid"},
    {"name": "Microsoft", "ctc": 44.0, "cgpa": 8.0, "backlogs": "0 active backlogs", "branches": ["CSE", "IT", "ECE"], "mode": "On Campus"},
    {"name": "Amazon", "ctc": 32.0, "cgpa": 7.5, "backlogs": "No active backlogs allowed", "branches": ["CSE", "IT", "ECE", "EEE"], "mode": "On Campus"},
    {"name": "Goldman Sachs", "ctc": 28.0, "cgpa": 8.0, "backlogs": "No history of backlogs", "branches": ["All Branches"], "mode": "Hybrid"},
    {"name": "Oracle", "ctc": 19.5, "cgpa": 7.0, "backlogs": "0 active arrears at time of drive", "branches": ["CSE", "IT", "ECE"], "mode": "On Campus"},
    {"name": "Qualcomm", "ctc": 22.0, "cgpa": 7.8, "backlogs": "No active backlogs", "branches": ["ECE", "EEE", "CSE"], "mode": "On Campus"},
    {"name": "Cisco", "ctc": 17.5, "cgpa": 7.5, "backlogs": "Maximum 1 cleared backlog permitted, 0 active", "branches": ["CSE", "IT", "ECE"], "mode": "On Campus"},
    {"name": "Deloitte USI", "ctc": 9.0, "cgpa": 6.8, "backlogs": "No standing arrears", "branches": ["All Branches"], "mode": "On Campus"},
    {"name": "Tata Consultancy Services", "ctc": 7.0, "cgpa": 6.0, "backlogs": "Max 1 active backlog allowed", "branches": ["All Branches"], "mode": "On Campus"},
    {"name": "Infosys Power Programmer", "ctc": 9.5, "cgpa": 6.5, "backlogs": "0 active backlogs", "branches": ["CSE", "IT", "ECE", "Data Science"], "mode": "Virtual"},
    {"name": "Wipro Turbo", "ctc": 6.5, "cgpa": 6.0, "backlogs": "No active backlogs at time of joining", "branches": ["All Branches"], "mode": "On Campus"},
    {"name": "Accenture Advanced Tech", "ctc": 11.5, "cgpa": 7.0, "backlogs": "0 active backlogs, max 1 year education gap", "branches": ["CSE", "IT", "ECE", "EEE", "Data Science"], "mode": "On Campus"},
    {"name": "Siemens", "ctc": 12.0, "cgpa": 7.0, "backlogs": "No active backlogs", "branches": ["ECE", "EEE", "Mechanical Engineering"], "mode": "On Campus"},
    {"name": "Larsen & Toubro", "ctc": 6.5, "cgpa": 6.5, "backlogs": "Strictly no history of backlogs", "branches": ["Mechanical Engineering", "Civil Engineering", "EEE"], "mode": "On Campus"},
    {"name": "Texas Instruments", "ctc": 25.0, "cgpa": 8.0, "backlogs": "No active backlogs", "branches": ["ECE", "EEE"], "mode": "On Campus"},
    {"name": "Intel", "ctc": 21.0, "cgpa": 7.5, "backlogs": "0 active backlogs", "branches": ["ECE", "CSE", "IT"], "mode": "Hybrid"},
    {"name": "Morgan Stanley", "ctc": 26.0, "cgpa": 8.2, "backlogs": "Zero backlogs throughout", "branches": ["CSE", "IT"], "mode": "Virtual"},
    {"name": "Samsung R&D", "ctc": 16.0, "cgpa": 7.5, "backlogs": "No active backlogs", "branches": ["CSE", "IT", "ECE"], "mode": "On Campus"},
    {"name": "Tata Motors", "ctc": 8.5, "cgpa": 6.5, "backlogs": "No active backlogs, max 1 cleared", "branches": ["Mechanical Engineering", "Electrical and Electronics Engineering"], "mode": "On Campus"},
    {"name": "Reliance Industries", "ctc": 7.5, "cgpa": 6.8, "backlogs": "No active backlogs", "branches": ["Chemical Engineering", "Mechanical Engineering", "Civil Engineering"], "mode": "On Campus"},
    {"name": "ITC Limited", "ctc": 11.0, "cgpa": 7.2, "backlogs": "No history of backlogs", "branches": ["Mechanical Engineering", "Chemical Engineering"], "mode": "On Campus"},
    {"name": "Biocon", "ctc": 7.0, "cgpa": 6.5, "backlogs": "0 active backlogs", "branches": ["Biotechnology", "Chemical Engineering"], "mode": "On Campus"},
    {"name": "ZS Associates", "ctc": 13.5, "cgpa": 7.0, "backlogs": "No standing backlogs", "branches": ["All Branches"], "mode": "Virtual"},
    {"name": "Barclays", "ctc": 14.0, "cgpa": 7.5, "backlogs": "No active arrears", "branches": ["CSE", "IT", "Data Science", "ECE"], "mode": "Hybrid"},
    {"name": "Atlassian", "ctc": 52.0, "cgpa": 8.5, "backlogs": "0 active backlogs, strictly clean academic record", "branches": ["CSE", "IT", "Data Science"], "mode": "Virtual"},
]


def generate_announcements() -> None:
    for idx, c in enumerate(COMPANIES_CONFIG):
        drive_num = idx + 1
        drive_date = (datetime.date(2026, 11, 1) + datetime.timedelta(days=idx * 3)).isoformat()
        branches_str = ", ".join(c["branches"])

        content_text = f"""CAMPUS RECRUITMENT DRIVE ANNOUNCEMENT #{drive_num:02d}

Company: {c['name']}

Drive Date: {drive_date}

Mode of Drive: {c['mode']}

CTC Offered: {c['ctc']} LPA



ELIGIBILITY CRITERIA:

1. Degree: B.Tech / B.E. (Batch of 2026)

2. Minimum CGPA Cut-off: {c['cgpa']} or above in B.Tech

3. 10th & 12th Percentage: Minimum 60.0% aggregate

4. Backlog Policy: {c['backlogs']}

5. Allowed Disciplines / Branches: {branches_str}

6. Education Gap: Maximum 1 year education gap permissible between 12th and degree. No gaps during engineering.



SELECTION PROCESS:

Round 1: Online Technical Assessment (Cognitive & Coding) - 90 mins

Round 2: Technical Interview Panel 1 - 45 mins

Round 3: Technical & System Design Panel 2 - 45 mins

Round 4: HR & Leadership Interview - 30 mins



Students meeting the cut-offs are requested to register on the placement portal by Friday 5:00 PM.

"""

        # Format allocation:
        # 1-7: Plain Text (.txt)
        # 8-13: Email (.eml)
        # 14-19: PDF (.pdf)
        # 20-25: DOCX (.docx)

        clean_comp_name = c["name"].lower().replace(" ", "_").replace("&", "and")

        if drive_num <= 7:
            # Plain Text
            filepath = os.path.join(ANNOUNCEMENTS_DIR, f"{drive_num:02d}_{clean_comp_name}_announcement.txt")
            with open(filepath, "w", encoding="utf-8") as f:
                f.write(content_text)
        elif drive_num <= 13:
            # Email .eml
            filepath = os.path.join(ANNOUNCEMENTS_DIR, f"{drive_num:02d}_{clean_comp_name}_email.eml")
            msg = EmailMessage()
            msg["Subject"] = f"Campus Recruitment Announcement: {c['name']} (CTC: {c['ctc']} LPA)"
            msg["From"] = f"campus-hiring@{clean_comp_name}.com"
            msg["To"] = "placements@college.edu"
            msg["Date"] = "Mon, 27 Oct 2026 10:00:00 +0530"
            msg.set_content(content_text)
            with open(filepath, "wb") as f:
                f.write(msg.as_bytes())
        elif drive_num <= 19:
            # PDF
            filepath = os.path.join(ANNOUNCEMENTS_DIR, f"{drive_num:02d}_{clean_comp_name}_spec.pdf")
            c_pdf = canvas.Canvas(filepath, pagesize=letter)
            c_pdf.setFont("Helvetica-Bold", 14)
            c_pdf.drawString(50, 750, f"Campus Recruitment: {c['name']}")
            c_pdf.setFont("Helvetica", 10)
            y = 720
            for line in content_text.splitlines():
                if y < 50:
                    c_pdf.showPage()
                    y = 750
                    c_pdf.setFont("Helvetica", 10)
                c_pdf.drawString(50, y, line)
                y -= 15
            c_pdf.save()
        else:
            # DOCX
            filepath = os.path.join(ANNOUNCEMENTS_DIR, f"{drive_num:02d}_{clean_comp_name}_jd.docx")
            doc = docx.Document()
            doc.add_heading(f"Recruitment Drive: {c['name']}", 0)
            for line in content_text.splitlines():
                if line.startswith("CAMPUS") or line.startswith("ELIGIBILITY") or line.startswith("SELECTION"):
                    doc.add_heading(line, level=2)
                else:
                    doc.add_paragraph(line)
            doc.save(filepath)

    print(f"Generated 25 mixed-format synthetic announcements in {ANNOUNCEMENTS_DIR}")


# ---------------------------------------------------------
# Sample Offer Policies (with deliberate real-world ambiguities)
# ---------------------------------------------------------

POLICIES = [
    {
        "id": "policy_1_strict_one_offer",
        "name": "Strict One-Offer with Dream Tier Exception",
        "version": 1,
        "max_offers_per_student": 1,
        "dream_tier_ctc_lpa": 10.0,
        "super_dream_tier_ctc_lpa": 20.0,
        "allow_dream_upgrade": True,
        "upgrade_ctc_multiplier": 1.5,
        "strict_backlog_cutoff": True,
        "allow_multiple_offers_if_different_tiers": True,
        "is_active": True,
        "summary": "Each student is strictly limited to 1 campus offer. If the offer CTC is < 10 LPA, they may participate in 'Dream' drives offering >= 10 LPA.",
        "deliberate_ambiguities": [
            "Off-campus offers: Does not state whether an undeclared off-campus offer disqualifies a student from receiving a second campus offer.",
            "Salary components: Ambiguity over whether the 10.0 LPA cut-off applies to Cost-to-Company (CTC) including retention bonus or fixed base pay only.",
            "Cleared backlogs: Ambiguity over whether a student whose backlog exam results were declared AFTER the drive test but BEFORE the interview is eligible."
        ]
    },
    {
        "id": "policy_2_progressive_multiplier",
        "name": "Progressive Salary Multiplier Policy (1.5x Rule)",
        "version": 1,
        "max_offers_per_student": 2,
        "dream_tier_ctc_lpa": 12.0,
        "super_dream_tier_ctc_lpa": 25.0,
        "allow_dream_upgrade": True,
        "upgrade_ctc_multiplier": 1.5,
        "strict_backlog_cutoff": False,
        "allow_multiple_offers_if_different_tiers": True,
        "is_active": False,
        "summary": "Students may hold up to 2 offers, provided the second offer is at least 1.5x the CTC of the first offer.",
        "deliberate_ambiguities": [
            "Concurrent drives: If a student appears for two drives on the same day and receives offers from both simultaneously, which one takes precedence?",
            "Offer decline: Can a student decline an accepted offer to regain eligibility for a standard drive? Policy mentions no fee or penalty.",
            "Lateral entry eligibility: Does not explicitly state whether diploma aggregate replaces 12th percentage for cut-off calculations."
        ]
    },
    {
        "id": "policy_3_tiered_quota",
        "name": "Tier-Based Quota Policy (Standard, Dream, Super-Dream)",
        "version": 1,
        "max_offers_per_student": 3,
        "dream_tier_ctc_lpa": 8.0,
        "super_dream_tier_ctc_lpa": 18.0,
        "allow_dream_upgrade": True,
        "upgrade_ctc_multiplier": 1.25,
        "strict_backlog_cutoff": True,
        "allow_multiple_offers_if_different_tiers": True,
        "is_active": False,
        "summary": "Students can secure up to 1 offer in Standard (<8 LPA), 1 in Dream (8-18 LPA), and 1 in Super-Dream (>18 LPA).",
        "deliberate_ambiguities": [
            "Timing of re-evaluation: If a student's active backlog is converted to a pass via university re-evaluation two days after the drive registration deadline, does the placement cell permit late entry?",
            "Dream offer lock: Once a Super-Dream offer is obtained, is the student banned from participating even if the job profile differs radically?",
            "Verbal offer vs written letter: Does an official verbal confirmation during campus visit block the student immediately or only upon receiving the physical letter?"
        ]
    }
]


def generate_policies() -> None:
    for p in POLICIES:
        json_path = os.path.join(POLICIES_DIR, f"{p['id']}.json")
        with open(json_path, "w", encoding="utf-8") as f:
            json.dump(p, f, indent=2)

        md_path = os.path.join(POLICIES_DIR, f"{p['id']}.md")
        with open(md_path, "w", encoding="utf-8") as f:
            f.write(f"# Placement Policy: {p['name']}\n\n")
            f.write(f"**Version**: {p['version']} | **Status**: {'Active' if p['is_active'] else 'Inactive'}\n\n")
            f.write(f"## Summary\n{p['summary']}\n\n")
            f.write("## Rules Specification\n")
            f.write(f"- Max offers permitted: {p['max_offers_per_student']}\n")
            f.write(f"- Dream Tier CTC: {p['dream_tier_ctc_lpa']} LPA\n")
            f.write(f"- Super-Dream Tier CTC: {p['super_dream_tier_ctc_lpa']} LPA\n")
            f.write(f"- Minimum upgrade multiplier: {p['upgrade_ctc_multiplier']}x\n")
            f.write(f"- Backlog cutoff strictness: {p['strict_backlog_cutoff']}\n\n")
            f.write("## Deliberate Policy Ambiguities (Known Edge Cases)\n")
            for amb in p["deliberate_ambiguities"]:
                f.write(f"- {amb}\n")
    print(f"Generated 3 sample offer policies in {POLICIES_DIR}")


def main() -> None:
    print("Generating synthetic dataset fixtures...")
    students = generate_students(1800)
    export_students_csv_and_xlsx(students)
    generate_announcements()
    generate_policies()
    print("All synthetic fixtures generated successfully.")


if __name__ == "__main__":
    main()