Minimal working web app

This commit is contained in:
Reynir Björnsson 2020-04-13 13:59:45 +02:00
parent a7a50dcada
commit e542e3fdc0
8 changed files with 204 additions and 5 deletions

View file

@ -14,8 +14,9 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import include, path
urlpatterns = [
path('schedule/', include('schedule.urls')),
path('admin/', admin.site.urls),
]

10
schedule/converters.py Normal file
View file

@ -0,0 +1,10 @@
from datetime import date
class DateConverter:
regex = '[0-9]{4}-[0-9]{2}-[0-9]{2}'
def to_python(self, value):
return date.fromisoformat(value)
def to_url(self, value):
return value.isoformat()

View file

@ -0,0 +1,45 @@
# Generated by Django 3.0.5 on 2020-04-09 13:11
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Member',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('member_number', models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='aoff member number')),
],
),
migrations.CreateModel(
name='PickupDate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(verbose_name='date of pickup')),
],
),
migrations.CreateModel(
name='Timeslot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start', models.TimeField(verbose_name='start of timeslot')),
],
),
migrations.CreateModel(
name='Pickup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='schedule.PickupDate')),
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='schedule.Member')),
('time', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='schedule.Timeslot')),
],
),
]

View file

@ -0,0 +1,27 @@
# Generated by Django 3.0.5 on 2020-04-11 16:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('schedule', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='timeslot',
options={'ordering': ['start']},
),
migrations.AlterField(
model_name='member',
name='member_number',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='aoff member number'),
),
migrations.AlterField(
model_name='pickupdate',
name='date',
field=models.DateField(unique=True, verbose_name='date of pickup'),
),
]

View file

@ -3,16 +3,24 @@ from django.db import models
# Create your models here.
class PickupDate(models.Model):
date = models.DateField('date of pickup')
date = models.DateField('date of pickup', unique=True)
def __str__(self):
return str(self.date)
class Timeslot(models.Model):
start = models.TimeField('start of timeslot')
# open-ended for now
def __str__(self):
return self.start.isoformat(timespec='minutes')
class Meta:
ordering = ['start']
class Member(models.Model):
member_number = models.PositiveIntegerField(
verbose_name='aoff member number',
unique=True,
blank=True,
null=True,
)

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<title>AOFF</title>
<h1>Afhentningstidsplan</h1>
<p>For at minimere kødannelse bedes medlemmer der skal afhente grøntsager om at vælge hvornår de har tænkt sig at afhente sine grøntsager.
Det er hverken bindende eller et krav, men blot en hjælp til vores medlemmer så I kan planlægge afhentning når der er få der skal hente.</p>
<p>Det foregår anonymt - andre kan ikke se hvem der henter hvornår.
Vi gemmer en såkaldt 'cookie' i din browser med det eneste formål så hvis du kommer tilbage til siden senere i samme browser kan du opdatere eller fjerne dit valgte afhentningstidspunkt.
Bemærk at du <em>ikke</em> kan ændre afhentningstidspunkt fra en anden browser eller hvis cookien bliver fjernet.
Du kan skrive dit medlemsnummer for at hjælpe administratorer i at rette dine afhentningstidspunkter, men det er <em>ikke</em> et krav.</p>
<h2>Afhentning {{ date }}</h2>
<form action="{% url 'schedule:schedule' date.date %}" method="POST">
{% csrf_token %}
<label for="member_number">Medlemsnummer (ikke obligatorisk)</label>
<input min="0" type="number" placeholder="Mit medlemsnummer" name="member_number" id="member_number" />
<table>
<thead><tr>
<th>Tidspunkt</th>
<th>Registrerede afhentninger</th>
<th></th> {# Action #}
</tr>
</thead>
{% for timeslot in timeslots %}
<tr>
<td>{{ timeslot.start | time:"H:i" }}</td>
<td>{{ timeslot.sum }} {% if timeslot.own_pickup %}(inklusiv dig){% endif %}</td>
<td>{% if timeslot.own_pickup %}
<button name="timeslot" value="{{ timeslot.pk }}" formaction="{% url 'schedule:submit_pickup_removal' date.date %}">Fravælg</button>
{% else %}
<button name="timeslot" value="{{ timeslot.pk }}" formaction="{% url 'schedule:submit_pickup' date.date %}">Vælg</button>
{% endif %}
</tr>
{% endfor %}
</table>
</form>

12
schedule/urls.py Normal file
View file

@ -0,0 +1,12 @@
from django.urls import path, register_converter
from . import views, converters
register_converter(converters.DateConverter, 'date')
app_name = 'schedule'
urlpatterns = [
path('<date:date>/', views.schedule, name='schedule'),
path('<date:date>/submit', views.submit_pickup, name='submit_pickup'),
path('<date:date>/remove', views.submit_pickup_removal, name='submit_pickup_removal'),
]

View file

@ -1,3 +1,62 @@
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from .models import *
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse
# Create your views here.
def schedule(request, date):
date = get_object_or_404(PickupDate, date=date)
member_number = request.POST and request.POST.get('member_number') \
or None
member_id = request.session.get('member_id')
if member_id is not None:
member = get_object_or_404(Member, pk=member_id)
elif member_number:
member = Member()
request.session['member_id'] = member.id
request.session.set_expiry(0)
else:
member = None
if member_number and member.member_number != member_number:
member.member_number = member_number
member.save()
timeslots = Timeslot.objects.all()
for timeslot in timeslots:
timeslot.sum = len(Pickup.objects.filter(time=timeslot))
if member:
try:
timeslot.own_pickup = \
Pickup.objects.filter(
date=date,
time=timeslot,
member=member,
).exists()
except Pickup.DoesNotExist:
timeslot.own_pickup = False
else:
timeslot.own_pickup = False
return render(request, 'schedule/schedule.html',
{ 'date': date,
'timeslots': timeslots,
})
def submit_pickup(request, date):
date = get_object_or_404(PickupDate, date=date)
request.session.set_expiry(0)
member_id = request.session.get('member_id')
if not member_id:
member = Member(member_number=request.POST.get('member_number'))
member.save()
request.session['member_id'] = member.id
else:
member = get_object_or_404(Member, pk=member_id)
timeslot = get_object_or_404(Timeslot, pk=request.POST.get('timeslot'))
Pickup(date=date, time=timeslot, member=member).save()
return HttpResponseRedirect(reverse('schedule:schedule', args=(date.date,)))
def submit_pickup_removal(request, date):
date = get_object_or_404(PickupDate, date=date)
member = get_object_or_404(Member, pk=request.session.get('member_id'))
timeslot = get_object_or_404(Timeslot, pk=request.POST.get('timeslot'))
Pickup.objects.filter(date=date, time=timeslot, member=member).delete()
return HttpResponseRedirect(reverse('schedule:schedule', args=(date.date,)))