Browse Source

add event grid view

subDesTagesMitExtraKaese 1 year ago
parent
commit
12c966c0d3

+ 41 - 21
zitap/helpers.py

@@ -24,8 +24,8 @@ def get_slot_count( event ) -> int :
     """ Get the number of slots in an event. """
     """ Get the number of slots in an event. """
 
 
     # Get the timespan of the event
     # Get the timespan of the event
-    start_time = datetime.datetime.combine(datetime.date.today(), event.end_time)
-    end_time = datetime.datetime.combine(datetime.date.today(), event.start_time)
+    start_time = datetime.datetime.combine(datetime.date.today(), event.start_time)
+    end_time = datetime.datetime.combine(datetime.date.today(), event.end_time)
     timespan = abs(end_time - start_time)
     timespan = abs(end_time - start_time)
 
 
     # Get the number of slots in the event
     # Get the number of slots in the event
@@ -40,45 +40,41 @@ def slots2grid( event : Event ) -> dict :
     n_slots = get_slot_count(event)
     n_slots = get_slot_count(event)
 
 
     data = {
     data = {
-        'days': [],
         'rows': [],
         'rows': [],
+        'days': [],
+        'slot_height': 30 * (event.slot_interval.total_seconds() / 3600),
+        'n_days': event.date_set.count(),
     }
     }
     # Get the timespan of the event
     # Get the timespan of the event
-    start_time = datetime.datetime.combine(datetime.date.today(), event.end_time)
-    end_time = datetime.datetime.combine(datetime.date.today(), event.start_time)
+    start_time = datetime.datetime.combine(datetime.date.today(), event.start_time)
+    end_time = datetime.datetime.combine(datetime.date.today(), event.end_time)
     timespan = abs(end_time - start_time)
     timespan = abs(end_time - start_time)
     
     
     # Get the slots in a day
     # Get the slots in a day
-    slots_per_day = timespan.total_seconds() // event.slot_interval.total_seconds()
-
-    # Fill the rows of the grid
-    last_hour = None
-    for i in range(slots_per_day):
-        current_time = start_time + i * event.slot_interval
-        data['rows'].append({
-            'time': current_time,
-            'is_full_hour': current_time.hour != last_hour,
-        })
-        last_hour = current_time.hour
+    slots_per_day = int(timespan.total_seconds() // event.slot_interval.total_seconds())
 
 
     # Get the slots of each day
     # Get the slots of each day
     participants = event.participant_set.all()
     participants = event.participant_set.all()
     participant_slot_strings = [slots2string(participant, n_slots) for participant in participants]
     participant_slot_strings = [slots2string(participant, n_slots) for participant in participants]
-    max_occupancy = 0
+    max_occupancy = 1
 
 
-    for date in event.date_set.all():
+    for n, date in enumerate(event.date_set.all()):
         # Get participants for each slot
         # Get participants for each slot
         slot_participants = [[] for i in range(slots_per_day)]
         slot_participants = [[] for i in range(slots_per_day)]
         for i, participant in enumerate(participants):
         for i, participant in enumerate(participants):
             for j in range(slots_per_day):
             for j in range(slots_per_day):
-                if participant_slot_strings[i][j] == '1':
+                if participant_slot_strings[i][n*slots_per_day + j] == '1':
                     slot_participants[j].append(participant)
                     slot_participants[j].append(participant)
         # Fill the slots of the day
         # Fill the slots of the day
         slots = []
         slots = []
-        for ps in slot_participants:
+        for j, ps in enumerate(slot_participants):
+            slot_begin = (start_time + j * event.slot_interval).strftime('%H:%M')
+            slot_end = (start_time + (j+1) * event.slot_interval).strftime('%H:%M')
             slots.append({
             slots.append({
-                'tooltip': ', '.join([p.user.username for p in ps]),
+                'tooltip': f"{slot_begin} - {slot_end} \n{', '.join([p.user.username for p in ps])}",
                 'occupancy': len(ps),
                 'occupancy': len(ps),
+                'offset': n*slots_per_day + j,
+                'date': date.date,
             })
             })
             max_occupancy = max(max_occupancy, len(ps))
             max_occupancy = max(max_occupancy, len(ps))
         
         
@@ -87,3 +83,27 @@ def slots2grid( event : Event ) -> dict :
             'slots': slots,
             'slots': slots,
         }
         }
         data['days'].append(day)
         data['days'].append(day)
+
+    # Fill the rows of the grid
+    last_hour = None
+    for i in range(slots_per_day):
+        current_time = start_time + i * event.slot_interval
+        data['rows'].append({
+            'days': [day['slots'][i] for day in data['days']],
+            'time': current_time,
+            'is_full_hour': current_time.hour != last_hour,
+        })
+        last_hour = current_time.hour
+    data['rows'].append({
+        'days': [None for day in data['days']],
+        'time': end_time,
+        'is_full_hour': True,
+    })
+    data['max_value'] = max_occupancy
+
+    # create a color for each slot from white to dark green
+    for row in data['rows']:
+        for day in row['days']:
+            if day is not None:
+                day['color'] = f"hsl(120, 100%, {100 - 70 * day['occupancy'] / max_occupancy}%)"
+    return data

+ 0 - 1
zitap/static/zitap/css/date-picker.css

@@ -3,7 +3,6 @@
     grid-template-columns: repeat(9, 1fr);
     grid-template-columns: repeat(9, 1fr);
     grid-template-rows: repeat(6, 1fr);
     grid-template-rows: repeat(6, 1fr);
     grid-gap: 1px;
     grid-gap: 1px;
