Compare commits

..

6 Commits

10 changed files with 143 additions and 17 deletions

View File

@ -119,3 +119,5 @@ USE_TZ = True
# https://docs.djangoproject.com/en/3.1/howto/static-files/ # https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
LOGIN_URL = '/accounts/login/'

View File

@ -18,5 +18,6 @@ from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('writing/', include('writingtogether.urls')), path('writing/', include('writingtogether.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
] ]

View File

@ -1,3 +1,5 @@
from typing import Tuple
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django.db.models import CASCADE from django.db.models import CASCADE
@ -29,6 +31,14 @@ class StoryRound(models.Model):
return False return False
return True return True
def advance_to_next_turn_and_get_status_and_round_number(self) -> Tuple[str, int]:
if self.current_turn+1 == self.number_of_rounds:
return 'finished', self.current_turn
self.current_turn += 1
self.save()
return 'ongoing', self.current_turn
#class Participant(models.Model): #class Participant(models.Model):
# user = models.ForeignKey(User, on_delete=CASCADE) # user = models.ForeignKey(User, on_delete=CASCADE)
@ -60,9 +70,9 @@ class StoryPart(models.Model):
user = models.ForeignKey(User, on_delete=CASCADE) user = models.ForeignKey(User, on_delete=CASCADE)
previous_part = models.ForeignKey('StoryPart', on_delete=CASCADE, null=True, blank=True) previous_part = models.ForeignKey('StoryPart', on_delete=CASCADE, null=True, blank=True)
text = models.TextField(null=True, blank=True) text = models.TextField(null=True, blank=True)
part_of = models.ForeignKey('Story', on_delete=CASCADE) part_of = models.ForeignKey('Story', on_delete=CASCADE, related_name='parts')
turn_number = models.IntegerField() turn_number = models.IntegerField()
@property @property
def finished(self): def finished(self):
return self.text return bool(self.text)

View File

