This commit is contained in:
root (lxc-fetsite-04)
2020-10-05 21:57:17 +02:00
33 changed files with 543 additions and 173 deletions

View File

@@ -28,6 +28,9 @@ def authentication(username, password):
except LDAPBindError as e:
logger.info('Username does not exist. Error: {}'.format(e))
username = None
except Exception as e:
logger.info('Connection to server lost. Error: {}'.format(e))
username = None
if not has_user:
username = None

View File

@@ -0,0 +1,6 @@
from django import forms
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(label='Passwort', widget=forms.PasswordInput())

View File

@@ -3,9 +3,11 @@ from django.contrib.auth import login, logout
from django.contrib import messages
from django.contrib.auth.models import User
from documents.etherpadlib import del_ep_cookie
from .authentications import authentication
from .decorators import unauthenticated_user, authenticated_user
from documents.etherpadlib import del_ep_cookie
from .forms import LoginForm
@unauthenticated_user
@@ -27,7 +29,11 @@ def loginPage(request):
else:
messages.info(request, 'username or password is incorrect')
context = {}
form = LoginForm()
context = {
"form": form,
}
return render(request, 'authentications/login.html', context)

View File

View File

@@ -0,0 +1,21 @@
from django.contrib import admin
from .models import JobPosting
class JobPostingAdmin(admin.ModelAdmin):
model = JobPosting
fieldsets = (
(None, {
'fields': (
'companyName',
'jobName',
'salary',
'pdfLocation',
'publishDate',
)
}),
)
admin.site.register(JobPosting, JobPostingAdmin)

View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class BlackboardConfig(AppConfig):
name = 'blackboard'

View File

@@ -0,0 +1,76 @@
from django.conf import settings
from django.core.validators import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
import locale
import os
from os.path import splitext, basename
import ghostscript
import logging
logger = logging.getLogger('blackboard')
class JobPosting(models.Model):
companyName = models.CharField(max_length=128)
jobName = models.CharField(max_length=128)
salary = models.PositiveSmallIntegerField()
pdfLocation = models.FileField(upload_to='uploads/blackboard/pdf/')
pdf_thumb_location = models.CharField(max_length=128)
publishDate = models.DateField('date published', default=timezone.now)
# Managers
all_jobPosting = models.Manager()
class Meta:
verbose_name = "Stellenausschreibung"
verbose_name_plural = "Stellenausschreibungen"
def __str__(self):
return str(self.publishDate) + "_" + self.companyName + "_" + self.jobName
def pdf2jpeg(self, pdf_input_path, jpeg_output_path):
args = [
"pef2jpeg", # actual value doesn't matter
"-dNOPAUSE",
"-sDEVICE=jpeg",
"-dDEVICEWIDTHPOINTS=600",
"-dDEVICEHEIGHTPOINTS=800",
"-sOutputFile=" + jpeg_output_path,
pdf_input_path
]
encoding = locale.getpreferredencoding()
args = [a.encode(encoding) for a in args]
ghostscript.Ghostscript(*args)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not os.path.exists(settings.MEDIA_ROOT + "uploads/blackboard/thumb/"):
os.makedirs(settings.MEDIA_ROOT + "uploads/blackboard/thumb/")
pdf_thumb_location_full = settings.MEDIA_ROOT \
+ "uploads/blackboard/thumb/" \
+ splitext(basename(self.pdfLocation.name))[0] \
+ ".jpg"
self.pdf_thumb_location = "/files/uploads/blackboard/thumb/" \
+ splitext(basename(self.pdfLocation.name))[0] \
+ ".jpg"
self.pdf2jpeg(self.pdfLocation.path, pdf_thumb_location_full)
logger.info("SavenThumbAs: " + self.pdf_thumb_location)
super().save(*args, **kwargs)
def clean(self):
count = 0
for i in self.pdfLocation.name:
if i == '.':
count = count + 1
if count > 1: # if more than one dot in filename
raise ValidationError(_('Keine Dateien mit >1 Punkten im Namen erlaubt.'))

View File

@@ -0,0 +1,3 @@
# from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,8 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='blackboard'),
]

View File

@@ -0,0 +1,14 @@
from django.shortcuts import render
from django.utils import timezone
from datetime import timedelta
from .models import JobPosting
def index(request):
jobPost_cutoff = timezone.now().date() - timedelta(30) # 30days from now
context = {
"jobPostings": JobPosting.all_jobPosting.filter(publishDate__gt=jobPost_cutoff),
}
return render(request, 'blackboard/index.html', context)

