Add new 'resume' module for getting back to a workflow that was interrupted by a necessary redirect
This commit is contained in:
parent
aa0793241f
commit
0a2adeaddb
9 changed files with 174 additions and 1 deletions
|
@ -54,6 +54,7 @@ INSTALLED_APPS = [
|
||||||
'get_together',
|
'get_together',
|
||||||
'events',
|
'events',
|
||||||
'accounts',
|
'accounts',
|
||||||
|
'resume',
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGIN_URL = 'login'
|
LOGIN_URL = 'login'
|
||||||
|
@ -82,6 +83,7 @@ MIDDLEWARE = [
|
||||||
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
||||||
|
|
||||||
'social_django.middleware.SocialAuthExceptionMiddleware',
|
'social_django.middleware.SocialAuthExceptionMiddleware',
|
||||||
|
'resume.middleware.ResumeMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'get_together.urls'
|
ROOT_URLCONF = 'get_together.urls'
|
||||||
|
|
|
@ -2,3 +2,4 @@ from django.test import TestCase
|
||||||
|
|
||||||
from .events import *
|
from .events import *
|
||||||
from .event_reminder import *
|
from .event_reminder import *
|
||||||
|
from .speakers import *
|
||||||
|
|
32
get_together/tests/speakers.py
Normal file
32
get_together/tests/speakers.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.shortcuts import resolve_url
|
||||||
|
from model_mommy import mommy
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from events.models import Speaker, Talk
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
|
class TalkCreationTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_resume_adding_talk(self):
|
||||||
|
user = User.objects.create(username='testuser', password='12345', is_active=True)
|
||||||
|
|
||||||
|
c = Client()
|
||||||
|
response = c.force_login(user)
|
||||||
|
|
||||||
|
response = c.get(resolve_url('add-talk'))
|
||||||
|
assert(response.status_code == 302)
|
||||||
|
assert(response.url == resolve_url('add-speaker'))
|
||||||
|
|
||||||
|
response = c.get(resolve_url('add-speaker'))
|
||||||
|
assert(response.status_code == 200)
|
||||||
|
response = c.post(resolve_url('add-speaker'), {'title': 'test', 'bio': 'testing'})
|
||||||
|
assert(response.status_code == 302)
|
||||||
|
assert(response.url == resolve_url('add-talk'))
|
||||||
|
|
|
@ -23,6 +23,8 @@ from events.models.speakers import Speaker, Talk, Presentation, SpeakerRequest
|
||||||
import datetime
|
import datetime
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
from resume import set_resume, resume_or_redirect
|
||||||
|
|
||||||
from .teams import *
|
from .teams import *
|
||||||
from .events import *
|
from .events import *
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ def add_speaker(request):
|
||||||
speaker_form = SpeakerBioForm(request.POST, request.FILES, instance=new_speaker)
|
speaker_form = SpeakerBioForm(request.POST, request.FILES, instance=new_speaker)
|
||||||
if speaker_form.is_valid():
|
if speaker_form.is_valid():
|
||||||
new_speaker = speaker_form.save()
|
new_speaker = speaker_form.save()
|
||||||
return redirect('user-talks')
|
return resume_or_redirect(request, 'user-talks')
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'speaker': new_speaker,
|
'speaker': new_speaker,
|
||||||
|
@ -138,6 +140,7 @@ def show_talk(request, talk_id):
|
||||||
def add_talk(request):
|
def add_talk(request):
|
||||||
if Speaker.objects.filter(user=request.user.profile).count() < 1:
|
if Speaker.objects.filter(user=request.user.profile).count() < 1:
|
||||||
messages.add_message(request, messages.WARNING, message=_('You must create a new Speaker profile before you can add a talk'))
|
messages.add_message(request, messages.WARNING, message=_('You must create a new Speaker profile before you can add a talk'))
|
||||||
|
set_resume(request)
|
||||||
return redirect('add-speaker')
|
return redirect('add-speaker')
|
||||||
|
|
||||||
new_talk = Talk()
|
new_talk = Talk()
|
||||||
|
|
1
resume/__init__.py
Normal file
1
resume/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from resume.api import *
|
29
resume/api.py
Normal file
29
resume/api.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from django.shortcuts import redirect, resolve_url
|
||||||
|
|
||||||
|
class ResumeFailure(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def set_resume(request):
|
||||||
|
try:
|
||||||
|
resume_points = request._resume_points
|
||||||
|
except AttributeError:
|
||||||
|
if not hasattr(request, 'META'):
|
||||||
|
raise TypeError(
|
||||||
|
"add_resume_point() argument must be an HttpRequest object, not "
|
||||||
|
"'%s'." % request.__class__.__name__
|
||||||
|
)
|
||||||
|
raise ResumeFailure(
|
||||||
|
'You cannot add resume points without installing '
|
||||||
|
'resume.middleware.ResumeMiddleware'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return resume_points.add(request.get_full_path())
|
||||||
|
|
||||||
|
|
||||||
|
def resume_or_redirect(request, to, permanent=False, *args, **kwargs):
|
||||||
|
if len(request._resume_points) > 0:
|
||||||
|
resume_from = request._resume_points.pop()
|
||||||
|
return redirect(resume_from)
|
||||||
|
else:
|
||||||
|
return redirect(to, permanent, *args, **kwargs)
|
5
resume/apps.py
Normal file
5
resume/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeConfig(AppConfig):
|
||||||
|
name = 'Resume'
|
67
resume/middleware.py
Normal file
67
resume/middleware.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import json
|
||||||
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeStorage:
|
||||||
|
session_key = '_resume'
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
self.request = request
|
||||||
|
self._resume_points = self.load()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._resume_points)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._resume_points)
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
return item in self._resume_points
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
"""
|
||||||
|
Retrieve a list of resume points from the request's session.
|
||||||
|
"""
|
||||||
|
if self.session_key not in self.request.session:
|
||||||
|
return []
|
||||||
|
return json.loads(self.request.session.get(self.session_key))
|
||||||
|
|
||||||
|
def store(self):
|
||||||
|
"""
|
||||||
|
Store a list of resume points to the request's session.
|
||||||
|
"""
|
||||||
|
if self._resume_points:
|
||||||
|
self.request.session[self.session_key] = json.dumps(self._resume_points)
|
||||||
|
else:
|
||||||
|
self.request.session.pop(self.session_key, None)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def add(self, path):
|
||||||
|
self._resume_points.append(path)
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
if len(self._resume_points) > 0:
|
||||||
|
return self._resume_points.pop()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeMiddleware(MiddlewareMixin):
|
||||||
|
"""
|
||||||
|
Middleware that handles setting resume points in a user flow.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process_request(self, request):
|
||||||
|
request._resume_points = ResumeStorage(request)
|
||||||
|
|
||||||
|
def process_response(self, request, response):
|
||||||
|
"""
|
||||||
|
Update the storage backend (i.e., save the resume points).
|
||||||
|
|
||||||
|
Raise ValueError if not all resume points could be stored and DEBUG is True.
|
||||||
|
"""
|
||||||
|
# A higher middleware layer may return a request which does not contain
|
||||||
|
# resume storage, so make no assumption that it will be there.
|
||||||
|
if hasattr(request, '_resume_points'):
|
||||||
|
request._resume_points.store()
|
||||||
|
return response
|
33
resume/tests.py
Normal file
33
resume/tests.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from resume import set_resume
|
||||||
|
from resume.middleware import ResumeStorage
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
|
class ResumeTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.request = HttpRequest()
|
||||||
|
self.request.path = 'test/foo'
|
||||||
|
self.request.session = {}
|
||||||
|
self.request._resume_points = ResumeStorage(self.request)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
del self.request
|
||||||
|
|
||||||
|
def test_resume_point_storage(self):
|
||||||
|
|
||||||
|
assert(len(self.request._resume_points) == 0)
|
||||||
|
|
||||||
|
no_resume_point = self.request._resume_points.pop()
|
||||||
|
assert(no_resume_point is None)
|
||||||
|
|
||||||
|
set_resume(self.request)
|
||||||
|
|
||||||
|
assert(len(self.request._resume_points) == 1)
|
||||||
|
|
||||||
|
one_resume_point = self.request._resume_points.pop()
|
||||||
|
assert(one_resume_point == 'test/foo')
|
||||||
|
assert(len(self.request._resume_points) == 0)
|
Loading…
Reference in a new issue