import datetime import os from typing import List from PyPDF2 import PdfFileMerger from django.contrib.auth.decorators import login_required, permission_required from django.http import Http404, FileResponse from django.shortcuts import render, redirect, get_object_or_404 from django.utils import timezone from django.views.decorators.cache import cache_page from dashboard.caches import SUBS_VIEW_CACHE, MY_PLAN_VIEW_CACHE, PLAN_VIEW_CACHE from debug.models import register_traceback, register_return_0 from schoolapps.settings import BASE_DIR from schoolapps.settings import SHORT_WEEK_DAYS, LONG_WEEK_DAYS from timetable.filters import HintFilter from timetable.forms import HintForm from timetable.hints import get_all_hints_by_time_period, get_all_hints_by_class_and_time_period, \ get_all_hints_for_teachers_by_time_period, get_all_hints_not_for_teachers_by_time_period from timetable.pdf import generate_class_tex, generate_pdf from untisconnect.api import * from untisconnect.datetimeutils import get_calendar_week, get_calendar_weeks, get_next_weekday, find_out_what_is_today, \ get_next_weekday_with_time from untisconnect.events import get_all_events_by_date from untisconnect.plan import get_plan, parse_lesson_times from untisconnect.sub import SubRow from untisconnect.sub import get_substitutions_by_date, generate_sub_table, get_header_information from untisconnect.utils import get_type_and_object_of_user, overview_dict from .models import Hint ############# # OVERVIEWS # ############# @login_required @permission_required("timetable.show_plan") def all(request): """ [DJANGO VIEW] Show all plans as collection :param request: Django request :return: rendered template """ context = overview_dict() return render(request, 'timetable/all.html', context) @login_required @permission_required("timetable.show_plan") def quicklaunch(request): """ [DJANGO VIEW] Show all plans as buttons :param request: Django request :return: rendered template """ context = overview_dict() return render(request, 'timetable/quicklaunch.html', context) ######### # PLANS # ######### @login_required @permission_required("timetable.show_plan") @cache_page(PLAN_VIEW_CACHE.expiration_time) def plan(request, plan_type, plan_id, regular="", year=None, calendar_week=None): """ [DJANGO VIEW] Show a timetable (class, teacher, room, smart/regular) :param request: Django requests :param plan_type: "teacher", "class" or "room" :param plan_id: UNTIS-ID of corresponding object :param regular: regular plan = True, smart plan = False :param year: year of plan (only for smart plan) :param calendar_week: calendar week in year (only for smart plan) :return: """ if year is None or calendar_week is None: date = get_next_weekday_with_time(timezone.datetime.now(), timezone.datetime.now().time()) year = date.year calendar_week = date.isocalendar()[1] # Regular or smart plan? if regular == "regular": smart = False else: smart = True # Get monday and friday of week monday = get_calendar_week(calendar_week, year)["first_day"] friday = monday + datetime.timedelta(days=4) # Init hints hints = None hints_b = None if plan_type == 'teacher': # Teacher _type = TYPE_TEACHER el = get_teacher_by_id(plan_id) # Get hints if smart: hints = list(get_all_hints_for_teachers_by_time_period(monday, friday)) hints_b = list(get_all_hints_not_for_teachers_by_time_period(monday, friday)) elif plan_type == 'class': # Class _type = TYPE_CLASS el = get_class_by_id(plan_id) # Get hints if smart: hints = list(get_all_hints_by_class_and_time_period(el, monday, friday)) elif plan_type == 'room': # Room _type = TYPE_ROOM el = get_room_by_id(plan_id) else: raise Http404('Plan not found.') # Get plan plan, holidays = get_plan(_type, plan_id, smart=smart, monday_of_week=monday) context = { "smart": smart, "type": _type, "raw_type": plan_type, "id": plan_id, "plan": plan, "el": el, "times": parse_lesson_times(), "weeks": get_calendar_weeks(year=year), "selected_week": calendar_week, "selected_year": year, "short_week_days": zip(SHORT_WEEK_DAYS, holidays), "long_week_days": zip(LONG_WEEK_DAYS, holidays), "holidays": holidays, "hints": hints, "hints_b": hints_b, "hints_b_mode": "week", } return render(request, 'timetable/plan.html', context) @login_required @permission_required("timetable.show_plan") @cache_page(MY_PLAN_VIEW_CACHE.expiration_time) def my_plan(request, year=None, month=None, day=None): date, time = find_out_what_is_today(year, month, day) # Get next weekday if it is a weekend next_weekday = get_next_weekday_with_time(date, time) if next_weekday != date: return redirect("timetable_my_plan", next_weekday.year, next_weekday.month, next_weekday.day) # Get calendar week and monday of week calendar_week = date.isocalendar()[1] monday_of_week = get_calendar_week(calendar_week, date.year)["first_day"] # Get user type (student, teacher, etc.) _type, el = get_type_and_object_of_user(request.user) if _type == TYPE_TEACHER: # Teacher plan_id = el.id raw_type = "teacher" # Get hints hints = list(get_all_hints_for_teachers_by_time_period(date, date)) hints_b = list(get_all_hints_not_for_teachers_by_time_period(date, date)) elif _type == TYPE_CLASS: # Student plan_id = el.id raw_type = "class" # Get hints hints = list(get_all_hints_by_class_and_time_period(el, date, date)) hints_b = None else: # No student or teacher > no my plan return redirect("timetable_admin_all") # Get plan plan, holidays = get_plan(_type, plan_id, smart=True, monday_of_week=monday_of_week) # print(parse_lesson_times()) holiday_for_the_day = holidays[date.isoweekday() - 1] # print(holiday_for_the_day) context = { "type": _type, "raw_type": raw_type, "id": plan_id, "plan": plan, "el": el, "times": parse_lesson_times(), "week_day": date.isoweekday() - 1, "date": date, "date_js": int(date.timestamp()) * 1000, "display_date_only": True, "holiday": holiday_for_the_day, "hints": hints, "hints_b": hints_b, "hints_b_mode": "day", } return render(request, 'timetable/myplan.html', context) ################# # SUBSTITUTIONS # ################# # TODO: Move to own helper file later def equal(sub_row_1: SubRow, sub_row_2: SubRow) -> bool: """ Checks the equality of two sub rows :param sub_row_1: SubRow 1 :param sub_row_2: SubRow 2 :return: Equality """ return sub_row_1.classes == sub_row_2.classes and sub_row_1.sub and sub_row_2.sub and \ sub_row_1.sub.teacher_old == sub_row_2.sub.teacher_old and \ sub_row_1.sub.teacher_new == sub_row_2.sub.teacher_new and \ sub_row_1.sub.subject_old == sub_row_2.sub.subject_old and \ sub_row_1.sub.subject_new == sub_row_2.sub.subject_new and \ sub_row_1.sub.room_old == sub_row_2.sub.room_old and \ sub_row_1.sub.room_new == sub_row_2.sub.room_new and \ sub_row_1.sub.text == sub_row_2.sub.text def merge_sub_rows(sub_table: List[SubRow]) -> List[SubRow]: """ Merge equal sub rows with different lesson numbers to one :param sub_table: :return: """ new_sub_table = [] i = 0 while i < len(sub_table) - 1: j = 1 while equal(sub_table[i], sub_table[i + j]): j += 1 if i + j > len(sub_table) - 1: break if j > 1: new_sub_row = sub_table[i] new_sub_row.lesson = sub_table[i].lesson + '-' + sub_table[i + j - 1].lesson new_sub_table.append(new_sub_row) else: new_sub_table.append(sub_table[i]) # get last item if i == len(sub_table) - 2: new_sub_table.append(sub_table[i + 1]) break i += j return new_sub_table def sub_pdf(request, plan_date=None): """Show substitutions as PDF for the next weekday (specially for monitors)""" if plan_date: splitted_date = [int(i) for i in plan_date.split("-")] today = timezone.datetime(year=splitted_date[0], month=splitted_date[1], day=splitted_date[2]) else: today = timezone.datetime.now() # Get the next weekday # today = parse_datetime(date) print("Today is:", today) first_day = get_next_weekday_with_time(today, today.time()) second_day = get_next_weekday(first_day + datetime.timedelta(days=1)) # Get subs and generate table for i, date in enumerate([first_day, second_day]): # Get subs and generate table events = get_all_events_by_date(date) subs = get_substitutions_by_date(date) sub_table = generate_sub_table(subs, events) sub_table = merge_sub_rows(sub_table) # Get header information and hints header_info = get_header_information(subs, date, events) hints = list(get_all_hints_by_time_period(date, date)) # latex = convert_markdown_2_latex(hints[0].text) # print(latex) # Generate LaTeX tex = generate_class_tex(sub_table, date, header_info, hints) # Generate PDF generate_pdf(tex, "aktuell{}".format(i)) # Merge PDFs try: merger = PdfFileMerger() class0 = open(os.path.join(BASE_DIR, "latex", "aktuell0.pdf"), "rb") class1 = open(os.path.join(BASE_DIR, "latex", "aktuell1.pdf"), "rb") merger.append(fileobj=class0) merger.append(fileobj=class1) # Write merged PDF to aktuell.pdf output = open(os.path.join(BASE_DIR, "latex", "aktuell.pdf"), "wb") merger.write(output) output.close() # Register successful merge in debugging tool register_return_0("merge_class", "pypdf2") except Exception: # Register exception in debugging tool register_traceback("merge_class", "pypdf2") # Read and response PDF file = open(os.path.join(BASE_DIR, "latex", "aktuell.pdf"), "rb") return FileResponse(file, content_type="application/pdf") @login_required @permission_required("timetable.show_plan") @cache_page(SUBS_VIEW_CACHE.expiration_time) def substitutions(request, year=None, month=None, day=None): """Show substitutions in a classic view""" date, time = find_out_what_is_today(year, month, day) # Get next weekday if it is a weekend next_weekday = get_next_weekday_with_time(date, time) if next_weekday != date: return redirect("timetable_substitutions_date", next_weekday.year, next_weekday.month, next_weekday.day) # Get subs and generate table events = get_all_events_by_date(date) subs = get_substitutions_by_date(date) sub_table = generate_sub_table(subs, events) # Merge Subs sub_table = merge_sub_rows(sub_table) # Get header information and hints header_info = get_header_information(subs, date, events) hints = list(get_all_hints_by_time_period(date, date)) context = { "subs": subs, "sub_table": sub_table, "date": date, "date_js": int(date.timestamp()) * 1000, "header_info": header_info, "hints": hints, } return render(request, 'timetable/substitution.html', context) ################### # HINT MANAGEMENT # ################### @login_required @permission_required("timetable.can_view_hint") def hints(request): f = HintFilter(request.GET, queryset=Hint.objects.all()) msg = None if request.session.get("msg", False): msg = request.session["msg"] request.session["msg"] = None return render(request, "timetable/hints.html", {"f": f, "msg": msg}) @login_required @permission_required('timetable.can_add_hint') def add_hint(request): msg = None if request.method == 'POST': form = HintForm(request.POST) if form.is_valid(): i = form.save() i.save() form = HintForm() msg = "success" else: form = HintForm() return render(request, 'timetable/hintform.html', {'form': form, "martor": True, "msg": msg, "mode": "new"}) @login_required @permission_required("timetable.can_edit_hint") def edit_hint(request, id): hint = get_object_or_404(Hint, pk=id) if request.method == 'POST': form = HintForm(request.POST, instance=hint) if form.is_valid(): i = form.save() i.save() request.session["msg"] = "success_edit" return redirect('timetable_hints') else: form = HintForm(instance=hint) return render(request, 'timetable/hintform.html', {'form': form, "martor": True, "mode": "edit"}) @login_required @permission_required("timetable.can_delete_hint") def delete_hint(request, id): hint = get_object_or_404(Hint, pk=id) hint.delete() request.session["msg"] = "success_delete" return redirect('timetable_hints')