View File

@@ -17,7 +17,7 @@ env = environ.Env(
DEBUG=(bool, True),
MYSQL_HOST=(str, "mysql"),
MYSQL_PORT=(int, 3308),
MYSQL_DATABASE=(str,"fet2020db"),
MYSQL_DATABASE=(str, "fet2020db"),
MYSQL_USER=(str),
MYSQL_PASSWORD=(str)
)
@@ -50,7 +50,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG')
if DEBUG:
# SECURITY WARNING: keep the secret key used in production secret!
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns'
else:
SECRET_KEY = env('SECRET_KEY')
@@ -81,6 +81,8 @@ INSTALLED_APPS = [
'posts.apps.PostsConfig',
'members.apps.MembersConfig',
'documents.apps.DocumentsConfig',
'blackboard.apps.BlackboardConfig',
'tasks.apps.TasksConfig',
]
MIDDLEWARE = [
@@ -121,21 +123,21 @@ WSGI_APPLICATION = 'fet2020.wsgi.application'
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
if DEBUG:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': env('MYSQL_DATABASE'),
'USER': env('MYSQL_USER'),
'PASSWORD':env('MYSQL_PASSWORD'),
'HOST': env('MYSQL_HOST'),
'PORT':env('MYSQL_PORT')
}
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': env('MYSQL_DATABASE'),
'USER': env('MYSQL_USER'),
'PASSWORD': env('MYSQL_PASSWORD'),
'HOST': env('MYSQL_HOST'),
'PORT': env('MYSQL_PORT')
}
}

View File

