From 1294a2a4c9109ba6bbe53b713c1cb5f29c2fa694 Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Tue, 3 Sep 2019 19:10:23 +0200 Subject: [PATCH] Show events | Get information about user --- react/src/dashboard.js | 60 ++++++---- requirements.txt | 3 +- .../migrations/0002_notification_read.py | 17 +++ .../dashboard/templates/dashboard/index.html | 1 + schoolapps/dashboard/views.py | 52 ++++++++- schoolapps/helper.py | 49 +++++++++ schoolapps/static/common/style.css | 16 +++ schoolapps/static/js/dashboard.js | 103 ++++++++++++------ .../migrations/0010_auto_20190901_1040.py | 17 +++ schoolapps/timetable/views.py | 38 +++++-- 10 files changed, 291 insertions(+), 65 deletions(-) create mode 100644 schoolapps/dashboard/migrations/0002_notification_read.py create mode 100644 schoolapps/timetable/migrations/0010_auto_20190901_1040.py diff --git a/react/src/dashboard.js b/react/src/dashboard.js index 7ecf67c9c..7886b5398 100644 --- a/react/src/dashboard.js +++ b/react/src/dashboard.js @@ -11,7 +11,8 @@ class Dashboard extends React.Component { constructor() { super(); this.state = { - refreshIn: REFRESH_TIME + refreshIn: REFRESH_TIME, + isLoading: true }; } @@ -33,7 +34,7 @@ class Dashboard extends React.Component { $.getJSON(API_URL, (data) => { console.log(data); if (data) { - that.setState({...data, refreshIn: REFRESH_TIME + 1}); + that.setState({...data, refreshIn: REFRESH_TIME + 1, isLoading: false}); that.updateRefreshTime(); } }) @@ -56,7 +57,29 @@ class Dashboard extends React.Component { } render() { + if (this.state.isLoading) { + return <div className={"row center-via-flex container"} style={{"height": "10em"}}> + <div className={"center2-via-flex"}> + <div className="preloader-wrapper big active"> + <div className="spinner-layer spinner-primary"> + <div className="circle-clipper left"> + <div className="circle"/> + </div> + <div className="gap-patch"> + <div className="circle"/> + </div> + <div className="circle-clipper right"> + <div className="circle"/> + </div> + </div> + </div> + <p className={"text-center"}>Wird geladen …</p> + </div> + </div>; + } + const that = this; + console.log(MY_PLAN_URL); return <div> <button className={"btn-flat right grey-text"} onClick={this.updateData}> <i className={"material-icons left"}>refresh</i> @@ -85,33 +108,30 @@ class Dashboard extends React.Component { <div className={"col s12 m6 l6 xl8 no-padding"}> <div className="col s12 m12 l12 xl6"> <div className="card"> - <div className="card-content"> - <span className="card-title">Vertretungen der <em>Eb</em> für heute</span> - <p>I am a very simple card. I am good at containing small bits of information. - I am convenient because I require little markup to use effectively.</p> - </div> - <div className="card-action"> - <a href="#"> + {this.state.has_plan ? <div className="card-content"> + <span className="card-title">Vertretungen {this.state.plan.type == 2 ? "der" : "für"} + <em>{this.state.plan.name}</em> für {this.state.date_formatted}</span> + <p>Keine Vertretungen für morgen vorhanden.</p> + </div> : <p className={"flow-text"}>Keine Vertretungen vorhanden.</p>} + {this.state.has_plan ? <div className="card-action"> + <a href={MY_PLAN_URL}> <span className="badge new primary-color card-action-badge">SMART PLAN</span> anzeigen </a> - </div> + </div> : ""} </div> </div> <div className="col s12 m12 l12 xl6"> <div className="card"> <div className="card-content"> <span className="card-title">Aktuelle Termine</span> - <div className="card-panel event-card"> - <span className={"title"}>Sextanereinschulung</span> - <br/> - 28.Aug. 2019 18:30 - 22:00 - </div> - <div className="card-panel event-card"> - <span className={"title"}>Sextanereinschulung</span> - <br/> - 28.Aug. 2019 18:30 - 22:00 - </div> + {this.state.current_events && this.state.current_events.length > 0 ? this.state.current_events.map(function (event) { + return <div className="card-panel event-card"> + <span className={"title"}>{event.name}</span> + <br/> + {event.formatted} + </div>; + }) : "Keine aktuellen Termine"} </div> <div className="card-action"> <a href="https://katharineum-zu-luebeck.de/aktuelles/termine/">Weitere Termine</a> diff --git a/requirements.txt b/requirements.txt index f0c4a37de..85652deeb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ django_react_templatetags kanboard PyPDF2 martor -django_widget_tweaks \ No newline at end of file +django_widget_tweaks +ics \ No newline at end of file diff --git a/schoolapps/dashboard/migrations/0002_notification_read.py b/schoolapps/dashboard/migrations/0002_notification_read.py new file mode 100644 index 000000000..19cc47986 --- /dev/null +++ b/schoolapps/dashboard/migrations/0002_notification_read.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.1 on 2019-09-01 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dashboard', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='notification', + name='read', + field=models.BooleanField(default=False), + ), + ] diff --git a/schoolapps/dashboard/templates/dashboard/index.html b/schoolapps/dashboard/templates/dashboard/index.html index cc023ef4e..96d7fbb15 100755 --- a/schoolapps/dashboard/templates/dashboard/index.html +++ b/schoolapps/dashboard/templates/dashboard/index.html @@ -4,6 +4,7 @@ <main> <script> var API_URL = "{% url "api_information" %}"; + var MY_PLAN_URL = "{% url "timetable_my_plan" %}"; </script> <script src="{% static "js/react/react.development.js" %}"></script> <script src="{% static "js/react/react-dom.development.js" %}"></script> diff --git a/schoolapps/dashboard/views.py b/schoolapps/dashboard/views.py index f258e37b3..e4da3c153 100755 --- a/schoolapps/dashboard/views.py +++ b/schoolapps/dashboard/views.py @@ -2,8 +2,13 @@ from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.shortcuts import render, redirect, get_object_or_404 from django.urls import reverse +from django.utils import timezone -from helper import get_newest_articles +from helper import get_newest_articles, get_current_events +from schoolapps.settings import SHORT_WEEK_DAYS, LONG_WEEK_DAYS +from timetable.hints import get_all_hints_by_class_and_time_period, get_all_hints_for_teachers_by_time_period +from timetable.views import get_next_weekday_with_time, get_type_and_object_of_user +from untisconnect.api import TYPE_TEACHER, TYPE_CLASS from .models import Activity, register_notification, Notification # from .apps import DashboardConfig from mailer import send_mail_with_template @@ -38,6 +43,38 @@ def api_information(request): else: newest_article = None print(newest_articles) + + next_weekday = get_next_weekday_with_time(timezone.now(), timezone.now().time()) + if next_weekday.date() == timezone.now().date(): + date_formatted = "heute" + print("Gleicher Tag") + elif next_weekday.date() == timezone.now().date() + timezone.timedelta(days=1): + print("Nächster Tag") + date_formatted = "morgen" + else: + print("Ganz anderer Tag") + date_formatted = LONG_WEEK_DAYS[next_weekday.isoweekday() - 2] + + # Get user type (student, teacher, etc.) + _type, el = get_type_and_object_of_user(request.user) + hints = None + if _type == TYPE_TEACHER: + # Teacher + plan_id = el.id + raw_type = "teacher" + + # Get hints + # hints = list(get_all_hints_for_teachers_by_time_period(next_weekday, next_weekday)) + + 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, next_weekday, next_weekday)) + context = { 'activities': list(activities.values()), 'notifications': list(notifications.values()), @@ -49,7 +86,20 @@ def api_information(request): 'subjects': UserInformation.user_subjects(request.user), 'has_wifi': UserInformation.user_has_wifi(request.user), "newest_article": newest_article, + "current_events": get_current_events()[:3], + "date_formatted": date_formatted, } + + if _type is not None: + context["plan"] = { + "type": _type, + "name": el.shortcode if _type == TYPE_TEACHER else el.name, + "hints": hints + } + context["has_plan"] = True + else: + context["has_plan"] = False + print(context) return JsonResponse(context) diff --git a/schoolapps/helper.py b/schoolapps/helper.py index 6c1538300..55ca87b07 100644 --- a/schoolapps/helper.py +++ b/schoolapps/helper.py @@ -2,6 +2,10 @@ import os from uuid import uuid4 from django.template.loader_tags import register +from datetime import datetime + +from django.utils import timezone, formats +from ics import Calendar def path_and_rename(instance, filename): @@ -65,3 +69,48 @@ def get_newest_articles(domain, limit=0, author_blacklist=None): break return posts + + +CALENDAR_URL = "https://nimbus.katharineum.de/remote.php/dav/public-calendars/owit7yysLB2CYNTq?export" + + +def get_current_events(): + c = Calendar(requests.get(CALENDAR_URL).text) + print(c.events) + e = list(c.timeline)[0] + print(c.timeline.today()) + i = 0 + events = [] + for event in c.timeline.start_after(timezone.now()): + if i >= 5: + break + i += 1 + + begin_date_formatted = formats.date_format(event.begin) + end_date_formatted = formats.date_format(event.end) + begin_time_formatted = formats.time_format(event.begin.time()) + end_time_formatted = formats.time_format(event.end.time()) + if event.begin.date() == event.end.date(): + formatted = begin_date_formatted + if not event.all_day: + formatted += " " + begin_time_formatted + if event.begin.time != event.end.time(): + formatted += " – " + end_time_formatted + else: + if event.all_day: + formatted = "{} – {}".format(begin_date_formatted, end_date_formatted) + else: + formatted = "{} {} – {} {}".format(begin_date_formatted, begin_time_formatted, end_date_formatted, + end_time_formatted) + print(formatted) + print(formats.date_format(event.begin)) + events.append({ + "name": event.name, + # "begin": event.begin, + # "end": event.end, + "formatted": formatted + }) + # print(event) + print(events) + print("Event '{}' started {}".format(e.name, e.begin.humanize())) + return events diff --git a/schoolapps/static/common/style.css b/schoolapps/static/common/style.css index dcdc1ae1b..dae5bc650 100755 --- a/schoolapps/static/common/style.css +++ b/schoolapps/static/common/style.css @@ -598,4 +598,20 @@ i.collapsible-trigger { border-radius: 0 3px 3px 0; text-transform: uppercase; font-weight: 300; +} + +.center-via-flex { + display: flex; + align-items: center; + justify-content: center; +} + +.center2-via-flex { + display: flex; + flex-direction: column; + align-items: center; +} + +.spinner-primary { + border-color: #da1f3d; } \ No newline at end of file diff --git a/schoolapps/static/js/dashboard.js b/schoolapps/static/js/dashboard.js index c47444712..8403f4577 100644 --- a/schoolapps/static/js/dashboard.js +++ b/schoolapps/static/js/dashboard.js @@ -85,14 +85,15 @@ var Dashboard = function (_React$Component) { $.getJSON(API_URL, function (data) { console.log(data); if (data) { - that.setState(Object.assign({}, data, {refreshIn: REFRESH_TIME + 1})); + that.setState(Object.assign({}, data, {refreshIn: REFRESH_TIME + 1, isLoading: false})); that.updateRefreshTime(); } }); }; _this.state = { - refreshIn: REFRESH_TIME + refreshIn: REFRESH_TIME, + isLoading: true }; return _this; } @@ -118,7 +119,47 @@ var Dashboard = function (_React$Component) { }, { key: "render", value: function render() { + if (this.state.isLoading) { + return React.createElement( + "div", + {className: "row center-via-flex container", style: {"height": "10em"}}, + React.createElement( + "div", + {className: "center2-via-flex"}, + React.createElement( + "div", + {className: "preloader-wrapper big active"}, + React.createElement( + "div", + {className: "spinner-layer spinner-primary"}, + React.createElement( + "div", + {className: "circle-clipper left"}, + React.createElement("div", {className: "circle"}) + ), + React.createElement( + "div", + {className: "gap-patch"}, + React.createElement("div", {className: "circle"}) + ), + React.createElement( + "div", + {className: "circle-clipper right"}, + React.createElement("div", {className: "circle"}) + ) + ) + ), + React.createElement( + "p", + {className: "text-center"}, + "Wird geladen \u2026" + ) + ) + ); + } + var that = this; + console.log(MY_PLAN_URL); return React.createElement( "div", null, @@ -196,32 +237,39 @@ var Dashboard = function (_React$Component) { React.createElement( "div", {className: "card"}, - React.createElement( + this.state.has_plan ? React.createElement( "div", {className: "card-content"}, React.createElement( "span", {className: "card-title"}, - "Vertretungen der ", + "Vertretungen ", + this.state.plan.type == 2 ? "der" : "für", + " ", React.createElement( "em", null, - "Eb" + this.state.plan.name ), - " f\xFCr heute" + " f\xFCr ", + this.state.date_formatted ), React.createElement( "p", null, - "I am a very simple card. I am good at containing small bits of information. I am convenient because I require little markup to use effectively." + "Keine Vertretungen f\xFCr morgen vorhanden." ) + ) : React.createElement( + "p", + {className: "flow-text"}, + "Keine Vertretungen vorhanden." ), - React.createElement( + this.state.has_plan ? React.createElement( "div", {className: "card-action"}, React.createElement( "a", - {href: "#"}, + {href: MY_PLAN_URL}, React.createElement( "span", {className: "badge new primary-color card-action-badge"}, @@ -229,7 +277,7 @@ var Dashboard = function (_React$Component) { ), "anzeigen" ) - ) + ) : "" ) ), React.createElement( @@ -246,28 +294,19 @@ var Dashboard = function (_React$Component) { {className: "card-title"}, "Aktuelle Termine" ), - React.createElement( - "div", - {className: "card-panel event-card"}, - React.createElement( - "span", - {className: "title"}, - "Sextanereinschulung" - ), - React.createElement("br", null), - "28.Aug. 2019 18:30 - 22:00" - ), - React.createElement( - "div", - {className: "card-panel event-card"}, - React.createElement( - "span", - {className: "title"}, - "Sextanereinschulung" - ), - React.createElement("br", null), - "28.Aug. 2019 18:30 - 22:00" - ) + this.state.current_events && this.state.current_events.length > 0 ? this.state.current_events.map(function (event) { + return React.createElement( + "div", + {className: "card-panel event-card"}, + React.createElement( + "span", + {className: "title"}, + event.name + ), + React.createElement("br", null), + event.formatted + ); + }) : "Keine aktuellen Termine" ), React.createElement( "div", diff --git a/schoolapps/timetable/migrations/0010_auto_20190901_1040.py b/schoolapps/timetable/migrations/0010_auto_20190901_1040.py new file mode 100644 index 000000000..05387a6d8 --- /dev/null +++ b/schoolapps/timetable/migrations/0010_auto_20190901_1040.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.1 on 2019-09-01 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('timetable', '0009_hint_classes_formatted'), + ] + + operations = [ + migrations.AlterField( + model_name='hintclass', + name='class_id', + field=models.IntegerField(), + ), + ] diff --git a/schoolapps/timetable/views.py b/schoolapps/timetable/views.py index 57868377a..09eb673ce 100755 --- a/schoolapps/timetable/views.py +++ b/schoolapps/timetable/views.py @@ -203,6 +203,28 @@ def plan(request, plan_type, plan_id, regular="", year=timezone.datetime.now().y return render(request, 'timetable/plan.html', context) +def get_type_and_object_of_user(user): + _type = UserInformation.user_type(user) + if _type == UserInformation.TEACHER: + # Teacher + _type = TYPE_TEACHER + shortcode = user.username + el = get_teacher_by_shortcode(shortcode) + plan_id = el.id + raw_type = "teacher" + + elif _type == UserInformation.STUDENT: + # Student + _type = TYPE_CLASS + _name = UserInformation.user_classes(user)[0] + el = get_class_by_name(_name) + plan_id = el.id + raw_type = "class" + else: + return None, None + + return _type, el + @login_required @permission_required("timetable.show_plan") def my_plan(request, year=None, month=None, day=None): @@ -223,13 +245,9 @@ def my_plan(request, year=None, month=None, day=None): monday_of_week = get_calendar_week(calendar_week, date.year)["first_day"] # Get user type (student, teacher, etc.) - _type = UserInformation.user_type(request.user) - - if _type == UserInformation.TEACHER: + _type, el = get_type_and_object_of_user(request.user) + if _type == TYPE_TEACHER: # Teacher - _type = TYPE_TEACHER - shortcode = request.user.username - el = get_teacher_by_shortcode(shortcode) plan_id = el.id raw_type = "teacher" @@ -237,11 +255,9 @@ def my_plan(request, year=None, month=None, day=None): 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 == UserInformation.STUDENT: + elif _type == TYPE_CLASS: # Student - _type = TYPE_CLASS - _name = UserInformation.user_classes(request.user)[0] - el = get_class_by_name(_name) + plan_id = el.id raw_type = "class" @@ -275,7 +291,7 @@ def my_plan(request, year=None, month=None, day=None): return render(request, 'timetable/myplan.html', context) -def get_next_weekday_with_time(date, time): +def get_next_weekday_with_time(date, time) -> datetime.datetime: """Get the next weekday by a datetime object""" if time > datetime.time(15, 35): -- GitLab