Compare commits
6 Commits
cc93abc752
...
7277fc7b5c
Author | SHA1 | Date |
---|---|---|
Gesche | 7277fc7b5c | |
Gesche | a59354693c | |
Gesche | 0d0cc2eeb5 | |
Gesche | e5275db8e8 | |
Gesche | e09f17bdfb | |
Gesche | 1527f07fa3 |
|
@ -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/'
|
||||||
|
|
|
@ -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),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
|
@ -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):
|
||||||
|
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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')
|
Loading…
Reference in New Issue