File size: 7,549 Bytes
f804bd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
File-System Database Layer

Replaces MySQL with JSON file storage for students, rooms, and allocations.
Mirrors the original MySQL schema:
  - main table  β†’ data/students.json
  - RoomNum     β†’ data/rooms.json
  - results     β†’ data/allocations.json
"""

import json
import os
from typing import List, Optional

# ─── Paths ────────────────────────────────────────────────────────────────────

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")

STUDENTS_FILE = os.path.join(DATA_DIR, "students.json")
ROOMS_FILE = os.path.join(DATA_DIR, "rooms.json")
ALLOCATIONS_FILE = os.path.join(DATA_DIR, "allocations.json")


def _ensure_data_dir():
    """Create data directory if it doesn't exist."""
    os.makedirs(DATA_DIR, exist_ok=True)


def _read_json(filepath: str) -> list:
    """Read a JSON file and return the parsed list."""
    _ensure_data_dir()
    if not os.path.exists(filepath):
        with open(filepath, "w") as f:
            json.dump([], f)
        return []
    try:
        with open(filepath, "r") as f:
            data = json.load(f)
        return data if isinstance(data, list) else []
    except (json.JSONDecodeError, IOError):
        return []


def _write_json(filepath: str, data: list):
    """Write a list to a JSON file."""
    _ensure_data_dir()
    with open(filepath, "w") as f:
        json.dump(data, f, indent=2)


# ─── Student Operations ──────────────────────────────────────────────────────


def get_all_students() -> List[dict]:
    """Get all students from the database."""
    return _read_json(STUDENTS_FILE)


def get_student_by_id(student_id: int) -> Optional[dict]:
    """Get a specific student by ID."""
    students = get_all_students()
    for s in students:
        if s["id"] == student_id:
            return s
    return None


def add_student(student: dict) -> bool:
    """
    Add a new student.

    student dict should contain:
      - id: int
      - name: str
      - cgpa: float
      - pref_roommate: List[int]  (ordered list of preferred roommate IDs)
      - pref_room: List[int]      (ordered list of preferred room IDs)
    """
    students = get_all_students()
    # Check for duplicate ID
    for s in students:
        if s["id"] == student["id"]:
            return False
    students.append(student)
    _write_json(STUDENTS_FILE, students)
    return True


def update_student(student_id: int, updated_data: dict) -> bool:
    """Update an existing student's data."""
    students = get_all_students()
    for i, s in enumerate(students):
        if s["id"] == student_id:
            students[i].update(updated_data)
            _write_json(STUDENTS_FILE, students)
            return True
    return False


def delete_student(student_id: int) -> bool:
    """Delete a student by ID."""
    students = get_all_students()
    new_students = [s for s in students if s["id"] != student_id]
    if len(new_students) == len(students):
        return False
    _write_json(STUDENTS_FILE, new_students)
    return True


def clear_all_students():
    """Remove all students."""
    _write_json(STUDENTS_FILE, [])


def save_all_students(students: List[dict]):
    """Overwrite the entire students database."""
    _write_json(STUDENTS_FILE, students)


def get_student_count() -> int:
    """Get the total number of students."""
    return len(get_all_students())


# ─── Room Operations ─────────────────────────────────────────────────────────


def get_all_rooms() -> List[dict]:
    """Get all rooms from the database."""
    return _read_json(ROOMS_FILE)


def add_room(room: dict) -> bool:
    """
    Add a new room.

    room dict should contain:
      - room_id: int
      - room_number: str (e.g., 'A101')
    """
    rooms = get_all_rooms()
    for r in rooms:
        if r["room_id"] == room["room_id"]:
            return False
    rooms.append(room)
    _write_json(ROOMS_FILE, rooms)
    return True


def delete_room(room_id: int) -> bool:
    """Delete a room by ID."""
    rooms = get_all_rooms()
    new_rooms = [r for r in rooms if r["room_id"] != room_id]
    if len(new_rooms) == len(rooms):
        return False
    _write_json(ROOMS_FILE, new_rooms)
    return True


def clear_all_rooms():
    """Remove all rooms."""
    _write_json(ROOMS_FILE, [])


def save_all_rooms(rooms: List[dict]):
    """Overwrite the entire rooms database."""
    _write_json(ROOMS_FILE, rooms)


def get_room_count() -> int:
    """Get the total number of rooms."""
    return len(get_all_rooms())


# ─── Allocation Operations ───────────────────────────────────────────────────


def get_all_allocations() -> List[dict]:
    """Get all allocation results."""
    return _read_json(ALLOCATIONS_FILE)


def save_allocations(allocations: List[dict]):
    """Save allocation results (overwrites previous results)."""
    _write_json(ALLOCATIONS_FILE, allocations)


def clear_allocations():
    """Clear all allocation results."""
    _write_json(ALLOCATIONS_FILE, [])


# ─── Bulk Import from CSV ────────────────────────────────────────────────────


def import_students_from_csv(csv_content: str) -> tuple:
    """
    Import students from CSV content.

    Expected columns: id, name, cgpa, pref_roommate, pref_room
    pref_roommate and pref_room should be space-separated integers.

    Returns:
        (success_count, error_messages)
    """
    import csv
    import io

    reader = csv.DictReader(io.StringIO(csv_content))
    students = []
    errors = []

    for row_num, row in enumerate(reader, start=2):
        try:
            student = {
                "id": int(row["id"].strip()),
                "name": row["name"].strip(),
                "cgpa": float(row["cgpa"].strip()),
                "pref_roommate": [
                    int(x) for x in row["pref_roommate"].strip().split()
                ],
                "pref_room": [
                    int(x) for x in row["pref_room"].strip().split()
                ],
            }
            students.append(student)
        except (KeyError, ValueError) as e:
            errors.append(f"Row {row_num}: {str(e)}")

    if students:
        save_all_students(students)

    return len(students), errors


def import_rooms_from_csv(csv_content: str) -> tuple:
    """
    Import rooms from CSV content.

    Expected columns: room_id, room_number

    Returns:
        (success_count, error_messages)
    """
    import csv
    import io

    reader = csv.DictReader(io.StringIO(csv_content))
    rooms = []
    errors = []

    for row_num, row in enumerate(reader, start=2):
        try:
            room = {
                "room_id": int(row["room_id"].strip()),
                "room_number": row["room_number"].strip(),
            }
            rooms.append(room)
        except (KeyError, ValueError) as e:
            errors.append(f"Row {row_num}: {str(e)}")

    if rooms:
        save_all_rooms(rooms)

    return len(rooms), errors