@ -0,0 +1,36 @@
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login">
<input type="hidden" name="next" value="{{ next }}">
</form>
{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
{% endblock %}

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Fertig!</h1>
{% for story in stories %}
{% for story_part in story.parts.all %}
<div>{{ story_part.text }} <i>({{ story_part.user }})</i></div>
{% endfor %}
<br>
<br>
{% endfor %}
</body>
</html>

View File

@ -5,8 +5,12 @@
<title>Title</title> <title>Title</title>
</head> </head>
<body> <body>
<h1>Runde {{ current_round }} / {{ total_rounds }}</h1>
<form action="{% url 'writing:create_story_part' %}" method="post"> {% if previous_part %}
<div>Letzter Satz: {{ previous_part.text }}</div>
{% endif %}
<form action="{% url 'writing:update_story_part' story_round_pk=story_round_pk story_pk=story_pk pk=pk %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Submit"> <input type="submit" value="Submit">

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div>Die nächste Runde ist noch nicht bereit.</div>
<div>Klick hier um zu prüfen, ob die anderen fertig sind:
<a href="{% url 'writing:redirect_story_part' story_round_pk=object.pk %}">nächste Runde</a>
</div>
</body>
</html>

View File

@ -9,8 +9,9 @@ User = get_user_model()
class TestViews(TestCase): class TestViews(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.user1 = User.objects.create(username='player1') self.user1 = User.objects.create_user(username='player1', password='12345')
self.user2 = User.objects.create(username='player2') self.user2 = User.objects.create_user(username='player2')
self.client.login(username='player1', password='12345')
def test_create_story_round_two_rounds(self): def test_create_story_round_two_rounds(self):

View File

@ -8,6 +8,8 @@ urlpatterns = [
path('', views.IndexView.as_view(), name='index'), path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'), path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('create/', views.StoryRoundCreate.as_view(), name='create_story_round'), path('create/', views.StoryRoundCreate.as_view(), name='create_story_round'),
path('open/<int:story_round_pk>/<int:story_pk>/<int:story_part_pk>/', views.StoryPartUpdate.as_view(), name='update_story_part'), path('open/<int:story_round_pk>/<int:story_pk>/<int:pk>/', views.StoryPartUpdate.as_view(), name='update_story_part'),
path('open/<int:pk>/waiting/', views.WaitForOthersView.as_view(), name='wait_for_others'),
path('open/<int:story_round_pk>/', views.RedirectToNextOpenPart.as_view(), name='redirect_story_part'), path('open/<int:story_round_pk>/', views.RedirectToNextOpenPart.as_view(), name='redirect_story_part'),
path('finished/<int:pk>/', views.RoundFinishedView.as_view(), name ='finished'),
] ]

View File

@ -1,16 +1,15 @@
from django import forms from django import forms
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
# Create your views here. # Create your views here.
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.views import generic from django.views.generic import CreateView, UpdateView, RedirectView, DetailView, ListView
from django.views.generic import CreateView, UpdateView, RedirectView
from writingtogether.models import Story, StoryPart, StoryRound from writingtogether.models import Story, StoryPart, StoryRound
class IndexView(generic.ListView): class IndexView(LoginRequiredMixin, ListView):
template_name = 'writingtogether/index.html' template_name = 'writingtogether/index.html'
context_object_name = 'open_story_round_list' context_object_name = 'open_story_round_list'
@ -19,12 +18,12 @@ class IndexView(generic.ListView):
return StoryRound.objects.order_by('-created')[:5] return StoryRound.objects.order_by('-created')[:5]
class DetailView(generic.DetailView): class StoryRoundDetailView(LoginRequiredMixin, DetailView):
model = StoryRound model = StoryRound
template_name = 'writingtogether/detail.html' template_name = 'writingtogether/detail.html'
class StoryRoundCreate(CreateView): class StoryRoundCreate(LoginRequiredMixin, CreateView):
model = StoryRound model = StoryRound
fields = ['name', 'participants', 'number_of_rounds'] fields = ['name', 'participants', 'number_of_rounds']
success_url = reverse_lazy('writing:index') success_url = reverse_lazy('writing:index')
@ -53,15 +52,27 @@ class StoryRoundCreate(CreateView):
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
class StoryUpdate(UpdateView): class StoryUpdate(LoginRequiredMixin, UpdateView):
model = Story model = Story
fields = ['name'] fields = ['name']
class StoryPartUpdate(UpdateView): class StoryPartUpdate(LoginRequiredMixin, UpdateView):
model = StoryPart model = StoryPart
fields = ['text'] fields = ['text']
template_name = 'writingtogether/story_part.html' template_name = 'writingtogether/story_part.html'
success_url = reverse_lazy('writing:index')
def get_context_data(self, **kwargs):
context = super(StoryPartUpdate, self).get_context_data(**kwargs)
context.update({
'current_round': self.object.turn_number + 1,
'total_rounds': self.object.part_of.part_of_round.number_of_rounds,
'previous_part': self.object.previous_part,
})
context.update(self.kwargs)
return context
#def form_valid(self, form): #def form_valid(self, form):
# form.instance.created_by = self.request.user # form.instance.created_by = self.request.user
@ -70,7 +81,7 @@ class StoryPartUpdate(UpdateView):
# return super().form_valid(form) # return super().form_valid(form)
class RedirectToNextOpenPart(RedirectView): class RedirectToNextOpenPart(LoginRequiredMixin, RedirectView):
permanent = False permanent = False
query_string = True query_string = True
pattern_name = 'writing:update_story_part' pattern_name = 'writing:update_story_part'
@ -78,8 +89,34 @@ class RedirectToNextOpenPart(RedirectView):
def get_redirect_url(self, *args, **kwargs): def get_redirect_url(self, *args, **kwargs):
story_round = get_object_or_404(StoryRound, pk=kwargs['story_round_pk']) story_round = get_object_or_404(StoryRound, pk=kwargs['story_round_pk'])
if story_round.next_round_ready():
status, turn_number = story_round.advance_to_next_turn_and_get_status_and_round_number()
if status == 'finished':
self.pattern_name = 'writing:finished'
kwargs['pk'] = kwargs.pop('story_round_pk')
return super().get_redirect_url(*args, **kwargs)
story_part = story_round.get_next_story_part(user=self.request.user) story_part = story_round.get_next_story_part(user=self.request.user)
kwargs['story_pk'] = story_part.part_of.pk if story_part.finished:
kwargs['story_part_pk'] = story_part.pk self.pattern_name = 'writing:wait_for_others'
kwargs['pk'] = kwargs.pop('story_round_pk')
else:
kwargs['story_pk'] = story_part.part_of.pk
kwargs['pk'] = story_part.pk
return super().get_redirect_url(*args, **kwargs) return super().get_redirect_url(*args, **kwargs)
class WaitForOthersView(LoginRequiredMixin, DetailView):
model = StoryRound
template_name = 'writingtogether/wait_for_others.html'
class RoundFinishedView(LoginRequiredMixin, ListView):
model = Story
context_object_name = 'stories'
template_name = 'writingtogether/finished.html'
def get_queryset(self):
return Story.objects.filter(part_of_round_id=self.kwargs['pk']).prefetch_related('parts')