@@ -38,4 +38,6 @@ urlpatterns = [
path('ckeditor/', include('ckeditor_uploader.urls')),
path('api/', include(router.urls)),
path('members/', include('members.urls'), name='members'),
path('blackboard/', include('blackboard.urls'), name='blackboard'),
path('tasks/', include('tasks.urls'), name='tasks'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -26,11 +26,19 @@ def index(request):
# remove the pinned post
posts.remove(featured_post)
featured_event = Event.only_events.get_future_events().first()
# if there is no futurity event
if not featured_event:
featured_event = Event.only_events.get_past_events().first()
featured_meeting = FetMeeting.objects.get_meetings()
context = {
'posts': posts,
'events': Event.all_events.get_five_events(),
'featured_post': featured_post,
'featured_meeting': FetMeeting.objects.get_meetings(),
'featured_event': featured_event,
'featured_meeting': featured_meeting,
'tags_list': ", ".join(t)
}

View File

@@ -158,6 +158,12 @@ class JobGroup(models.Model):
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self.shortterm
super().save(*args, **kwargs)
class Job(models.Model):
name = models.CharField(max_length=128)

View File

@@ -5,7 +5,7 @@ from django.utils import timezone
class PostManager(models.Manager):
def get_queryset(self):
return super().get_queryset()
return super().get_queryset().order_by('-public_date')
def get_visible_articles(self):
return self.get_queryset().filter(is_hidden=False)
@@ -34,7 +34,7 @@ class NewsManager(models.Manager):
Provide a query set only for "News"
"""
def get_queryset(self):
return super().get_queryset().filter(post_type='N')
return super().get_queryset().filter(post_type='N').order_by('-public_date')
def get_visible_articles(self):
return self.get_queryset().filter(is_hidden=False)
@@ -48,7 +48,8 @@ class AllEventManager(models.Manager):
return super().get_queryset().filter(Q(post_type='E') | Q(post_type='F'))
def get_five_events(self):
return self.get_queryset().order_by('-event_start')[:5]
date_today = timezone.now()
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')[:5]
class EventManager(models.Manager):
@@ -59,6 +60,14 @@ class EventManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(post_type='E')
def get_future_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')
def get_past_events(self):
date_today = timezone.now()
return self.get_queryset().filter(event_start__lt=date_today).order_by('-event_start')
class FetMeetingManager(models.Manager):
"""

View File

@@ -219,14 +219,6 @@ class Event(Post):
verbose_name = "Event"
verbose_name_plural = "Events"
@property
def event_start_month(self):
return self.event_start.strftime("%b")
@property
def event_start_day(self):
return self.event_start.strftime("%d")
def clean(self):
if self.event_end is None or self.event_start is None:
raise ValidationError(_('Das Datum des Events fehlt.'))

View File

@@ -1,9 +1,10 @@
django==3.1.*
django-taggit==1.3.0
django-ckeditor==5.9.0
Pillow==7.2.0
djangorestframework==3.11.0
django-ckeditor==6.0.0
django-environ==0.4.5
django-filter==2.3.0
django-static-jquery-ui==1.12.1.1
django-taggit==1.3.0
djangorestframework==3.12.1
docutils==0.16
easy-thumbnails==2.7.0
etherpad-lite==0.5
@@ -11,3 +12,7 @@ django-filter
ldap3
django-environ
mysql-python
ldap3==2.8.1
#mysqlclient==2.0.1
Pillow==7.2.0
ghostscript==0.6

View File

33
fet2020/tasks/admin.py Normal file
View File

@@ -0,0 +1,33 @@
from django.contrib import admin
from .models import Task, TaskList
class TaskAdmin(admin.ModelAdmin):
model = Task
fieldsets = (
(None, {
'fields': (
'title',
'task_list',
'due_date',
'completed',
'completed_date',
'assigned_to',
'note',
'priority',
)
}),
)
list_display = ('title', 'task_list', 'completed', 'priority', 'due_date')
list_filter = ('task_list', )
ordering = ('priority', )
search_fields = ('title', )
def save_model(self, request, obj, form, change):
obj.created_by = request.user
super().save_model(request, obj, form, change)
admin.site.register(TaskList)
admin.site.register(Task, TaskAdmin)

5
fet2020/tasks/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class TasksConfig(AppConfig):
name = 'tasks'

15
fet2020/tasks/forms.py Normal file
View File

@@ -0,0 +1,15 @@
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = [
'title',
'task_list',
'due_date',
'assigned_to',
]

75
fet2020/tasks/models.py Normal file
View File

@@ -0,0 +1,75 @@
from django.conf import settings
from django.db import models
from django.db.models import Q
from django.utils import timezone
class TaskQuerySet(models.QuerySet):
def get_ordered(self):
return self.order_by('task_list')
class TaskManager(models.Manager):
def get_tasks(self, user, completed):
if completed:
return self.get_queryset().get_ordered().filter(Q(assigned_to=user))
else:
return self.get_queryset().get_ordered().filter(
Q(assigned_to=user)
& Q(completed=completed)
)
def get_queryset(self):
return TaskQuerySet(self.model, using=self._db)
class TaskList(models.Model):
name = models.CharField(max_length=60)
slug = models.SlugField(unique=True, null=True, blank=True)
objects = models.Manager()
def __str__(self):
return self.name
class Task(models.Model):
title = models.CharField(max_length=140)
task_list = models.ForeignKey(TaskList, on_delete=models.CASCADE, null=True)
created_date = models.DateTimeField(auto_now_add=True)
due_date = models.DateField(blank=True, null=True)
completed = models.BooleanField(default=False)
completed_date = models.DateField(blank=True, null=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="todo_created_by",
on_delete=models.CASCADE,
)
assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
related_name="todo_assigned_to",
on_delete=models.CASCADE,
)
note = models.TextField(blank=True, null=True)
priority = models.PositiveIntegerField(blank=True, null=True)
objects = models.Manager()
taskmanager = TaskManager()
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if self.completed and not self.completed_date:
self.completed_date = timezone.now().date()
if not self.completed and self.completed_date:
self.completed_date = None
super().save(*args, **kwargs)

3
fet2020/tasks/tests.py Normal file
View File

@@ -0,0 +1,3 @@
# from django.test import TestCase
# Create your tests here.

8
fet2020/tasks/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='tasks'),
]

56
fet2020/tasks/views.py Normal file
View File

@@ -0,0 +1,56 @@
from django.contrib.auth.models import User
from django.shortcuts import render
from django.utils import timezone
from collections import deque
from .forms import TaskForm
from .models import Task
def index(request):
current_user = request.user.id
current_action = False
if request.method == 'POST':
if 'btn_input' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
task = form.save(commit=False)
task.created_by = request.user
task.save()
elif 'btn_checkbox' in request.POST:
for task_id in request.POST.getlist('checkbox'):
task = Task.objects.get(id=task_id)
if not task.completed:
task.completed = True
task.completed_date = timezone.now().date()
task.save()
elif 'btn_user' in request.POST:
if request.POST['action'] == 'show_incompleted':
current_action = False
else:
current_action = True
if request.POST['user'] == 'all':
current_user = None
else:
current_user = int(request.POST['user'])
form = TaskForm()
tasks = deque(Task.taskmanager.get_tasks(user=current_user, completed=current_action))
users = User.objects.all()
context = {
"form": form,
"tasks": tasks,
"users": users,
"current_user": current_user,
"current_action": current_action,
}
return render(request, 'tasks/index.html', context)

