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