|
8 | 8 | from ediauth.models import Profile |
9 | 9 | from answers.models import Answer, AnswerSection, Comment, Exam, ExamType |
10 | 10 | from documents.models import DocumentType, Document, DocumentFile |
11 | | -from categories.models import Category, MetaCategory, EuclidCode |
| 11 | +from categories.models import Category, MetaCategory, EuclidCode, CourseStats |
12 | 12 | from feedback.models import Feedback |
13 | 13 | from filestore.models import Attachment |
14 | 14 | from images.models import Image |
15 | 15 | from notifications.models import Notification, NotificationSetting, NotificationType |
16 | 16 | import os |
| 17 | +import random |
17 | 18 | from answers import pdf_utils |
18 | 19 |
|
19 | 20 |
|
@@ -329,12 +330,110 @@ def create_documents(self): |
329 | 330 | if (i + user.id) % 4 == 0: |
330 | 331 | document.likes.add(user) |
331 | 332 |
|
| 333 | + def create_course_stats(self): |
| 334 | + self.stdout.write("Create course statistics") |
| 335 | + |
| 336 | + # Generate realistic course names for Informatics courses |
| 337 | + course_name_patterns = [ |
| 338 | + "Algorithms and Data Structures", |
| 339 | + "Secure Programming", |
| 340 | + "Machine Learning", |
| 341 | + "Software Engineering and Professional Practice", |
| 342 | + "Introduction to Databases", |
| 343 | + "Systems Design Project", |
| 344 | + "Operating Systems", |
| 345 | + "Human-Computer Interaction", |
| 346 | + "Reasoning and Agents", |
| 347 | + "Cyber Security", |
| 348 | + "Applied Cloud Programming", |
| 349 | + "Distributed Systems", |
| 350 | + "Computer Vision", |
| 351 | + ] |
| 352 | + |
| 353 | + # Mock Course Organisers with some generic and funny names |
| 354 | + course_organisers = [ |
| 355 | + "Dr. John Smith", |
| 356 | + "Prof. Mary Johnson", |
| 357 | + "Dr. Bob Wilson", |
| 358 | + "Prof. Alice Brown", |
| 359 | + "Dr. Gandalf McTeachface", |
| 360 | + ] |
| 361 | + |
| 362 | + # Academic years from 2017-18 to 2024-25 |
| 363 | + academic_years = [ |
| 364 | + "2017-18", |
| 365 | + "2018-19", |
| 366 | + "2019-20", |
| 367 | + "2020-21", |
| 368 | + "2021-22", |
| 369 | + "2022-23", |
| 370 | + "2023-24", |
| 371 | + "2024-25", |
| 372 | + ] |
| 373 | + |
| 374 | + objs = [] |
| 375 | + |
| 376 | + # Get all Euclid codes from categories |
| 377 | + euclid_codes = EuclidCode.objects.all() |
| 378 | + |
| 379 | + for euclid_code in euclid_codes: |
| 380 | + # Seed random number generator based on euclid code for reproducible data |
| 381 | + random.seed(hash(euclid_code.code)) |
| 382 | + |
| 383 | + # Pick a random course name pattern |
| 384 | + base_course_name = random.choice(course_name_patterns) |
| 385 | + course_name = f"{base_course_name} ({euclid_code.category.displayname})" |
| 386 | + |
| 387 | + # Pick a random course organiser (could change over years) |
| 388 | + # Some courses might change organiser, others might stay the same |
| 389 | + base_organiser = random.choice(course_organisers) |
| 390 | + |
| 391 | + # Generate stats for each academic year |
| 392 | + for year in academic_years: |
| 393 | + # Organiser might change sometimes (20% chance per year) |
| 394 | + current_organiser = base_organiser |
| 395 | + if random.random() < 0.2: # 20% chance of organiser change |
| 396 | + current_organiser = random.choice(course_organisers) |
| 397 | + base_organiser = current_organiser # Update for future years |
| 398 | + |
| 399 | + # Generate realistic grade statistics |
| 400 | + # Mean marks typically range from 45-85, with most courses 55-75 |
| 401 | + base_mean = random.uniform(55, 75) |
| 402 | + |
| 403 | + # Add some year-to-year variation (-5 to +5) |
| 404 | + year_variation = random.uniform(-5, 5) |
| 405 | + mean_mark = max(45, min(85, base_mean + year_variation)) |
| 406 | + |
| 407 | + # Standard deviation typically 10-25, with most 12-20 |
| 408 | + std_deviation = random.uniform(12, 20) |
| 409 | + |
| 410 | + # Some years might have missing data (simulate N/A values) |
| 411 | + if random.random() < 0.05: # 5% chance of missing data |
| 412 | + mean_mark = None |
| 413 | + std_deviation = None |
| 414 | + |
| 415 | + objs.append( |
| 416 | + CourseStats( |
| 417 | + course_name=course_name, |
| 418 | + course_code=euclid_code.code, |
| 419 | + mean_mark=mean_mark, |
| 420 | + std_deviation=std_deviation, |
| 421 | + academic_year=year, |
| 422 | + course_organiser=current_organiser, |
| 423 | + ) |
| 424 | + ) |
| 425 | + |
| 426 | + # Bulk create all course stats |
| 427 | + CourseStats.objects.bulk_create(objs, ignore_conflicts=True) |
| 428 | + self.stdout.write(f"Created {len(objs)} course statistics entries") |
| 429 | + |
332 | 430 | def handle(self, *args, **options): |
333 | 431 | self.flush_db() |
334 | 432 | self.create_users() |
335 | 433 | self.create_images() |
336 | 434 | self.create_meta_categories() |
337 | 435 | self.create_categories() |
| 436 | + self.create_course_stats() |
338 | 437 | self.create_exam_types() |
339 | 438 | self.create_exams() |
340 | 439 | self.create_answer_sections() |
|
0 commit comments