Commit 544cbb39 authored by dorothee.kueppers's avatar dorothee.kueppers
Browse files

small fixes:

- todos bind to user
- delete selection
- recurring todos get incompleted again
- email von registration to console
parent bebf315d
......@@ -37,7 +37,7 @@ Zur Sortierung wird der jeweils als nächstes anstehende Termin ermittelt, wodur
Installation:
echo 'DATABASE_URL=sqlite:///db.sqlite3' > .env
echo DATABASE_URL=sqlite:///db.sqlite3 > .env
im Hauptverzeichnis ausführen
......
......@@ -38,7 +38,6 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django_crontab',
'django.contrib.admin',
'django.contrib.sites',
'django.contrib.auth',
......@@ -142,7 +141,6 @@ USE_L10N = True
USE_TZ = True
RECURRENCE_USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
......@@ -151,10 +149,15 @@ STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
CRONJOBS = [
('0 8 * * *', 'todo.cron.email_todays_todos')
('*/1 * * * *', 'todo.cron.undo_complete_repetitive_tasks'),
]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Activate Django-Heroku.
django_heroku.settings(locals())
......
......@@ -23,7 +23,7 @@ from django.urls import path, include
from todo import views
from todo.views import get_landing_page, get_selection_page, todo_details, get_impressum, get_show_todo, \
get_archiv, get_selection_id, delete_todo, redirect_profile, get_add_category
get_archiv, get_selection_id, delete_todo, redirect_profile, get_add_category, delete_selection
urlpatterns = [
path('admin/', admin.site.urls),
......@@ -32,13 +32,14 @@ urlpatterns = [
path('', get_landing_page, name='landing_page'),
path('selection/', get_selection_page, name='selection_page'),
path('selection/<int:pk>/', get_selection_id, name='selection_id'),
path('selection/delete/<int:pk>/', delete_selection, name='delete_selection'),
path('todo/add/', todo_details, name='add_todo'),
path('todo/edit/<int:pk>/', todo_details, name='edit_todo'),
path('todo/delete/<int:pk>/', delete_todo, name='delete_todo'),
path('todo/delete/<int:pk>/<str:source>/<int:s_id>', delete_todo, name='delete_todo_selection'),
path('impressum/', get_impressum, name='impressum'),
path('todo/show/', get_show_todo, name='show_todo'),
path('todo/archiv/', get_archiv, name='archiv'),
path('impressum/', get_impressum, name='impressum'),
path('accounts/', include('allauth.urls')),
path('accounts/profile/', redirect_profile, name="redirect_profile"),
path('contact/', include('contactforms.urls')),
......
......@@ -5,11 +5,9 @@ asgiref==3.2.9
Babel==2.8.0
beautifulsoup4==4.9.1
billiard==3.3.0.23
celery==3.1.26.post2
certifi==2020.4.5.2
chardet==3.0.4
colorama==0.4.3
crontab==0.22.8
defusedxml==0.6.0
dj-database-url==0.5.0
Django==3.0.7
......
......@@ -34,9 +34,6 @@
<li class="nav-item active">
<a class="nav-link" href="{% url 'landing_page' %}">Start<span class="sr-only">(current)</span></a>
</li>
{# <li class="nav-item active">#}
{# <a class="nav-link" href="{% url 'selection_page' %}">Aufgabenliste<span class="sr-only">(current)</span></a>#}
{# </li>#}
<li class="nav-item active">
<a class="nav-link" href="{% url 'add_todo' %}">neue Aufgabe <span class="sr-only">(current)</span></a>
</li>
......@@ -50,6 +47,7 @@
</ul>
</div>
<!-- Navbar auf rechter Seite/Log In und Register Seite-->
<a class="nav-brand">Hallo {{ username }}!</a>
<div class="navbar-nav">
<a class="nav-item nav-link active" href="{% url 'account_logout' %}">Logout</a>
</div>
......@@ -72,7 +70,7 @@
{% block footer %}
<nav class="navbar fixed-bottom navbar-light bg-light">
<a class="navbar-brand" href="{% url 'impressum' %}">Impressum</a>
<a class="navbar-brand" href="{% url 'contact' %}">Kontakt</a>
<a class="navbar-brand" href="{% url 'contact' %}">Kontakt</a>
</nav>
<script type='text/javascript' src={% static 'js/selectionpage.js' %}></script>
<script type='text/javascript' src={% static 'js/showtodos.js' %}></script>
......
......@@ -3,7 +3,7 @@
{% block content %}
{% if not running_selection %}
<div class="row justify-content-center m-2">
<div class="card">
<div class="card full-width">
<div class="card-body">
<h5 class="card-title">Lege eine neue Aufgabenliste an</h5>
<p class="card-text">Du hast gerade Zeit, um ein paar Dinge zu erledigen? Super! Dann such dir aus deiner Liste die wichtigsten Dinge aus,
......@@ -16,7 +16,7 @@
{% endif %}
{% if running_selection %}
<div class="row justify-content-center m-2">
<div class="card">
<div class="card full-width">
<div class="card-body">
<h5 class="card-title">Schaue dir deine aktuelle Aufgabenliste an</h5>
<p class="card-text">Hier findest du die Aufgaben, die du jetzt gerade erledigen möchtest!</p>
......@@ -27,17 +27,17 @@
</div>
{% endif %}
<div class="row justify-content-center m-2">
<div class="card">
<div class="card full-width">
<div class="card-body">
<h5 class="card-title">Lege eine neue Aufgabe an</h5>
<p class="card-text">Leg eine neue Aufgabe an! Sie kann sich wiederholen, einen fixen Endtermin haben oder einfach etwas sein,
was du mal erreichen oder erledigen möchtest. Denk auch dran, dir mal ein paar Pausen einzuplanen ;)</p>
<a class="btn btn-primary btn-lg float-right" href="{% url 'add_todo' %}">Neue Aufgabe Anlegen</a>
<a class="btn btn-primary btn-lg float-right" href="{% url 'add_todo' %}">Neue Aufgabe anlegen</a>
</div>
</div>
</div>
<div class="row justify-content-center m-2">
<div class="card">
<div class="card full-width">
<div class="card-body">
<h5 class="card-title">Zeige alle Aufgaben an</h5>
<p class="card-text">Was wolltest du nochmal alles machen / erledigen / erreichen? Schaus dir hier an! Oder hast du gerade
......@@ -47,7 +47,7 @@
</div>
</div>
<div class="row justify-content-center m-2">
<div class="card">
<div class="card full-width">
<div class="card-body">
<h5 class="card-title">Schaue dir alte Aufgaben an</h5>
<p class="card-text">Was hattest du letztens nochmal erledigt und wie lang hatte das so gedauert?
......
......@@ -80,7 +80,7 @@
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo" data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
{% if todos_today %}
......@@ -109,7 +109,7 @@
</button>
</h5>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
<div id="collapseThree" class="collapse show" aria-labelledby="headingThree"
data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
......@@ -139,7 +139,7 @@
</button>
</h5>
</div>
<div id="collapseFour" class="collapse" aria-labelledby="headingFour"
<div id="collapseFour" class="collapse show" aria-labelledby="headingFour"
data-parent="#accordion">
<div class="card-body">
<ul class="list-group list-group-flush">
......
......@@ -9,6 +9,7 @@
{{ form }}
<input type="submit" class="btn btn-primary" value="Änderungen speichern">
</form>
<a class="btn btn-outline-danger" href="{% url 'delete_selection' pk=pk %}">Aufgabenliste löschen</a>
</div>
<div class="row justify-content-center">
<div id="accordion">
......@@ -63,7 +64,7 @@
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo" data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
{% if todos_today %}
......@@ -103,7 +104,7 @@
</button>
</h5>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
<div id="collapseThree" class="collapse show" aria-labelledby="headingThree"
data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
......@@ -144,7 +145,7 @@
</button>
</h5>
</div>
<div id="collapseFour" class="collapse" aria-labelledby="headingFour"
<div id="collapseFour" class="collapse show" aria-labelledby="headingFour"
data-parent="#accordion">
<div class="card-body">
<ul class="list-group list-group-flush">
......@@ -155,12 +156,12 @@
<div class="todo-text">{{ todo.text }}</div>
<div class="todo-duration">{{ todo.duration }} Minuten</div>
<div class="todo-edit">
<a href="{% url 'edit_todo' pk=todo.0.id %}">
<a href="{% url 'edit_todo' pk=todo.id %}">
<span class="material-icons">edit</span>
</a>
</div>
<div class="todo-delete">
<a href="{% url 'delete_todo_selection' pk=todo.0.id source='selection_id' s_id=pk %}">
<a href="{% url 'delete_todo_selection' pk=todo.id source='selection_id' s_id=pk %}">
<span class="material-icons">delete</span>
</a>
</div>
......
......@@ -63,7 +63,7 @@
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo" data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
{% if todos_today %}
......@@ -103,7 +103,7 @@
</button>
</h5>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
<div id="collapseThree" class="collapse show" aria-labelledby="headingThree"
data-parent="#accordion">
<div class="card-body">
<div class="list-group list-group-flush">
......@@ -144,7 +144,7 @@
</button>
</h5>
</div>
<div id="collapseFour" class="collapse" aria-labelledby="headingFour"
<div id="collapseFour" class="collapse show" aria-labelledby="headingFour"
data-parent="#accordion">
<div class="card-body">
<ul class="list-group list-group-flush">
......
from post_office import mail
from django.core.mail import send_mail
def email_todays_todos():
send_mail('Hello from Feierabend',
'Mail gesendet',
'feierabend-todo@gmx.de',
['DoroKueppers@web.de'],
fail_silently=False)
......@@ -11,7 +11,7 @@ from django import forms
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
exclude =[]
exclude = []
class CreateUserForm(UserCreationForm):
......@@ -23,11 +23,8 @@ class CreateUserForm(UserCreationForm):
class TodoForm(forms.ModelForm):
class Meta:
model = Todo
exclude = ['created', 'complete', 'rank', 'selection', 'finished']
widgets = {
'text': forms.TextInput(attrs={'placeholder': 'Küche putzen, Python lernen, Haare waschen...'}),
# 'recurrences': RecurrenceField()
}
exclude = ['created', 'complete', 'rank', 'selection', 'finished', 'user_id', 'completed_until']
widgets = {'text': forms.TextInput(attrs={'placeholder': 'Küche putzen, Python lernen, Haare waschen...'}),}
labels = {'text': 'Was ist zu tun? *',
'duration': 'Was meinst du, wie lange die Aufgabe dauert? (in Minuten) *',
'recurrences': 'Wiederholungen',
......
# Generated by Django 3.0.7 on 2020-07-14 04:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('todo', '0004_auto_20200713_1725'),
]
operations = [
migrations.AddField(
model_name='todo',
name='user_id',
field=models.IntegerField(default=1),
preserve_default=False,
),
]
# Generated by Django 3.0.7 on 2020-07-14 04:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('todo', '0005_todo_user_id'),
]
operations = [
migrations.AddField(
model_name='selection',
name='user_id',
field=models.IntegerField(default=1),
preserve_default=False,
),
]
# Generated by Django 3.0.7 on 2020-07-14 05:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('todo', '0006_selection_user_id'),
]
operations = [
migrations.AddField(
model_name='todo',
name='completed_until',
field=models.DateField(null=True),
),
]
......@@ -2,36 +2,28 @@ import datetime
from django.db import models
from recurrence.fields import RecurrenceField
from django import forms
#TODO_CHOICES= [
# ('Malen',),
# ('Schwimmen', 'Lesen'),
# ('Lernen', 'Fotografieren'),
#('Einkaufen', 'Spielen'),
#]
class Selection(models.Model):
total_time = models.IntegerField(default=0) # in minutes
active = models.BooleanField(default=False)
started = models.DateTimeField(default=datetime.datetime.today)
endtime = models.DateTimeField()
#message = models.CharField("Event Type", max_length=12, choices = TODO_CHOICES)
user_id = models.IntegerField()
def __str__(self):
return f'Aufgabenliste {self.pk}, Gesamtdauer: {self.total_time.__str__()}'
class Category(models.Model):
title = models.CharField(max_length=200)
class Category(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
def __str__(self):
return self.title
class Todo(models.Model):
#text = models.CharField(max_length=200)
text=models.CharField(max_length=256)
text = models.CharField(max_length=256)
complete = models.BooleanField(default=False)
duration = models.IntegerField(default=15)
created = models.DateField(default=datetime.date.today)
......@@ -39,7 +31,8 @@ class Todo(models.Model):
recurrences = RecurrenceField(blank=True, null=True)
selection = models.ForeignKey(Selection, on_delete=models.SET_NULL, null=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
user_id = models.IntegerField()
completed_until = models.DateField(null=True)
def __str__(self):
return self.text
......@@ -105,6 +105,6 @@ div.form-control.recurrence-widget {
width: fit-content;
}
#show-todos #accordion {
#show-todos #accordion, .full-width {
width: -webkit-fill-available;
}
\ No newline at end of file
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect
from django.shortcuts import render
from django.urls import reverse_lazy
from django.utils import timezone
from django.views.generic import CreateView
from todo.forms import *
from todo.models import *
......@@ -26,26 +25,33 @@ def get_add_category(request):
else:
form = CategoryForm(instance=category)
return render(request, 'todo/add_category.html', {'page_title': 'Füge eine neue Kategorie hinzu! ',
'form': form})
content = {'page_title': 'Füge eine neue Kategorie hinzu! ',
'form': form,
'username': request.user.username}
return render(request, 'todo/add_category.html', content)
@login_required()
def get_landing_page(request):
user_id = request.user.id
try:
selection = Selection.objects.filter(active=True).latest('endtime')
selection = Selection.objects.filter(active=True).filter(user_id=user_id).latest('endtime')
except:
selection = None
if selection is not None and selection.endtime > timezone.now():
running_selection = True
todos = Todo.objects.filter(user_id=user_id).filter(complete=False).filter(selection=selection.id)
if todos:
running_selection = True
else:
running_selection = False
else:
running_selection = False
content = {'page_title': 'Feierabend!',
'running_selection': running_selection,
'username': request.user.username,
'selection': selection}
return render(request, 'todo/landing_page.html', content)
......@@ -53,6 +59,7 @@ def get_landing_page(request):
@login_required()
def todo_details(request, pk=None):
user_id = request.user.id
if pk:
todo = Todo.objects.get(pk=pk)
else:
......@@ -62,6 +69,7 @@ def todo_details(request, pk=None):
form = TodoForm(request.POST, instance=todo)
if form.is_valid():
todo = form.instance
todo.user_id = user_id
todo.save()
messages.success(request, "Dein Todo wurde abgespeichert")
return HttpResponseRedirect(reverse_lazy('landing_page'))
......@@ -70,8 +78,11 @@ def todo_details(request, pk=None):
else:
form = TodoForm(instance=todo)
return render(request, 'todo/add_todo.html', {'page_title': 'Lege eine neue Aufgabe an! ',
'form': form})
content = {'page_title': 'Lege eine neue Aufgabe an!',
'form': form,
'username': request.user.username}
return render(request, 'todo/add_todo.html', content)
@login_required()
def delete_todo(request, pk=None, source='show_todo', s_id=None):
......@@ -85,6 +96,9 @@ def delete_todo(request, pk=None, source='show_todo', s_id=None):
@login_required()
def get_selection_page(request):
# set todos with status complete but with passed completed_until date to incomplete
undo_complete_recurrences()
selection = Selection()
if request.method == 'POST':
......@@ -92,6 +106,7 @@ def get_selection_page(request):
if form.is_valid():
selection.total_time = form.calc_total_time()
selection.active = True
selection.user_id = request.user.id
selection.endtime = selection.started + datetime.timedelta(minutes=selection.total_time)
selection.save()
for id in form.todos_string_to_list():
......@@ -110,7 +125,7 @@ def get_selection_page(request):
todos_soon = []
todos_late = []
todos_someday = []
todos_someday = sort_todos(todos_late, todos_someday, todos_soon, todos_today)
todos_someday = sort_todos(todos_late, todos_someday, todos_soon, todos_today, request.user.id)
content = {'page_title': 'Leg eine neue Aufgabenliste an',
'form': form,
......@@ -119,6 +134,7 @@ def get_selection_page(request):
'todos_late': todos_late,
'todos_someday': todos_someday,
'todos_selection': [],
'username': request.user.username
}
return render(request, 'todo/selection_page.html', content)
......@@ -133,6 +149,8 @@ def get_selection_id(request, pk=None):
for todo_id in checked_ids:
todo = Todo.objects.get(pk=todo_id)
todo.complete = True
todo.finished = datetime.datetime.now()
get_completed_until_date(todo)
todo.save()
messages.success(request, 'Änderung gespeichert')
return HttpResponseRedirect(reverse_lazy('landing_page'))
......@@ -146,7 +164,7 @@ def get_selection_id(request, pk=None):
todos_soon = []
todos_late = []
todos_someday = []
todos_someday = sort_todos(todos_late, todos_someday, todos_soon, todos_today, pk)
todos_someday = sort_todos(todos_late, todos_someday, todos_soon, todos_today, request.user.id, pk)
content = {'page_title': 'Deine aktuelle Aufgabenliste',
'form': form,
......@@ -155,19 +173,53 @@ def get_selection_id(request, pk=None):
'todos_late': todos_late,
'todos_someday': todos_someday,
'todos_selection': [],
'pk': pk
'pk': pk,
'username': request.user.username
}
return render(request, 'todo/show_selection.html', content)
def sort_todos(todos_late, todos_someday, todos_soon, todos_today, select_id=None):
@login_required()
def delete_selection(request, pk):
selection = Selection.objects.get(pk=pk)
if selection.user_id == request.user.id:
Selection.objects.get(pk=pk).delete()
return HttpResponseRedirect(reverse_lazy('landing_page'))
def get_completed_until_date(todo):
next_occur = todo.recurrences.after(
datetime.datetime.now() - datetime.timedelta(days=1),
inc=True, )
if next_occur:
next_next_occur = todo.recurrences.after(
next_occur + datetime.timedelta(days=1),
inc=True, )
if next_next_occur:
todo.completed_until = next_occur
# set todos with status complete but with passed completed_until date to incomplete
def undo_complete_recurrences():
todos = Todo.objects.filter(complete=True).filter(completed_until__lt=datetime.date.today())
if todos:
for todo in todos:
todo.complete = False
todo.completed_until = None
todo.save()
def sort_todos(todos_late, todos_someday, todos_soon, todos_today, user_id, select_id=None):
if select_id is None:
todos_with_recurrence = Todo.objects.filter(complete=False).exclude(recurrences__exact='')
todos_someday = Todo.objects.filter(complete=False).filter(recurrences__exact='')
todos_with_recurrence = Todo.objects.filter(user_id=user_id).filter(complete=False).exclude(
recurrences__exact='')
todos_someday = Todo.objects.filter(user_id=user_id).filter(complete=False).filter(recurrences__exact='')
else:
todos_with_recurrence = Todo.objects.filter(complete=False).filter(selection_id=select_id).exclude(
todos_with_recurrence = Todo.objects.filter(user_id=user_id).filter(complete=False).filter(
selection_id=select_id).exclude(
recurrences__exact='')
todos_someday = Todo.objects.filter(complete=False).filter(selection_id=select_id).filter(recurrences__exact='')
todos_someday = Todo.objects.filter(user_id=user_id).filter(complete=False).filter(
selection_id=select_id).filter(recurrences__exact='')
for recur_todo in todos_with_recurrence:
next_occur = recur_todo.recurrences.after(
......@@ -191,16 +243,22 @@ def sort_todos(todos_late, todos_someday, todos_soon, todos_today, select_id=Non
def get_impressum(request):
content = {'page_title': 'Impressum'}
content = {'page_title': 'Impressum',
'username': request.user.username}
return render(request, 'todo/impressum.html', content)
def get_contact(request):
content = {'page_title': 'Kontakt'}
content = {'page_title': 'Kontakt',
'username': request.user.username}