def post(self, request, *args, **kwargs):
        profile = self.request.user.user_profile
        current_calendar = get_current_academic_calendar()

        if not current_calendar:
            raise Http404()

        study_class = get_object_or_404(
            StudyClass,
            id=self.kwargs['study_class_id'],
            academic_year=current_calendar.academic_year)
        subject = get_object_or_404(
            Subject.objects.distinct(),
            id=self.kwargs['subject_id'],
            teacher_class_through__study_class=study_class,
            teacher_class_through__teacher=profile,
        )

        serializer = CsvUploadSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        file = serializer.validated_data['file']
        importer = CatalogsImporter(file=file,
                                    study_class=study_class,
                                    subject=subject,
                                    current_calendar=current_calendar)

        return Response(data=importer.import_catalogs_and_get_report())
    def get_serializer_context(self):
        context = super().get_serializer_context()
        study_class = self.get_study_class()
        subject = self.get_subject()
        calendar = get_current_academic_calendar()

        is_technological_school = has_technological_category(
            study_class.school_unit)
        working_weeks_count_sem1 = get_working_weeks_count(
            calendar, 1, study_class, is_technological_school)
        working_weeks_count_sem2 = get_working_weeks_count(
            calendar, 2, study_class, is_technological_school)
        weekly_hours_count = get_weekly_hours_count(study_class, subject.id)

        third_of_hours_count_sem1 = (working_weeks_count_sem1 *
                                     weekly_hours_count) // 3
        third_of_hours_count_sem2 = (working_weeks_count_sem2 *
                                     weekly_hours_count) // 3

        context.update({
            'avg_limit':
            get_avg_limit_for_subject(study_class, subject.is_coordination,
                                      subject.id),
            'third_of_hours_count_sem1':
            third_of_hours_count_sem1,
            'third_of_hours_count_sem2':
            third_of_hours_count_sem2,
            'third_of_hours_count_annual':
            third_of_hours_count_sem1 + third_of_hours_count_sem2,
        })
        return context
Example #3
0
def generate_next_year_academic_programs():
    current_academic_year = get_current_academic_calendar().academic_year
    academic_programs = list(
        AcademicProgram.objects.filter(
            academic_year=current_academic_year -
            1).prefetch_related('program_subjects_through'))

    academic_programs_to_clone = []
    for academic_program in academic_programs:
        academic_programs_to_clone.append(
            clone_object_and_override_fields(
                academic_program,
                save=False,
                academic_year=current_academic_year,
                classes_count=0,
                students_at_risk_count=0,
                avg_sem1=None,
                avg_sem2=None,
                avg_annual=None,
                unfounded_abs_avg_sem1=None,
                unfounded_abs_avg_sem2=None,
                unfounded_abs_avg_annual=None))
    cloned_programs = AcademicProgram.objects.bulk_create(
        academic_programs_to_clone, batch_size=100)

    program_subjects_through_to_clone = []
    for index, academic_program in enumerate(academic_programs):
        for subject_through in academic_program.program_subjects_through.all():
            program_subjects_through_to_clone.append(
                clone_object_and_override_fields(
                    subject_through,
                    save=False,
                    academic_program=cloned_programs[index]))
    ProgramSubjectThrough.objects.bulk_create(
        program_subjects_through_to_clone, batch_size=100)
    def has_assigned_study_classes(teacher):
        current_academic_calendar = get_current_academic_calendar()
        if current_academic_calendar is None:
            return False

        return teacher.teacher_class_through.filter(
            academic_year=current_academic_calendar.academic_year).exists()
Example #5
0
def create_students_at_risk_counts_for_school_unit_task(school_unit_id):
    try:
        school_unit = RegisteredSchoolUnit.objects.get(id=school_unit_id)
    except ObjectDoesNotExist:
        return

    current_calendar = get_current_academic_calendar()
    if not current_calendar:
        return

    months_since_academic_year_start = get_months_since_academic_calendar_start(
        current_calendar)
    objects_to_create = []
    for year, month in months_since_academic_year_start:
        days_in_month = monthrange(year, month)[1]
        if not StudentAtRiskCounts.objects.filter(
                month=month, year=year, school_unit=school_unit).exists():
            objects_to_create.append(
                StudentAtRiskCounts(month=month,
                                    year=year,
                                    school_unit=school_unit,
                                    daily_counts=[{
                                        'count':
                                        0,
                                        'day':
                                        day,
                                        'weekday':
                                        WEEKDAYS_MAP[datetime.datetime(
                                            year, month, day).weekday()]
                                    } for day in range(1, days_in_month + 1)]))
    StudentAtRiskCounts.objects.bulk_create(objects_to_create)