-    width: 8mm;
     justify-items: center;
     justify-items: center;
     align-items: center;
     align-items: center;
     user-select: none;
     user-select: none;

+ 41 - 0
zitap/static/zitap/css/event.css

@@ -0,0 +1,41 @@
+.occupancy-grid {
+  display: grid;
+  user-select: none;
+  width: fit-content;
+  margin: 10px;
+}
+.occupancy-grid .day {
+  font-weight: bold;
+  align-self: center;
+  justify-self: center;
+}
+.occupancy-grid .time {
+  position: relative;
+  width: 50px;
+}
+.occupancy-grid .time>.time-label{
+  position: absolute;
+  font-weight: bold;
+  transform: translate(0, -50%);
+}
+.occupancy-grid .slot {
+  width: 70px;
+  background-color: #fff;
+  transition: background-color .2s ease;
+  display: grid;
+  justify-items: center;
+  align-items: center;
+  border-left: 1px solid #000;
+  border-right: 1px solid #000;
+  margin: 0 5px;
+}
+.occupancy-grid .slot.full-hour {
+  border-top: 1px solid #000;
+}
+.occupancy-grid .slot.last {
+  border-bottom: 1px solid #000;
+}
+.occupancy-grid .slot:hover {
+  background-color: rgb(214, 93, 93) !important;
+  cursor: pointer;
+}

+ 30 - 1
zitap/templates/zitap/event.html

@@ -4,10 +4,39 @@
 
 
 {% block title %}{{ event.name }}{% endblock %}
 {% block title %}{{ event.name }}{% endblock %}
 
 
+{% block head %}
+    <link rel="stylesheet" href="{% static 'zitap/css/event.css' %}">
+    <style type="text/css">
+        .occupancy-grid {
+            grid-template-columns: auto repeat({{ grid.n_days }}, 1fr);
+        }
+        .occupancy-grid .slot {
+            height: {{ grid.slot_height }}px;
+        }
+    </style>
+{% endblock %}
+
 {% block content %}
 {% block content %}
 <h1>{{ event.name }}</h1>
 <h1>{{ event.name }}</h1>
 
 
-
+<div class="occupancy-grid">
+    <div></div>
+    {% for day in grid.days %}
+        <div class="day">{{ day.date|date:"M d" }}</div>
+    {% endfor %}
+    {% for row in grid.rows %}
+        <div class="time">
+            {% if row.is_full_hour %}<div class="time-label">{{ row.time|date:"H:i" }}</div>{% endif %}
+        </div>
+        {% for day in row.days %}
+            {% if day %}
+            <div class="slot{% if row.is_full_hour %} full-hour{% endif %}" id="grid_slot_{{ day.offset }}" title="{{ day.tooltip }}" style="background-color: {{ day.color }};"></div>
+            {% else %}
+            <div class="slot last"></div>
+            {% endif %}
+        {% endfor %}
+    {% endfor %}
+</div>
 
 
 
 
 {% if update_form %}
 {% if update_form %}

+ 1 - 0
zitap/templates/zitap/index.html

@@ -7,6 +7,7 @@
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="stylesheet" href="{% static 'zitap/css/style.css' %}">
     <link rel="stylesheet" href="{% static 'zitap/css/style.css' %}">
     <link rel="stylesheet" href="{% static 'zitap/css/date-picker.css' %}">
     <link rel="stylesheet" href="{% static 'zitap/css/date-picker.css' %}">
+    {% block head %}{% endblock %}
   </head>
   </head>
   <body>
   <body>
     <div class="container">
     <div class="container">

+ 3 - 5
zitap/views.py

@@ -1,4 +1,4 @@
-import datetime
+import random
 from django.db import IntegrityError
 from django.db import IntegrityError
 from django.http import HttpResponseNotFound, HttpResponseRedirect, JsonResponse, HttpResponseNotAllowed
 from django.http import HttpResponseNotFound, HttpResponseRedirect, JsonResponse, HttpResponseNotAllowed
 from django.shortcuts import render
 from django.shortcuts import render
@@ -44,7 +44,6 @@ def event(request, url):
     else:
     else:
         login_form = LoginForm()
         login_form = LoginForm()
         update_form = None
         update_form = None
-
     return render(
     return render(
         request, 
         request, 
         'zitap/event.html', 
         'zitap/event.html', 
@@ -77,6 +76,8 @@ def login(request, url):
 
 
         auth_login(request, user)
         auth_login(request, user)
         participant, created = Participant.objects.get_or_create(event=event, user=user)
         participant, created = Participant.objects.get_or_create(event=event, user=user)
+        participant.slots = random.getrandbits(get_slot_count(event)).to_bytes(get_slot_count(event) // 8, 'big')
+        participant.save()
         request.session['participant_id'] = participant.id
         request.session['participant_id'] = participant.id
         return HttpResponseRedirect(f'/{event.url}')
         return HttpResponseRedirect(f'/{event.url}')
 
 
@@ -85,9 +86,6 @@ def logout(request, url):
         event = Event.objects.get(url=url)
         event = Event.objects.get(url=url)
     except Event.DoesNotExist:
     except Event.DoesNotExist:
         return render(request, 'zitap/event-not-found.html')
         return render(request, 'zitap/event-not-found.html')
-    
-    if request.method != 'POST':
-        return HttpResponseNotAllowed(['POST'])
 
 
     if 'participant_id' in request.session:
     if 'participant_id' in request.session:
         del request.session['participant_id']
         del request.session['participant_id']