View File

@@ -1,123 +1,23 @@
<!DOCTYPE html>
<html>
{% extends 'layout.html' %}
{% block content %}
<head>
<title>Login</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous">
<div class="grid-container">
<div class="grid-x">
<form action="" method="post">
<h3>Anmeldung für FET-Mitarbeiter</h3>
{% csrf_token %}
{{ form }}
<input type="submit" class="button" name="btn_input" value="Anmelden">
<style>
body,
html {
margin: 0;
padding: 0;
height: 100%;
background: #7abecc !important;
}
.user_card {
width: 350px;
margin-top: auto;
margin-bottom: auto;
background: #74cfbf;
position: relative;
display: flex;
justify-content: center;
flex-direction: column;
padding: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-webkit-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-moz-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
border-radius: 5px;
{% for message in messages %}
<p id="messages">{{message}}</p>
{% endfor %}
}
</form>
.form_container {
margin-top: 20px;
}
</div>
#form-title{
color: #fff;
</div>
}
.login_btn {
width: 100%;
background: #33ccff !important;
color: white !important;
}
.login_btn:focus {
box-shadow: none !important;
outline: 0px !important;
}
.login_container {
padding: 0 2rem;
}
.input-group-text {
background: #f7ba5b !important;
color: white !important;
border: 0 !important;
border-radius: 0.25rem 0 0 0.25rem !important;
}
.input_user,
.input_pass:focus {
box-shadow: none !important;
outline: 0px !important;
}
#messages{
background-color: grey;
color: #fff;
padding: 10px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container h-100">
<div class="d-flex justify-content-center h-100">
<div class="user_card">
<div class="d-flex justify-content-center">
<h3 id="form-title">LOGIN</h3>
</div>
<div class="d-flex justify-content-center form_container">
<form method="POST" action="">
{% csrf_token %}
<div class="input-group mb-3">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<input type="text" name="username" placeholder="Username..." class="form-control">
</div>
<div class="input-group mb-2">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
<input type="password" name="password" placeholder="Password..." class="form-control" >
</div>
<div class="d-flex justify-content-center mt-3 login_container">
<input class="btn login_btn" type="submit" value="Login">
</div>
</form>
</div>
{% for message in messages %}
<p id="messages">{{message}}</p>
{% endfor %}
</div>
</div>
</div>
</div>
</body>
</html>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends 'layout.html' %}
{% block content %}
<div class="grid-container">
<h1>Blackboard</h1>
<!-- Tab panes -->
<div class="tabs-content">
{% for job in jobPostings %}
{% include 'blackboard/partials/_jobPosting.html' %}
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,18 @@
<div class="grid-x">
<div class="medium-3 large-2 small-6 cell">
<h2>{{job.companyName}}</h2>
<p>{{job.jobName}}<br>
Mindestgehalt: {{job.salary}}€</p>
{# only thumb and name of member #}
<a class="thumbnail member-thumb" href="{{job.pdfLocation.url}}" style="width:200px;height:300px">
<img src="{{job.pdf_thumb_location}}" alt="" />
<div class="thumb-layer">
<div>
<h1>{{job.companyName}}</h1>
<p>{{job.jobName}} <br>
Mindestgehalt: {{job.salary}}€</p>
</div>
</div>
</a>
</div>
</div>

View File

@@ -29,7 +29,7 @@
<div class="medium-4 responsive-side-box cell">
<a href="{% url 'posts.index' %}"><h1>Neuigkeiten</h1></a>
<div class="article-row-section">
{% with post=featured_post %}
{% with post=featured_event %}
{% include 'posts/partials/_article_row.html' %}
{% endwith %}
{% for post in featured_meeting %}

View File

@@ -23,12 +23,14 @@
{% if request.user.is_authenticated %}
Hallo {{request.user.username}}
<li class=""><a href="/admin">Admin</a></li>
<li class=""><a href="{%url 'tasks'%}">Tasks</a> </li>
<li class=""><a href="{%url 'logout'%}">Logout</a> </li>
{% else %}
<li class=""><a href="{%url 'login'%}">Login</a> </li>
{% endif %}
<li class=""><a href="{%url 'home'%}">Aktuelles</a> </li>
<li class=""><a href="/fotos">Fotos</a> </li>
<li class=""><a href="/blackboard">Blackboard</a> </li>
<li class=""><a href="{%url 'members'%}">Mitarbeiter</a> </li>
</ul>
</div>

View File

@@ -6,18 +6,19 @@
<h1>Mitglieder / Tätigkeiten</h1>
<!-- Nav tabs -->
<ul class="nav nav-tabs nav-fill">
<a class="active" id="members" data-toggle="tab" href="/members">Mitglieder</a>
<a id="members" data-toggle="tab" href="/members/A">Aktive Mitglieder</a>
<a id="members" data-toggle="tab" href="/members/P">Inaktive Mitglieder</a>
<div class="tabs.simple">
<a href="/members">Mitglieder</a>
<a href="/members/A">Aktive Mitglieder</a>
<a href="/members/P">Inaktive Mitglieder</a>
{% for job in job_group %}
<a id="jobs-{{job.slug}}" data-toggle="tab" href="/members/jobs/{{job.slug}}">{{job.name}}</a>
<a href="/members/jobs/{{job.slug}}">{{job.name}}</a>
{% endfor %}
</ul>
</div>
<!-- Tab panes -->
<div class="tab-content" id="nav-tabContent">
<div class="tabs-content">
{% for mem in member %}
{% with member=mem %}
{% include 'members/partials/_member_details.html' %}
@@ -25,27 +26,21 @@
{% endfor %}
{% if members is not None %}
<div id="members" class="tab-pane">
{% include 'members/members_list.html' %}
</div>
{% include 'members/members_list.html' %}
{% endif %}
{% for job in job_list %}
<div id="jobs" class="tab-pane">
<div class="grid-container">
<div class="tabs-content" id="{{job.3}}">
<div id="{{job.3}}">
<h2>{{job.0}}<a class="headerlink" href="#{{job.3}}" title="Permalink to {{job.0}}"> #</a></h2>
<h2>{{job.0}}<a class="headerlink" href="#{{job.3}}" title="Permalink to {{job.0}}"> #</a></h2>
{% with active_members=job.1 inactive_members=job.2 %}
{% include 'members/jobs_list.html' %}
{% endwith %}
</div>
{% with active_members=job.1 inactive_members=job.2 %}
{% include 'members/jobs_list.html' %}
{% endwith %}
</div>
{% endfor %}
</div>
</div>

View File

@@ -5,8 +5,8 @@
{% if post.subtitle is not None %}
<p class="article-row-content-description">{{post.subtitle}}</p>
{% endif %}
<p class="article-row-content-author">{{post.author}}</p>
<time class="article-row-content-time" datetime="2008-02-14 20:00">{{post.public_date}}</time>
<!--<p class="article-row-content-author">{{post.author}}</p>-->
<time class="article-row-content-time">{{post.event_start|date}}</time>
</div>
</article>
</a>

View File

@@ -0,0 +1,78 @@
{% extends 'layout.html' %}
{% block content %}
<div class="grid-container">
<h1>Tasks</h1>
<div class="grid-x">
<form action="" method="post">
{% csrf_token %}
<label>User
<select id="id_user" name="user">
<option value="all">
Alle
</option>
{% for user in users %}
<option value="{{ user.id }}" {% if current_user == user.id %} selected {% endif %}>
{{ user.username }}
</option>
{% endfor %}
</select>
</label>
<label>Aktion
<select id="id_action" name="action">
<option value="show_incompleted" {% if not current_action %} selected {% endif %}>unvollständige Tasks</option>
<option value="show_all" {% if current_action %} selected {% endif %}>alle Tasks</option>
</select>
</label>
<input type="submit" class="button" name="btn_user" value="Anzeigen">
</form>
</div>
<div class="grid-x">
<form action="" method="post">
{% csrf_token %}
{% regroup tasks by task_list as section_list %}
{% for group in section_list %}
<div class="cell">
<ul class="no-bullet">
<h3>{{ group.grouper }}</h3>
{% for task in group.list %}
<input type="checkbox" name="checkbox" value="{{ task.id }}" {% if task.completed %} checked {% endif %}>
{{ task.title }}</a>
{{ task.due_date|date:"d.m.Y" }}
<br>
{% endfor %}
</div>
{% endfor %}
<input type="submit" class="button" name="btn_checkbox" value="Task abschließen">
</form>
</div>
<div class="grid-x">
<form action="" method="post">
<h2>neue Tasks hinzufügen</h2>
{% csrf_token %}
{{ form }}
<input type="submit" class="button" name="btn_input" value="Hinzufügen">
</form>
</div>
</div>
{% endblock %}