def send_alerts_for_school_situation():
    today = timezone.now().date()
    two_weeks_ago = today - timezone.timedelta(days=14)
    one_week_ago = today - timezone.timedelta(days=8)
    time_period = get_time_period(two_weeks_ago, one_week_ago)

    current_calendar = get_current_academic_calendar()
    if not current_calendar:
        return
    second_semester_end_events = get_second_semester_end_events(
        current_calendar)

    for school_unit in RegisteredSchoolUnit.objects.all():
        is_technological_school = has_technological_category(school_unit)

        for student in UserProfile.objects.filter(
                school_unit_id=school_unit.id,
                user_role=UserProfile.UserRoles.STUDENT,
                is_active=True).select_related('student_in_class'):
            try:
                current_semester = get_current_semester(
                    today, current_calendar, second_semester_end_events,
                    student.student_in_class.class_grade_arabic,
                    is_technological_school)
                if current_semester is None:
                    continue

                unfounded_absences_count = get_unfounded_absences_count_for_student(
                    student.id, current_calendar.academic_year,
                    current_semester)
                grades = get_grades_for_students(student.id, two_weeks_ago,
                                                 one_week_ago)

                if unfounded_absences_count == 0 and grades.count() == 0:
                    continue

                parents_with_emails, parents_with_phone_numbers = get_parents_contact(
                    student)
                grouped_grades = group_grades_by_subject(grades)

                if parents_with_emails:
                    formatted_grades_for_email = get_formatted_grades(
                        grouped_grades)
                    format_and_send_school_situation_email(
                        student.full_name, time_period,
                        formatted_grades_for_email, unfounded_absences_count,
                        school_unit.name, parents_with_emails)
                if parents_with_phone_numbers:
                    student_initials = get_student_initials(student.full_name)
                    formatted_grades_for_sms = get_formatted_grades(
                        grouped_grades, True)
                    format_and_send_school_situation_sms(
                        student_initials, time_period,
                        formatted_grades_for_sms, unfounded_absences_count,
                        parents_with_phone_numbers)
            except Exception:
                print("Couldn't send alert for student: {}".format(
                    student.full_name))

    print('Finished task send_alerts_for_school_situation')
    def get_object(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        return get_object_or_404(StudentCatalogPerYear,
                                 student__id=self.request.user.user_profile.id,
                                 academic_year=current_calendar.academic_year)
Example #8
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return AcademicProgram.objects.none()

        return AcademicProgram.objects.filter(
            school_unit_id=self.request.user.user_profile.school_unit_id,
            academic_year=current_calendar.academic_year,
            students_at_risk_count__gt=0).order_by('-students_at_risk_count',
                                                   Lower('name'))
def create_stats_for_schools(apps, schema_editor):
    RegisteredSchoolUnit = apps.get_model('schools', 'RegisteredSchoolUnit')
    SchoolUnitStats = apps.get_model('statistics', 'SchoolUnitStats')

    calendar = get_current_academic_calendar()
    if calendar:
        for school in RegisteredSchoolUnit.objects.all():
            SchoolUnitStats.objects.create(
                school_unit=school,
                school_unit_name=school.name,
                academic_year=calendar.academic_year)
    def get_object(self):
        parent = self.request.user.user_profile
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        return get_object_or_404(StudentCatalogPerYear,
                                 student__id=self.kwargs['id'],
                                 student__parents__id=parent.id,
                                 student__school_unit_id=parent.school_unit_id,
                                 academic_year=current_calendar.academic_year)
Example #11
0
    def check_if_operation_allowed(self,
                                   operation_name,
                                   check_date_limit=True):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return _('No academic calendar defined.')

        study_class = self.get_object()
        if study_class.academic_year != current_calendar.academic_year:
            return _('Cannot {} a study class from a previous year.').format(
                operation_name)
Example #12
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return SchoolUnitStats.objects.none()

        order_by = 'unfounded_abs_avg_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else 'unfounded_abs_avg_annual'

        return SchoolUnitStats.objects.filter(
            academic_year=current_calendar.academic_year).order_by(
                F(order_by).desc(nulls_last=True), Lower('school_unit_name'))
Example #13
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return AcademicProgram.objects.none()

        order_by = 'unfounded_abs_avg_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else 'unfounded_abs_avg_annual'

        return AcademicProgram.objects.filter(
            school_unit_id=self.request.user.user_profile.school_unit_id,
            academic_year=current_calendar.academic_year).order_by(
                F(order_by).desc(nulls_last=True), Lower('name'))
Example #14
0
 def __init__(self, file, study_class, subject, current_calendar):
     self.report = {'errors': {}}
     self.file = file
     self.study_class = study_class
     self.subject = subject
     self.today = timezone.now().date()
     self.current_calendar = current_calendar or get_current_academic_calendar(
     )
     self.reverse_field_mapping = {
         value: field
         for field, value in self.field_mapping.items()
     }
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return StudyClass.objects.none()

        order_by = 'avg_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else 'avg_annual'

        return StudyClass.objects.filter(
            school_unit_id=self.request.user.user_profile.school_unit_id,
            academic_year=current_calendar.academic_year).order_by(
                F(order_by).desc(nulls_last=True), 'class_grade_arabic',
                'class_letter')
    def get_queryset(self):
        try:
            current_academic_year = get_current_academic_calendar().academic_year
        except AttributeError:
            return GenericAcademicProgram.objects.none()

        principal_school = self.request.user.user_profile.school_unit
        exclude_ids = AcademicProgram.objects.filter(academic_year=current_academic_year,
                                                     school_unit=principal_school) \
            .values_list('generic_academic_program_id', flat=True)

        return GenericAcademicProgram.objects.filter(academic_profile=principal_school.academic_profile,
                                                     category_id__in=principal_school.categories.values_list('id', flat=True)) \
            .exclude(id__in=exclude_ids)
Example #17
0
def validate_and_get_semester(taken_at):
    today = timezone.now().date()
    current_calendar = get_current_academic_calendar()

    if taken_at > today:
        raise serializers.ValidationError(
            {'taken_at': _('The date cannot be in the future.')})

    second_sem_start = current_calendar.second_semester.starts_at
    if taken_at < second_sem_start <= today:
        raise serializers.ValidationError(
            {'taken_at': _('The date cannot be in the first semester.')})

    return 2 if today >= second_sem_start else 1
Example #18
0
def create_students_at_risk_counts_task():
    today = timezone.now().date()
    days_in_month = monthrange(today.year, today.month)[1]
    current_calendar = get_current_academic_calendar()
    if not current_calendar:
        return

    for school_unit in RegisteredSchoolUnit.objects.all():
        StudentAtRiskCounts.objects.create(
            month=today.month,
            year=today.year,
            school_unit=school_unit,
            daily_counts=[{
                'day':
                day,
                'weekday':
                WEEKDAYS_MAP[datetime.datetime(today.year, today.month,
                                               day).weekday()],
                'count':
                0
            } for day in range(1, days_in_month + 1)])
        for study_class in school_unit.study_classes.filter(
                academic_year=current_calendar.academic_year):
            StudentAtRiskCounts.objects.create(
                month=today.month,
                year=today.year,
                study_class=study_class,
                daily_counts=[{
                    'day':
                    day,
                    'weekday':
                    WEEKDAYS_MAP[datetime.datetime(today.year, today.month,
                                                   day).weekday()],
                    'count':
                    0
                } for day in range(1, days_in_month + 1)])

    StudentAtRiskCounts.objects.create(
        month=today.month,
        year=today.year,
        by_country=True,
        daily_counts=[{
            'day':
            day,
            'weekday':
            WEEKDAYS_MAP[datetime.datetime(today.year, today.month,
                                           day).weekday()],
            'count':
            0
        } for day in range(1, days_in_month + 1)])
Example #19
0
    def get(self, request, *args, **kwargs):
        today = timezone.now().date()
        month = self.request.GET.get('month')
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        if not month or not (month.isnumeric() and 1 <= int(month) <= 12):
            month = today.month

        filters = {}
        profile = self.request.user.user_profile
        if profile.user_role == UserProfile.UserRoles.ADMINISTRATOR:
            school_unit = self.request.GET.get('school_unit')
            filters.update({'school_unit_id': school_unit} if school_unit and
                           school_unit.isnumeric() else {'by_country': True})

        elif profile.user_role == UserProfile.UserRoles.PRINCIPAL:
            filters.update({'school_unit_id': profile.school_unit_id})

        elif profile.user_role == UserProfile.UserRoles.TEACHER:
            current_calendar = get_current_academic_calendar()
            if current_calendar:
                filters.update({
                    'study_class__class_master_id':
                    profile.id,
                    'study_class__academic_year':
                    current_calendar.academic_year
                })

        stats = StudentAtRiskCounts.objects.filter(
            year=today.year
            if int(month) <= today.month else current_calendar.academic_year,
            month=month,
            **filters).first()
        return Response(stats.daily_counts if stats else [])
Example #20
0
    def get_queryset(self):
        teacher = self.request.user.user_profile
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        current_mastering_class = get_object_or_404(
            teacher.mastering_study_classes,
            academic_year=current_calendar.academic_year)

        return UserProfile.objects.filter(
            child__student_in_class=current_mastering_class,
            user_role=UserProfile.UserRoles.PARENT,
            school_unit_id=teacher.school_unit_id,
            last_online__lt=timezone.now() - timedelta(days=30)).order_by(
                '-last_online', Lower('full_name'))
Example #21
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        mastering_study_class = get_object_or_404(
            self.request.user.user_profile.mastering_study_classes,
            academic_year=current_calendar.academic_year)

        order_by = self.get_order_by(current_calendar)

        return mastering_study_class.student_catalogs_per_year \
            .select_related('student') \
            .order_by(
                F(order_by).desc(nulls_last=True),
                Lower('student__full_name'))
Example #22
0
    def create(self, validated_data):
        instance = super().create(validated_data)
        instance.school_principal.school_unit = instance
        instance.school_principal.save()

        current_calendar = get_current_academic_calendar()
        if current_calendar:
            SchoolUnitStats.objects.create(
                school_unit=instance,
                school_unit_name=instance.name,
                academic_year=current_calendar.academic_year)

        update_school_unit_enrollment_stats_task.delay()
        create_students_at_risk_counts_for_school_unit_task.delay(instance.id)

        return instance
Example #23
0
    def get(self, request, *args, **kwargs):
        today = timezone.now().date()
        month = self.request.GET.get('month')
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        if not month or not (month.isnumeric() and 1 <= int(month) <= 12):
            month = today.month

        enrollment_stats = get_object_or_404(
            SchoolUnitEnrollmentStats,
            year=today.year
            if int(month) <= today.month else current_calendar.academic_year,
            month=month)
        return Response(enrollment_stats.daily_statistics)
Example #24
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return StudentCatalogPerYear.objects.none()

        order_by1 = '-unfounded_abs_count_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else '-unfounded_abs_count_annual'
        order_by2 = '-behavior_grade_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else '-behavior_grade_annual'

        profile = self.request.user.user_profile
        return StudentCatalogPerYear.objects.select_related('student', 'study_class') \
            .filter(student__school_unit_id=profile.school_unit_id,
                    academic_year=current_calendar.academic_year,
                    student__is_at_risk=True) \
            .distinct() \
            .order_by(order_by1, order_by2, Lower('student__full_name'))
Example #25
0
    def get_queryset(self):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            raise Http404()

        mastering_study_class = get_object_or_404(
            self.request.user.user_profile.mastering_study_classes,
            academic_year=current_calendar.academic_year)

        order_by1 = '-unfounded_abs_count_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else '-unfounded_abs_count_annual'
        order_by2 = '-behavior_grade_sem1' if timezone.now().date(
        ) < current_calendar.second_semester.ends_at else '-behavior_grade_annual'

        return mastering_study_class.student_catalogs_per_year.select_related('student') \
            .filter(student__is_at_risk=True) \
            .order_by(order_by1, order_by2, Lower('student__full_name'))
Example #26
0
def get_current_school_cycle(study_class):
    calendar = get_current_academic_calendar()
    semester = 2 if calendar and timezone.now().date(
    ) >= calendar.second_semester.starts_at else 1
    if semester == 1:
        current_cycle = [
            grade for grade in get_school_cycle_for_class_grade(
                study_class.class_grade_arabic)
            if grade < study_class.class_grade_arabic
        ]
    else:
        current_cycle = [
            grade for grade in get_school_cycle_for_class_grade(
                study_class.class_grade_arabic)
            if grade <= study_class.class_grade_arabic
        ]

    return current_cycle
Example #27
0
    def get_catalogs_per_subjects(self, obj):
        calendar = get_current_academic_calendar()
        study_class = self.context['study_class']
        is_technological_school = has_technological_category(study_class.school_unit)

        self.context.update({
            'working_weeks_count_sem1': get_working_weeks_count(calendar, 1, study_class, is_technological_school),
            'working_weeks_count_sem2': get_working_weeks_count(calendar, 2, study_class, is_technological_school)
        })

        return StudentCatalogPerSubjectWithTeacherSerializer(
            instance=obj.student_catalogs_per_subject.filter(study_class=study_class, is_enrolled=True)
                .select_related('teacher', 'study_class__school_unit__academic_profile', 'study_class__academic_program')
                .prefetch_related('grades', 'absences', 'examination_grades')
                .order_by('-is_coordination_subject', Lower('subject_name')),
            many=True,
            context=self.context
        ).data
    def get_absences_report_for_student(self, student):
        current_calendar = get_current_academic_calendar()
        if not current_calendar:
            return []

        now = timezone.now()
        month = self.get_month_parameter() or now.month
        if month < current_calendar.first_semester.starts_at.month:
            year = now.year
        else:
            year = current_calendar.academic_year

        absences = student.absences.filter(taken_at__year=year,
                                           taken_at__month=month,
                                           created__lt=now -
                                           timezone.timedelta(hours=2))

        month_range = calendar.monthrange(year, month)
        weekday = month_range[0]
        last_day_of_month = month_range[1]
        absences_map_by_month_days = self.map_absences_count_to_month_days(
            absences, last_day_of_month)
        filter_by_category = True if self.request.query_params.get(
            'by_category') == 'true' else False

        results = []
        for day in range(1, last_day_of_month + 1):
            result = {
                'day': day,
                'weekday': WEEKDAYS_MAP[weekday % 7],
            }
            if filter_by_category:
                result['founded_count'] = absences_map_by_month_days[day][
                    'founded_count']
                result['unfounded_count'] = absences_map_by_month_days[day][
                    'unfounded_count']
            else:
                result['total_count'] = absences_map_by_month_days[day]['founded_count'] + \
                                        absences_map_by_month_days[day]['unfounded_count']

            results.append(result)
            weekday += 1

        return results
Example #29
0
    def to_representation(self, instance):
        study_class = self.context['study_class']
        subject = self.context['subject']
        calendar = get_current_academic_calendar()

        is_technological_school = has_technological_category(
            study_class.school_unit)
        working_weeks_count_sem1 = get_working_weeks_count(
            calendar, 1, study_class, is_technological_school)
        working_weeks_count_sem2 = get_working_weeks_count(
            calendar, 2, study_class, is_technological_school)
        weekly_hours_count = get_weekly_hours_count(study_class, subject.id)

        third_of_hours_count_sem1 = (working_weeks_count_sem1 *
                                     weekly_hours_count) // 3
        third_of_hours_count_sem2 = (working_weeks_count_sem2 *
                                     weekly_hours_count) // 3

        self.context.update({
            'avg_limit':
            get_avg_limit_for_subject(study_class, subject.is_coordination,
                                      subject.id),
            'third_of_hours_count_sem1':
            third_of_hours_count_sem1,
            'third_of_hours_count_sem2':
            third_of_hours_count_sem2,
            'third_of_hours_count_annual':
            third_of_hours_count_sem1 + third_of_hours_count_sem2,
        })

        catalogs = StudentCatalogPerSubjectSerializer(
            instance=StudentCatalogPerSubject.objects.filter(
                study_class_id=self.context['study_class'].id,
                subject_id=self.context['subject'].id,
                teacher_id=self.context['request'].user.user_profile.id,
                is_enrolled=True).prefetch_related(
                    'grades', 'absences', 'examination_grades').order_by(
                        Lower('student__full_name')),
            many=True,
            context=self.context).data

        # Convert response data to dictionary
        return {"catalogs": catalogs}
Example #30
0
def can_update_examination_grades(study_class, grade_type):
    current_calendar = get_current_academic_calendar()
    if current_calendar is None:
        return False

    if grade_type == ExaminationGrade.GradeTypes.SECOND_EXAMINATION and study_class.academic_year != current_calendar.academic_year:
        return False

    event_type = SchoolEvent.EventTypes.CORIGENTE if grade_type == ExaminationGrade.GradeTypes.SECOND_EXAMINATION \
        else SchoolEvent.EventTypes.DIFERENTE
    event = current_calendar.school_events.filter(
        event_type=event_type).first()

    if event is None:
        return False

    if not event.starts_at <= timezone.now().date() <= event.ends_at:
        return False

    return True