add first signage view, terminal for team
This commit is contained in:
parent
4900a90dc0
commit
962734262c
|
@ -44,6 +44,7 @@ INSTALLED_APPS = [
|
||||||
"shiftregister.team.apps.TeamConfig",
|
"shiftregister.team.apps.TeamConfig",
|
||||||
"shiftregister.pages.apps.PagesConfig",
|
"shiftregister.pages.apps.PagesConfig",
|
||||||
"shiftregister.metrics.apps.MetricsConfig",
|
"shiftregister.metrics.apps.MetricsConfig",
|
||||||
|
"shiftregister.signage.apps.SignageConfig",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import TeamBackup
|
||||||
|
|
||||||
|
admin.site.register(TeamBackup)
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SignageConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "shiftregister.signage"
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class TeamBackupForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Generated by Django 4.0.4 on 2022-05-18 13:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("app", "0010_room_meeting_location"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="TeamBackup",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=100)),
|
||||||
|
(
|
||||||
|
"shift",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="app.shift"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
from django.db import models
|
||||||
|
from shiftregister.app.models import *
|
||||||
|
|
||||||
|
|
||||||
|
class TeamBackup(models.Model):
|
||||||
|
shift = models.ForeignKey(Shift, on_delete=models.CASCADE)
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.shift} {self.name}"
|
|
@ -0,0 +1,41 @@
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
<div class="box {%if not shift.teambackup_set.all%} has-background-warning{%endif%}">
|
||||||
|
<div class="content">
|
||||||
|
<strong>Ort:</strong> {{ shift.room.name }}<br>
|
||||||
|
<strong>Beginn:</strong> {{ shift.start_at }}<br>
|
||||||
|
<strong>Dauer:</strong> {{ shift.duration }}<br>
|
||||||
|
<strong>Belegung:</strong> {{ shift.shiftregistration_set.count }}/{{ shift.required_helpers|default:shift.room.required_helpers }}
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
{% for item in shift.teambackup_set.all%}
|
||||||
|
<li>{{item.name}}</li>
|
||||||
|
{%endfor%}
|
||||||
|
</ul>
|
||||||
|
<form action="{%url 'signage:work_add' shift.pk%}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form %}
|
||||||
|
<div class="field">
|
||||||
|
{% if field.widget_type == 'checkbox' %}
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox" for="{{ field.id_for_label }}">{{ field }} {{ field.label }}</label>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<label class="label" for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||||
|
<div class="control">
|
||||||
|
{{ field }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<p class="help is-danger">{{ error }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% if field.help_text %}
|
||||||
|
<p class="help">{{ field.help_text }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<button class="button is-link" type="submit">Team hinzufuegen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Team{% endblock %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="{% url 'team:shift_overview' %}">Schichtübersicht</a>
|
||||||
|
<a class="navbar-item" href="{% url 'team:shift_free' %}">Freie Schichten</a>
|
||||||
|
<a class="navbar-item" href="{% url 'team:shift_all' %}">Alle Schichten</a>
|
||||||
|
<a class="navbar-item" href="{% url 'team:bulk_message' %}">Massen-Nachricht</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end"></div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
{% include 'notifications.html' %}
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>{% block title %}Help!{% endblock %} – Helfer*innen</title>
|
||||||
|
<link rel="icon" href="{% static 'tonkakt.svg' %}" sizes="any" type="image/svg+xml">
|
||||||
|
<link rel="stylesheet" href="{% static 'bulma.min.css' %}">
|
||||||
|
<link rel="stylesheet" href="{% static 'bulma-prefers-dark.min.css' %}">
|
||||||
|
<meta http-equiv="refresh" content="60">
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: "Maven Pro";
|
||||||
|
src: url("{% static 'MavenPro-VariableFont:wght.ttf' %}");
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "Maven Pro", sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: #17181c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
{% for shift in shifts %}
|
||||||
|
<tr>
|
||||||
|
<td>{{shift.start_at}}</td>
|
||||||
|
<td>{{ shift.registration_count }}/{{ shift.required_helpers|default:shift.room.required_helpers }}</td>
|
||||||
|
<td>{{shift.room.name}} </td>
|
||||||
|
<td>{%for item in shift.teambackup_set.all%}<b>{{item.name}}</b>{%empty%} <div class="has-text-danger">unbesetzt</div> {%endfor%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends "signage_base.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<section class="section">
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
{% for shift in shifts %}
|
||||||
|
{%include 'partials/worklist_shift.html'%}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1,10 @@
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "signage"
|
||||||
|
urlpatterns = [
|
||||||
|
path("team/worklist", views.worklist, name="worklist"),
|
||||||
|
path("team/worklist/add/<int:shiftid>", views.add_teammember, name="work_add"),
|
||||||
|
path("team/terminal", views.terminal, name="terminal"),
|
||||||
|
]
|
|
@ -0,0 +1,58 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.db.models import F, Count, Q, ExpressionWrapper
|
||||||
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils import timezone
|
||||||
|
from .models import TeamBackup
|
||||||
|
from .models import Shift
|
||||||
|
from .forms import TeamBackupForm
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
@login_required
|
||||||
|
def worklist(request):
|
||||||
|
help_wanted = Q(required_helpers__gt=F("reg_count")) | Q(required_helpers=0) & Q(
|
||||||
|
room__required_helpers__gt=F("reg_count")
|
||||||
|
)
|
||||||
|
shifts = (
|
||||||
|
Shift.with_reg_count()
|
||||||
|
.filter(
|
||||||
|
help_wanted,
|
||||||
|
start_at__gt=timezone.now(),
|
||||||
|
)
|
||||||
|
.order_by("start_at", "room_id")
|
||||||
|
)
|
||||||
|
context = {}
|
||||||
|
context["shifts"] = shifts
|
||||||
|
context["form"] = TeamBackupForm()
|
||||||
|
return render(request, "worklist.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def add_teammember(request, shiftid):
|
||||||
|
if request.method != "POST":
|
||||||
|
return redirect("signage:worklist")
|
||||||
|
form = TeamBackupForm(request.POST)
|
||||||
|
if not form.is_valid():
|
||||||
|
return redirect("signage:worklist")
|
||||||
|
shift = get_object_or_404(Shift, pk=shiftid)
|
||||||
|
o = TeamBackup(shift=shift, name=form.cleaned_data["name"])
|
||||||
|
o.save()
|
||||||
|
return redirect("signage:worklist")
|
||||||
|
|
||||||
|
|
||||||
|
def terminal(request):
|
||||||
|
help_wanted = Q(required_helpers__gt=F("reg_count")) | Q(required_helpers=0) & Q(
|
||||||
|
room__required_helpers__gt=F("reg_count")
|
||||||
|
)
|
||||||
|
shifts = (
|
||||||
|
Shift.with_reg_count()
|
||||||
|
.filter(
|
||||||
|
help_wanted,
|
||||||
|
start_at__gt=timezone.now() - timedelta(minutes=20),
|
||||||
|
)
|
||||||
|
.order_by("start_at", "room_id")
|
||||||
|
)
|
||||||
|
context = {}
|
||||||
|
context["shifts"] = shifts
|
||||||
|
return render(request, "terminal.html", context)
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block title %}Schichtdetails{% endblock %}
|
{% block title %}Schichtdetails{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3 class="title">{% if shift.deleted %}(gelöscht) {% endif %}{{ shift.room.name }} {{ shift.start_at }} ({{ shift.shiftregistration_set.count }}/{{ shift.required_helpers|default:shift.room.required_helpers }})</h3>
|
<h3 class="title">{% if shift.deleted %}(gelöscht) {% endif %}{{ shift.room.name }} {{ shift.start_at }} ({{ shift.registration_count }}/{{ shift.required_helpers|default:shift.room.required_helpers }})</h3>
|
||||||
{% if shift.description %}
|
{% if shift.description %}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<em>{{ shift.description|linebreaksbr }}</em>
|
<em>{{ shift.description|linebreaksbr }}</em>
|
||||||
|
|
|
@ -22,4 +22,5 @@ urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("p/", include("shiftregister.pages.urls")),
|
path("p/", include("shiftregister.pages.urls")),
|
||||||
path("metrics/", include("shiftregister.metrics.urls")),
|
path("metrics/", include("shiftregister.metrics.urls")),
|
||||||
|
path("", include("shiftregister.signage.urls")),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue