diff --git a/.gitignore b/.gitignore index 32467d7dd1e7870d88336bb86dd53d29825095ee..75ae30bd2c43b1179aa62e408901ba973bfeb029 100755 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ env/ venv/ *.pyc secure* +*.aux +*.log +class.pdf +class.tex .idea/ diff --git a/schoolapps/latex/.keep b/schoolapps/latex/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/schoolapps/static/common/helper.js b/schoolapps/static/common/helper.js new file mode 100644 index 0000000000000000000000000000000000000000..e0802213c9f99c29a0f9f6b564f7a57eba7c6e00 --- /dev/null +++ b/schoolapps/static/common/helper.js @@ -0,0 +1,65 @@ +function formatDate(date) { + return date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear(); +} + + +function addZeros(i) { + if (i < 10) { + return "0" + i; + } else { + return "" + i; + } +} + +function formatDateForDjango(date) { + return "" + date.getFullYear() + "/" + addZeros(date.getMonth() + 1) + "/" + addZeros(date.getDate()) + "/"; + +} + +function getNow() { + return new Date(); +} + +function getNowFormatted() { + return formatDate(getNow()); +} + + +$(document).ready(function () { + // Initialize sidenav [MAT] + $(".sidenav").sidenav(); + + // Initialize datepicker [MAT] + $('.datepicker').datepicker({ + format: 'dd.mm.yyyy', + // Translate to German + i18n: { + months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], + weekdaysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], + weekdaysAbbrev: ['S', 'M', 'D', 'M', 'D', 'F', 'S'], + + // Buttons + today: 'Heute', + clear: 'Löschen', + done: 'OK', + }, + + // Set monday as first day of week + firstDay: 1, + autoClose: true + }); + + // Initialize timepicker [MAT] + $('.timepicker').timepicker({ + twelveHour: false, + autoClose: true, + cancelText: 'Abbrechen', + clearText: 'Löschen', + doneText: 'OK' + }); + + //Initialize tooltip [MAT] + $('.tooltipped').tooltip(); +}); \ No newline at end of file diff --git a/schoolapps/static/common/style.css b/schoolapps/static/common/style.css index e4f6d35ee626bd8f541a233c3e5fb533a1545c5a..9e28771820a373b2d2291a7b1b52a2585eb2267c 100755 --- a/schoolapps/static/common/style.css +++ b/schoolapps/static/common/style.css @@ -144,6 +144,10 @@ span.badge.new::after { width: 100%; } +table.substitutions td, table.substitutions th { + padding: 10px 5px; +} + /*.timetable-time {*/ /*margin-right: 20px;*/ /*}*/ @@ -173,4 +177,8 @@ span.badge.new::after { .btn-timetable-quicklaunch { margin: 1%; width: 30%; +} + +table.striped > tbody > tr:nth-child(odd) { + background-color: rgba(208, 208, 208, 0.5); } \ No newline at end of file diff --git a/schoolapps/templates/partials/header.html b/schoolapps/templates/partials/header.html index 5813c9d0e5189f2f3b7fa6bde8fc465b177de9bd..b020e0c1eaea1e81f8712ef4197d518d64d159ce 100755 --- a/schoolapps/templates/partials/header.html +++ b/schoolapps/templates/partials/header.html @@ -26,7 +26,8 @@ <!----------------> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="{% static 'materialize/dist/js/materialize.min.js' %}"></script> - <script> + <script src="{% static 'common/helper.js' %}"></script> + <!--<script> $(document).ready(function () { // Initialize sidenav [MAT] $(".sidenav").sidenav(); @@ -65,7 +66,7 @@ //Initialize tooltip [MAT] $('.tooltipped').tooltip(); }); - </script> + </script>--> <script> //set AUB-Form function set_time(lesson_field) { @@ -98,7 +99,6 @@ <!-- django-material --> {% include 'material/includes/material_js.html' %} - </head> <body> <header> diff --git a/schoolapps/timetable/pdf.py b/schoolapps/timetable/pdf.py new file mode 100644 index 0000000000000000000000000000000000000000..811fc9f7977234c71f0f86a9cdad0030c91d610a --- /dev/null +++ b/schoolapps/timetable/pdf.py @@ -0,0 +1,169 @@ +import os +import subprocess + +from django.utils import timezone +from django.utils import formats + +# LaTeX constants + +TEX_HEADER = """\\documentclass[11pt]{article} +\\usepackage[ngerman]{babel} +\\usepackage[utf8]{inputenc} +\\usepackage[a4paper,left=1cm,right=1cm,top=2cm,bottom=2cm,bindingoffset=0mm]{geometry} + +\\usepackage{fancyhdr} +\\usepackage{graphicx} + +\\usepackage{longtable} +\\usepackage{multirow} +\\usepackage{color, colortbl} + +\\usepackage{geometry} + +\\usepackage{ulem, xpatch} +\\xpatchcmd{\\sout} + {\\bgroup} + {\\bgroup\def\\ULthickness{1.5pt}} + {}{} + +\\usepackage[framemethod=tikz]{mdframed} +\\newmdenv[ + roundcorner=5pt, + backgroundcolor=green, + linecolor=green, + skipabove=0pt, + skipbelow=0pt, + leftmargin=0pt, + rightmargin=0pt +]{badges} + +\\usepackage{tcolorbox} +\\newtcbox{\\badge}{nobeforeafter,colframe=green,colback=green,boxrule=0.5pt,arc=4pt, + boxsep=0pt,left=5pt,right=5pt,top=5pt,bottom=5pt,tcbox raise base, + grow to left by=0pt, + grow to right by=-3pt, + enlarge top by=3pt, + enlarge bottom by=3pt,coltext=white} + + +\\usepackage{helvet} %Helvetica als Standardschriftart +\\renewcommand{\\familydefault}{\\sfdefault} %Helvetica als Standardschriftart + +\\definecolor{grey}{rgb}{0.95,0.95,0.95} +\\definecolor{darkgrey}{rgb}{0.6,0.6,0.6} +\\definecolor{white}{rgb}{1,1,1} +\\definecolor{green}{RGB}{76,175,80} + +\\pagestyle{fancy} +%\\renewcommand{\\sectionmark}[1]{#1} +%\\lhead{\\rightmark} +\\lhead{\\includegraphics[width=5cm]{static/common/logo.png}} +\\lfoot{Katharineum zu Lübeck} +\\cfoot{\\thepage} +\\rfoot{\\small Umsetzung: © 2018 by Computer-AG} + +\\begin{document}""" + +TEX_FOOTER = '\end{document}' + +TEX_TABLE_HEADER_CLASS = """ +\def\\arraystretch{1.5} +\\begin{longtable}{|p{20mm}|p{10mm}|p{32mm}|p{25mm}|p{30mm}|p{35mm}|} +\\hline\n +\\rowcolor{darkgrey} +\\color{white}\\textbf{Klasse} & +\\color{white}\\textbf{Std.} & +\\color{white}\\textbf{Lehrer} & +\\color{white}\\textbf{Fach} & +\\color{white}\\textbf{Raum} & +\\color{white}\\textbf{Hinweis}\\\\\\hline +""" + +TEX_HEADER_CLASS = """ +\\rhead{\\textbf{Vertretungen %s}\\\\Stand: %s\\\\ } +\\Large +\\subsubsection*{} +\\section*{\\Huge Vertretungen %s} +\n""" + + +def generate_pdf(tex, filename): + """Generate a PDF by LaTeX code""" + + # Read LaTeX file + tex_file = open(os.path.join("latex", filename + ".tex"), "w") + tex_file.write(tex) + tex_file.close() + + # Execute pdflatex to generate the PDF + bash_command = "pdflatex -output-directory latex {}.tex".format(filename) + process = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE) + output = process.communicate()[0] + + +def tex_replacer(s): + """Replace HTML tags by LaTeX tags""" + + # Strong text + s = s.replace("<strong>", "\\textbf{") + s = s.replace("</strong>", "}") + + # Struck out text + s = s.replace("<s>", "\\sout{") + s = s.replace("</s>", "}") + + # Arrow + s = s.replace("→", "$\\rightarrow$") + + return s + + +def generate_class_tex(subs, date): + """Generate LaTeX for a PDF by a substitution table""" + tex_body = "" + + # Format dates + status_date = formats.date_format(date, format="j. F Y, \\K\\W W ") + current_date = formats.date_format(timezone.datetime.now(), format="j. F Y H:i") + head_date = formats.date_format(date, format="l, j. F Y") + + # Generate header with dates + tex_body += TEX_HEADER_CLASS % (status_date, current_date, head_date) + + # Begin table + tex_body += TEX_TABLE_HEADER_CLASS + + color_background = True + for sub in subs: + # Color every second row in grey + if color_background: + tex_body += '\\rowcolor{grey}' + + # Get color tag for row + color = "\color{%s}" % sub.color + + # Print classes + tex_body += color + tex_body += '\\textbf{' + sub.classes + '} & ' + + # Print lesson number, teacher, subject and room + for i in [sub.lesson, sub.teacher, sub.subject, sub.room]: + tex_body += color + tex_body += tex_replacer(i) + ' & ' + + # Print badge (Cancellation) + if sub.badge is not None: + tex_body += """\\large\\badge{%s}""" % sub.badge + + # Print notice and new line + tex_body += color + tex_body += "\\Large\\textit{%s}\\\\\\hline\n" % (sub.text or "") + + # Change background + color_background = not color_background + # End table + tex_body += '\\end{longtable}' + + # Connect header, body and footer + tex_content = TEX_HEADER + tex_body + TEX_FOOTER + return tex_content diff --git a/schoolapps/timetable/templates/timetable/substitution.html b/schoolapps/timetable/templates/timetable/substitution.html index 02e8f2719198462fb91addfe7f92451ce4709be5..6099c0337841892091a00954865f209441100575 100755 --- a/schoolapps/timetable/templates/timetable/substitution.html +++ b/schoolapps/timetable/templates/timetable/substitution.html @@ -1,12 +1,139 @@ {% include 'partials/header.html' %} +<script type="text/javascript"> + function updateDatepicker() { + $("#date").val(formatDate(activeDate)); + } + + function update() { + console.log("Render new."); + + updateDatepicker(); + } + + function loadNew() { + window.location.href = "/timetable/substitutions/" + formatDateForDjango(activeDate); + } + + function onDateBeforeClick() { + activeDate.setDate(activeDate.getDate() - 1); + update(); + loadNew(); + } + + function onDateNextClick() { + activeDate.setDate(activeDate.getDate() + 1); + update(); + loadNew(); + } + + function onDateChanged() { + var str = $("#date").val(); + var split = str.split(".") + activeDate = new Date(split[2], split[1] - 1, split[0]); + update(); + loadNew(); + } + + var activeDate = new Date({{ date_js }}); + + $(document).ready(function () { + $("#date-before").click(onDateBeforeClick); + $("#date-next").click(onDateNextClick); + $("#date").change(onDateChanged); + + // Print the site + $("#print").click(function () { + window.print(); + }); + + update(); + }) +</script> + <main> <h3>Vertretungen</h3> - <p class="flow-text"> - Leider ist der Vertretungsplan noch nicht in Betrieb. Nutzen Sie solange die <a - href="https://info.katharineum.de/aktuell.pdf">alte Version</a>. Vielen Dank! - </p> + <div class="row"> + <div class="col s12 m6 l4 xl3"> + <div class="col s2"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right" id="date-before"> + <i class="material-icons center">navigate_before</i> + </a> + + </div> + <div class="col s8"> + <input type="text" class="datepicker center-align" id="date"> + </div> + <div class="col s2"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium left" id="date-next"> + <i class="material-icons center">navigate_next</i> + </a> + </div> + </div> + <div class="col l4 xl6"> + </div> + <div class="col s12 m6 l4 xl3 right align-right"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right" id="print"> + <i class="material-icons center">print</i> + </a> + </div> + </div> + + <h5>{{ date|date:"l, j. F Y" }}</h5> + + <table class="substitutions striped"> + <thead> + <tr> + <th><i class="material-icons">access_time</i></th> + <th>Klassen</th> + <th>Lehrer</th> + <th>Fach</th> + <th>Raum</th> + <th>Hinweis</th> + <th></th> + </tr> + </thead> + <tbody> + {% for sub in sub_table %} + <tr class="{{ sub.css_class }}"> + <td> + <strong> + {{ sub.lesson }} + </strong> + </td> + <td> + {{ sub.classes }} + </td> + <td> + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ sub.teacher_full|safe }}">{{ sub.teacher|safe }}</span> + </td> + <td> + {{ sub.subject|safe }} + </td> + <td> + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ sub.room_full|safe }}">{{ sub.room|safe }}</span> + </td> + <td> + {% if sub.badge %} + <span class="badge new green hide-on-med-and-up">{{ sub.badge }}</span> + {% endif %} + <em>{{ sub.text|default:"" }}</em> + </td> + <td class="hide-on-small-and-down"> + + {% if sub.badge %} + <span class="badge new green">{{ sub.badge }}</span> + {% endif %} + + </td> + </tr> + {% endfor %} + </tbody> + </table> + </main> {% include 'partials/footer.html' %} diff --git a/schoolapps/timetable/urls.py b/schoolapps/timetable/urls.py index 3b79722e07404fe38765e97a9a13233e2acd46b6..9e7c7dcf253b593405fa9281af470d0bb95c68be 100755 --- a/schoolapps/timetable/urls.py +++ b/schoolapps/timetable/urls.py @@ -3,7 +3,9 @@ from . import views urlpatterns = [ path('', views.all, name='timetable_admin_all'), - path('quick', views.quicklaunch, name='timetable_quicklaunch'), - path('<str:plan_type>/<int:plan_id>', views.plan, name='timetable_plan'), - path('substitutions', views.substitutions, name='timetable_substitutions'), + path('quick/', views.quicklaunch, name='timetable_quicklaunch'), + path('<str:plan_type>/<int:plan_id>/', views.plan, name='timetable_plan'), + path('substitutions/', views.substitutions, name='timetable_substitutions'), + path('substitutions/<int:year>/<int:month>/<int:day>/', views.substitutions, name='timetable_substitutions_date'), + path('class.pdf', views.sub_pdf, name="timetable_substitutions_pdf") ] diff --git a/schoolapps/timetable/views.py b/schoolapps/timetable/views.py index 7d7bed5e419154af8c078b0b896e93f51487c56e..8a9a586cf3a61830ec6d5ec8b3002d7a70628883 100755 --- a/schoolapps/timetable/views.py +++ b/schoolapps/timetable/views.py @@ -1,12 +1,16 @@ import datetime +import os from django.contrib.auth.decorators import login_required, permission_required -from django.http import Http404 +from django.http import Http404, FileResponse from django.shortcuts import render from django.utils import timezone +from timetable.pdf import generate_class_tex, generate_pdf from schoolapps.settings import LESSONS + from untisconnect.parse import * +from untisconnect.sub import get_substitutions_by_date, date_to_untis_date, untis_date_to_date, generate_sub_table try: from schoolapps.untisconnect.api import * @@ -70,7 +74,58 @@ def plan(request, plan_type, plan_id): return render(request, 'timetable/plan.html', context) +def get_next_weekday(date): + """Get the next weekday by a datetime object""" + + if date.isoweekday() in {6, 7}: + if date.isoweekday() == 6: + plus = 2 + else: + plus = 1 + date += datetime.timedelta(days=plus) + return date + + +def sub_pdf(request): + """Show substitutions as PDF for the next weekday (specially for monitors)""" + + # Get the next weekday + today = timezone.datetime.now() + first_day = get_next_weekday(today) + + # Get subs and generate table + subs = get_substitutions_by_date(first_day) + sub_table = generate_sub_table(subs) + + # Generate LaTeX + tex = generate_class_tex(sub_table, first_day) + + # Generate PDF + generate_pdf(tex, "class") + + # Read and response PDF + file = open(os.path.join("latex", "class.pdf"), "rb") + return FileResponse(file, content_type="application/pdf") + + @login_required @permission_required("timetable.show_plan") -def substitutions(request): - return render(request, 'timetable/substitution.html') +def substitutions(request, year=None, day=None, month=None): + """Show substitutions in a classic view""" + + date = timezone.datetime.now() + if year is not None and day is not None and month is not None: + date = timezone.datetime(year=year, month=month, day=day) + + # Get subs and generate table + subs = get_substitutions_by_date(date) + sub_table = generate_sub_table(subs) + + context = { + "subs": subs, + "sub_table": sub_table, + "date": date, + "date_js": int(date.timestamp()) * 1000 + } + + return render(request, 'timetable/substitution.html', context) diff --git a/schoolapps/untisconnect/api.py b/schoolapps/untisconnect/api.py index b1e8c01bbd1575a9dd5498509ff1f5edbf1a3c06..598b00ffcad34eba66707d1acd1c193d114ea13c 100755 --- a/schoolapps/untisconnect/api.py +++ b/schoolapps/untisconnect/api.py @@ -25,8 +25,7 @@ def run_default_filter(obj, filter_term=True): return obj.filter(school_id=SCHOOL_ID, schoolyear_id=SCHOOLYEAR_ID, version_id=VERSION_ID) -def row_by_row(db_ref, obj, filter_term=True): - db_rows = run_all(db_ref.objects, filter_term=filter_term) +def row_by_row_helper(db_rows, obj): out_rows = [] for db_row in db_rows: o = obj() @@ -35,6 +34,11 @@ def row_by_row(db_ref, obj, filter_term=True): return out_rows +def row_by_row(db_ref, obj, filter_term=True): + db_rows = run_all(db_ref.objects, filter_term=filter_term) + return row_by_row_helper(db_rows, obj) + + def one_by_id(db_ref, obj): # print(db_ref) if db_ref != None: @@ -154,6 +158,38 @@ def get_room_by_id(id): return one_by_id(room, Room) +######## +# CORRIDOR # +######## +class Corridor(object): + def __init__(self): + self.filled = False + self.id = None + self.name = None + + def __str__(self): + if self.filled: + return self.name or "Unbekannt" + else: + return "Unbekannt" + + def create(self, db_obj): + self.filled = True + self.id = db_obj.corridor_id + self.name = db_obj.name + + +def get_all_corridors(): + corridors = row_by_row(models.Corridor, Corridor, filter_term=False) + return corridors + + +def get_corridor_by_id(id): + print(id) + corridor = run_one(models.Corridor.objects, filter_term=False).get(corridor_id=id) + return one_by_id(corridor, Corridor) + + ########### # SUBJECT # ########### diff --git a/schoolapps/untisconnect/api_helper.py b/schoolapps/untisconnect/api_helper.py index be7b58107c2cb080025aa6f34427b183581684a0..5ec087772424e2f28dd28e45116cb06806b33865 100644 --- a/schoolapps/untisconnect/api_helper.py +++ b/schoolapps/untisconnect/api_helper.py @@ -21,7 +21,7 @@ def run_using(obj): def get_term_by_id(term_id): data = run_using(models.Terms.objects).get(term_id=term_id) - print(data.schoolyear_id) + # print(data.schoolyear_id) return data @@ -47,5 +47,30 @@ def get_terms(): term = Term() term.create(item) terms.append(term) - print(term.name) + # print(term.name) return terms + + +################ +# HELP METHODS # +################ +def clean_array(a, conv=None): + b = [] + for el in a: + if el != '' and el != "0": + if conv is not None: + el = conv(el) + b.append(el) + return b + + +def untis_split_first(s, conv=None): + return clean_array(s.split(","), conv=conv) + + +def untis_split_second(s, conv=None): + return clean_array(s.split("~"), conv=conv) + + +def untis_split_third(s, conv=None): + return clean_array(s.split(";"), conv=conv) diff --git a/schoolapps/untisconnect/parse.py b/schoolapps/untisconnect/parse.py index b09a56d9d85ce19df3eb0dd14893c9a010431ce9..73731d8c18c7570e8b78587eb08d91921d05ffe5 100755 --- a/schoolapps/untisconnect/parse.py +++ b/schoolapps/untisconnect/parse.py @@ -21,9 +21,75 @@ class Lesson(object): el.create(day, hour, rooms) self.times.append(el) - def create(self, db_obj): + def create(self, raw_lesson, drive): self.filled = True + # Split data (,) + lesson_id = raw_lesson.lesson_id + raw_lesson_data = raw_lesson.lessonelement1.split(",") + raw_time_data = raw_lesson.lesson_tt.split(",") + + rtd2 = [] + for el in raw_time_data: + rtd2.append(el.split("~")) + + # print(rtd2) + + for el in rtd2: + day = int(el[1]) + hour = int(el[2]) + room_ids = untis_split_third(el[3], conv=int) + + rooms = [] + for room_id in room_ids: + r = drive["rooms"][room_id] + rooms.append(r) + + self.add_time(day, hour, rooms) + + # print(raw_lesson_data) + # print(raw_time_data) + + # Split data more (~) + rld2 = [] + for el in raw_lesson_data: + rld2.append(el.split("~")) + + # print(rld2) + + for el in rld2: + teacher_id = int(el[0]) + subject_id = int(el[2]) + room_ids = untis_split_third(el[4], int) + class_ids = untis_split_third(el[17], conv=int) + # print("TEACHER – ", teacher_id, "; SUBJECT – ", subject_id, "; ROOMS – ", room_ids, "; CLASSES – ", + # class_ids) + + if teacher_id != 0: + teacher = drive["teachers"][teacher_id] + else: + teacher = None + + if subject_id != 0: + subject = drive["subjects"][subject_id] + else: + subject = None + + rooms = [] + for room_id in room_ids: + r = drive["rooms"][room_id] + rooms.append(r) + + classes = [] + for class_id in class_ids: + c = drive["classes"][class_id] + classes.append(c) + + # print("TEACHER – ", teacher, "; SUBJECT – ", subject, "; ROOMS – ", rooms, + # "; CLASSES – ", classes) + + self.add_element(teacher, subject, rooms, classes) + class LessonElement(object): def __init__(self): @@ -52,43 +118,39 @@ class LessonTime(object): from .api import * +from .api_helper import untis_split_third -def clean_array(a, conv=None): - b = [] - for el in a: - if el != '' and el != "0": - if conv is not None: - el = conv(el) - b.append(el) - return b - - -def untis_split(s, conv=None): - return clean_array(s.split(";"), conv=conv) - - -def parse(): +def build_drive(): odrive = { "teachers": get_all_teachers(), "rooms": get_all_rooms(), "classes": get_all_classes(), - "subjects": get_all_subjects() + "subjects": get_all_subjects(), + "corridors": get_all_corridors(), } drive = { - "teachers": {}, - "rooms": {}, - "classes": {}, - "subjects": {} + # "teachers": {}, + # "rooms": {}, + # "classes": {}, + # "subjects": {} } for key, value in odrive.items(): + drive[key] = {} for el in value: id = el.id drive[key][id] = el print(drive) + return drive + +drive = build_drive() + + +def parse(): + global drive lessons = [] raw_lessons = get_raw_lessons() @@ -101,74 +163,7 @@ def parse(): if raw_lesson.lesson_tt and raw_lesson.lessonelement1: # Create object lesson_obj = Lesson() - - # Split data (,) - lesson_id = raw_lesson.lesson_id - raw_lesson_data = raw_lesson.lessonelement1.split(",") - raw_time_data = raw_lesson.lesson_tt.split(",") - - rtd2 = [] - for el in raw_time_data: - rtd2.append(el.split("~")) - - # print(rtd2) - - for el in rtd2: - day = int(el[1]) - hour = int(el[2]) - room_ids = untis_split(el[3], conv=int) - - rooms = [] - for room_id in room_ids: - r = drive["rooms"][room_id] - rooms.append(r) - - lesson_obj.add_time(day, hour, rooms) - - # print(raw_lesson_data) - # print(raw_time_data) - - # Split data more (~) - rld2 = [] - for el in raw_lesson_data: - rld2.append(el.split("~")) - - # print(rld2) - - for i, el in enumerate(rld2): - teacher_id = int(el[0]) - subject_id = int(el[2]) - # room_ids = untis_split(el[4], int) - class_ids = untis_split(el[17], conv=int) - # print("TEACHER – ", teacher_id, "; SUBJECT – ", subject_id, "; ROOMS – ", room_ids, "; CLASSES – ", - # class_ids) - - if teacher_id != 0: - teacher = drive["teachers"][teacher_id] - else: - teacher = None - - if subject_id != 0: - subject = drive["subjects"][subject_id] - else: - subject = None - - rooms = [] - # for room_id in room_ids: - # r = drive["rooms"][room_ids[i]] - # rooms.append(r) - - classes = [] - for class_id in class_ids: - c = drive["classes"][class_id] - classes.append(c) - - # print("TEACHER – ", teacher, "; SUBJECT – ", subject, "; ROOMS – ", rooms, - # "; CLASSES – ", classes) - - lesson_obj.add_element(teacher, subject, rooms, classes) - - # print("DAY – ", day, "; HOUR – ", hour, "; ROOMS – ", room_ids) + lesson_obj.create(raw_lesson, drive) lessons.append(lesson_obj) @@ -208,6 +203,26 @@ class LessonElementContainer(object): self.room = room +def get_lesson_by_id(id): + global drive + lesson = Lesson() + raw_lesson = run_one(models.Lesson.objects, filter_term=True).get(lesson_id=id) + lesson.create(raw_lesson, drive) + return lesson + + +def get_lesson_element_by_id_and_teacher(lesson_id, teacher): + print(lesson_id) + try: + lesson = get_lesson_by_id(lesson_id) + except Exception: + return None + for element in lesson.elements: + print(element.teacher.shortcode) + if element.teacher.id == teacher.id: + return element + return None + def parse_lesson_times(): times = [] for i, t in enumerate(LESSONS): diff --git a/schoolapps/untisconnect/sub.py b/schoolapps/untisconnect/sub.py new file mode 100644 index 0000000000000000000000000000000000000000..e4e21239e07428066affaad0591ec99777fc3799 --- /dev/null +++ b/schoolapps/untisconnect/sub.py @@ -0,0 +1,259 @@ +from django.utils import timezone + +from untisconnect import models +from untisconnect.api import run_default_filter, row_by_row_helper, get_teacher_by_id, get_subject_by_id, \ + get_room_by_id, get_class_by_id, get_corridor_by_id +from untisconnect.api_helper import run_using, untis_split_first +from untisconnect.parse import get_lesson_by_id, get_lesson_element_by_id_and_teacher, build_drive + +DATE_FORMAT = "%Y%m%d" + + +def untis_date_to_date(untis): + return timezone.datetime.strptime(str(untis), DATE_FORMAT) + + +def date_to_untis_date(date): + return date.strftime(DATE_FORMAT) + + +TYPE_SUBSTITUTION = 0 +TYPE_CANCELLATION = 1 +TYPE_TEACHER_CANCELLATION = 2 +TYPE_CORRIDOR = 3 + + +def parse_type_of_untis_flags(flags): + type_ = TYPE_SUBSTITUTION + if "E" in flags: + type_ = TYPE_CANCELLATION + elif "F" in flags: + type_ = TYPE_TEACHER_CANCELLATION + return type_ + + +drive = build_drive() + + +class Substitution(object): + def __init__(self): + self.filled = False + self.id = None + self.lesson_id = None + self.date = None + self.lesson = None + self.type = None + self.text = None + self.teacher_old = None + self.teacher_new = None + self.subject_old = None + self.subject_new = None + self.room_old = None + self.room_new = None + self.corridor = None + self.classes = None + self.lesson_element = None + + def __str__(self): + if self.filled: + return self.id + else: + return "Unbekannt" + + def create(self, db_obj): + self.filled = True + self.id = db_obj.substitution_id + self.lesson_id = db_obj.lesson_idsubst + self.date = untis_date_to_date(db_obj.date) + self.lesson = db_obj.lesson + self.type = parse_type_of_untis_flags(db_obj.flags) + self.text = db_obj.text + + # Lesson + + # Teacher + # print(db_obj.teacher_idlessn) + if db_obj.teacher_idlessn != 0: + self.teacher_old = drive["teachers"][db_obj.teacher_idlessn] + if db_obj.teacher_idsubst != 0: + self.teacher_new = drive["teachers"][db_obj.teacher_idsubst] + + if self.teacher_old is not None and self.teacher_new.id == self.teacher_old.id: + self.teacher_new = None + + self.lesson_element = get_lesson_element_by_id_and_teacher(self.lesson_id, self.teacher_old) + # print(self.lesson) + + # Subject + self.subject_old = self.lesson_element.subject if self.lesson_element is not None else None + if db_obj.subject_idsubst != 0: + self.subject_new = drive["subjects"][db_obj.subject_idsubst] + + if self.subject_old is not None and self.subject_old.id == self.subject_new.id: + self.subject_new = None + + # Room + self.rooms_old = self.lesson_element.rooms if self.lesson_element is not None else [] + if len(self.rooms_old) >= 1: + self.room_old = self.rooms_old[0] + + if db_obj.room_idsubst != 0: + self.room_new = drive["rooms"][db_obj.room_idsubst] + + if self.room_old is not None and self.room_old.id == self.room_new.id: + self.room_new = None + # if self.rooms_old + + # print(self.room_new) + # print("CORRIDOR") + # print(self.corridor) + if db_obj.corridor_id != 0: + self.corridor = drive["corridors"][db_obj.corridor_id] + self.type = TYPE_CORRIDOR + # Classes + + self.classes = [] + class_ids = untis_split_first(db_obj.classids, conv=int) + # print(class_ids) + for id in class_ids: + self.classes.append(drive["classes"][id]) + + +def substitutions_sorter(sub): + # First, sort by class + sort_by = "".join(class_.name for class_ in sub.classes) + + # If the sub hasn't got a class, then put it to the bottom + if sort_by == "": + sort_by = "Z" + + # Second, sort by lesson number + sort_by += str(sub.lesson) + + return sort_by + + +class SubRow(object): + def __init__(self): + self.color = "black" + self.css_class = "black-text" + self.lesson = "" + self.classes = "" + self.teacher = "" + self.teacher_full = "" + self.subject = "" + self.subject_full = "" + self.room = "" + self.room_full = "" + self.text = "" + self.extra = "" + + +def generate_teacher_row(sub, full=False): + if sub.type == 1: + teacher = "<s>{}</s>".format(sub.teacher_old.shortcode if not full else sub.teacher_old.name) + + elif sub.teacher_new and sub.teacher_old: + teacher = "<s>{}</s> → <strong>{}</strong>".format( + sub.teacher_old.shortcode if not full else sub.teacher_old.name, + sub.teacher_new.shortcode if not full else sub.teacher_new.name) + elif sub.teacher_new and not sub.teacher_old: + teacher = "<strong>{}</strong>".format(sub.teacher_new.shortcode if not full else sub.teacher_new.name) + else: + teacher = "<strong>{}</strong>".format(sub.teacher_old.shortcode if not full else sub.teacher_old.name) + + return teacher + + +def generate_subject_row(sub, full=False): + if sub.type == 3: + subject = "Aufsicht" + elif sub.type == 1 or sub.type == 2: + subject = "<s>{}</s>".format(sub.subject_old.shortcode if not full else sub.subject_old.name) + elif sub.subject_new and sub.subject_old: + subject = "<s>{}</s> → <strong>{}</strong>".format( + sub.subject_old.shortcode if not full else sub.subject_old.name, + sub.subject_new.shortcode if not full else sub.subject_new.name) + elif sub.subject_new and not sub.subject_old: + subject = "<strong>{}</strong>".format(sub.subject_new.shortcode if not full else sub.subject_new.name) + else: + subject = "<strong>{}</strong>".format(sub.subject_old.shortcode if not full else sub.subject_old.name) + + return subject + + +def generate_room_row(sub, full=False): + room = "" + if sub.type == 3: + room = sub.corridor.name + elif sub.type == 1 or sub.type == 2: + pass + elif sub.room_new and sub.room_old: + room = "<s>{}</s> → <strong>{}</strong>".format(sub.room_old.shortcode if not full else sub.room_old.name, + sub.room_new.shortcode if not full else sub.room_new.name) + elif sub.room_new and not sub.room_old: + room = sub.room_new.shortcode if not full else sub.room_new.name + elif not sub.room_new and not sub.room_old: + pass + else: + room = sub.room_old.shortcode if not full else sub.room_old.name + + return room + + +def generate_sub_table(subs): + sub_rows = [] + for sub in subs: + sub_row = SubRow() + + sub_row.color = "black" + if sub.type == 1 or sub.type == 2: + sub_row.css_class = "green-text" + sub_row.color = "green" + elif sub.type == 3: + sub_row.css_class = "blue-text" + sub_row.color = "blue" + + if sub.type == 3: + sub_row.lesson = "{}./{}".format(sub.lesson - 1, sub.lesson) + else: + sub_row.lesson = "{}.".format(sub.lesson) + + for class_ in sub.classes: + sub_row.classes = class_.name + + sub_row.teacher = generate_teacher_row(sub) + sub_row.teacher_full = generate_teacher_row(sub, full=True) + sub_row.subject = generate_subject_row(sub) + sub_row.subject_full = generate_subject_row(sub, full=True) + sub_row.room = generate_room_row(sub) + sub_row.room_full = generate_room_row(sub, full=True) + + sub_row.text = sub.text + + sub_row.badge = None + if sub.type == 1: + sub_row.badge = "Schüler frei" + elif sub.type == 2: + sub_row.badge = "Lehrer frei" + + sub_row.extra = "{} {}".format(sub.id, sub.lesson_id) + + sub_rows.append(sub_row) + return sub_rows + + +def get_substitutions_by_date(date): + subs_raw = run_default_filter( + run_using(models.Substitution.objects.filter(date=date_to_untis_date(date)).order_by("classids", "lesson")), + filter_term=False) + # print(subs_raw) + + subs = row_by_row_helper(subs_raw, Substitution) + # print(subs) + # for row in subs: + # print(row.classes) + # for class_ in row.classes: + # print(class_.name) + subs.sort(key=substitutions_sorter) + return subs