Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

183 gift fulfillment workflow

184 wish fulfillment workflow

    first version:
    users can write thank-you notes for gifts and wishes
    users can click you're-welcome for gifts and wishes
    once a gift or wish has both a thank-you note and a you're-welcome, it is marked completed
    notification is done via auto-generated messages

    todo:
    make thank-you notes public
    improve UI/flow using wireframes
  • Loading branch information...
commit 79892d29f0a0c9fd959b4bdd44c125d3855d96ad 1 parent 3475bdd
@djudd djudd authored
View
1  .gitignore
@@ -9,4 +9,5 @@ search/whoosh_index
.ipython.py
src/
media/pictures
+build/
*~
View
17 messaging/views.py
@@ -13,6 +13,8 @@
from messaging.forms import ComposeForm
from messaging.utils import format_quote
+from records.models import RecordResponse
+
if "notification" in settings.INSTALLED_APPS:
from notification import models as notification
else:
@@ -203,10 +205,19 @@ def view(request, message_id, template_name='messaging/view.html'):
message = get_object_or_404(Message, id=message_id)
if (message.sender != user) and (message.recipient != user):
raise Http404
+
+ model = {
+ 'message': message,
+ }
+
+ try:
+ record_response = RecordResponse.objects.get(message = message)
+ model['record_response'] = record_response
+ except RecordResponse.DoesNotExist:
+ pass
+
if message.read_at is None and message.recipient == user:
message.read_at = now
message.save()
- return render_to_response(template_name, {
- 'message': message,
- }, context_instance=RequestContext(request))
+ return render_to_response(template_name, model, context_instance=RequestContext(request))
view = login_required(view)
View
62 records/forms.py
@@ -4,8 +4,11 @@
from django.core.files import File
from messaging.forms import ComposeForm
+from messaging.models import Message
-from records.models import Wish, Gift
+from records.models import Record, Wish, Gift, ThankYou, YoureWelcome
+
+from profiles.models import Profile
class UserCreatedModelForm(forms.ModelForm):
"""
@@ -17,25 +20,13 @@ def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UserCreatedModelForm, self).__init__(*args, **kwargs)
- def clean_picture(self):
- picture = self.cleaned_data['picture']
-
- if picture is not None and isinstance(picture, File) and picture._size > settings.MAX_UPLOAD_SIZE:
- raise forms.ValidationError(
- 'Images cannot be larger than %s. (Uploaded image size was %s.)' %
- (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(picture._size))
- )
-
- return picture
-
def save(self, commit=True):
""" Auto-set user """
obj = super(UserCreatedModelForm, self).save(commit=False)
has_user = False
try:
- if obj.user is not None:
- has_user = True
+ has_user = obj.user is not None
except:
pass
@@ -45,12 +36,51 @@ def save(self, commit=True):
obj.save()
return obj
-class WishForm(UserCreatedModelForm):
+class ThankYouForm(UserCreatedModelForm):
+ class Meta:
+ fields = ['record', 'other_user', 'note']
+ model = ThankYou
+
+# TODO Make record, and (if present) other user, readonly
+
+ def save(self, commit=True):
+ """ Create message """
+ obj = super(ThankYouForm, self).save(commit=False)
+ obj.create(commit=commit)
+ return obj
+
+class YoureWelcomeForm(UserCreatedModelForm):
+ class Meta:
+ fields = ['record', 'other_user']
+ model = YoureWelcome
+
+# TODO Make record, and (if present) other user, readonly
+
+ def save(self, commit=True):
+ """ Create message """
+ obj = super(YoureWelcomeForm, self).save(commit=False)
+ obj.create(commit=commit)
+ return obj
+
+class RecordForm(UserCreatedModelForm):
+ def clean_picture(self):
+ picture = self.cleaned_data['picture']
+
+ if picture is not None and isinstance(picture, File) and picture._size > settings.MAX_UPLOAD_SIZE:
+ raise forms.ValidationError(
+ 'Images cannot be larger than %s. (Uploaded image size was %s.)' %
+ (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(picture._size))
+ )
+
+ return picture
+
+class WishForm(RecordForm):
class Meta:
fields = ['title', 'description', 'location', 'category', 'picture']
model = Wish
-class GiftForm(UserCreatedModelForm):
+class GiftForm(RecordForm):
class Meta:
fields = ['title', 'description', 'location', 'category', 'picture']
model = Gift
+
View
161 records/models.py
@@ -1,6 +1,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
-
+from messaging.models import Message
+from profiles.models import Profile
# Managers
@@ -53,7 +54,7 @@ class Record(models.Model):
# the parties involved
user = models.ForeignKey('profiles.Profile', db_index=True,
- related_name='records_created')
+ related_name='records_created', editable=False)
other_user = models.ForeignKey('profiles.Profile', blank=True, null=True,
related_name='records_fulfilled')
@@ -102,6 +103,10 @@ def status_name(self):
def short_title(self):
return self.title[:17] + '...' if len(self.title) > 20 else self.title
+ def mark_completed(self):
+ if self.status == 0: # active
+ self.status = 2 # completed
+
def get_absolute_url(self):
return self.child.get_absolute_url()
@@ -144,9 +149,149 @@ class RecordResponse(models.Model):
""" Represent an interaction between users regarding a record """
record = models.ForeignKey(Record)
- sender = models.ForeignKey('profiles.Profile',
- related_name='sent_responses')
- recipient = models.ForeignKey('profiles.Profile',
- related_name='received_responses')
- parent = models.ForeignKey('self', null=True)
- text = models.TextField()
+
+ # sender
+ user = models.ForeignKey('profiles.Profile', db_index=True,
+ related_name='sent_responses', editable=False)
+ # recipient
+ other_user = models.ForeignKey('profiles.Profile',
+ related_name='received_responses')
+
+ message = models.OneToOneField(Message, blank=True)
+
+ @staticmethod
+ def _validate(giver, wisher, record):
+ if giver == wisher:
+ raise Exception("Giver and wisher must be different")
+ if (record.type == 'Wish' and record.user != wisher) or (record.type == 'Gift' and record.user != giver):
+ raise Exception("Invalid data")
+ if record.status != 0:
+ raise Exception("Record is not active")
+
+
+class YoureWelcome(RecordResponse):
+ """ Assertion or acknowledgement that I gave a gift/fulfilled a wish """
+
+ response = models.OneToOneField(RecordResponse, parent_link=True)
+ thank_you = models.ForeignKey('records.ThankYou', null=True, blank=True)
+
+ objects = models.Manager()
+
+ @models.permalink
+ def get_absolute_url(self):
+ return ('welcome', (), {'pk': self.id})
+
+ @property
+ def has_reply(self):
+ try:
+ return self.thank_you is not None
+ except ObjectDoesNotExist:
+ return False
+
+ def _generate_request_message(self, giver, wisher, record):
+ return Message(
+ subject = 'Will you write me a thank-you note for %s?' % record.short_title,
+ body = 'Please write a public note to let people know what you thought about the %s.' % record.title,
+ sender = giver.user,
+ recipient = wisher.user,
+ record = record
+ )
+
+ def _generate_accept_message(self, giver, wisher, record):
+ return Message(
+ subject = "Thanks for the note!",
+ body = "You're welcome for the %s." % record.title,
+ sender = giver.user,
+ recipient = wisher.user,
+ record = record
+ )
+
+ def _find_corresponding_thank_you(self, wisher, record):
+ thankyous = ThankYou.objects.filter(record=record,user=wisher).order_by('-pk')
+ if len(thankyous) > 0:
+ return thankyous[0]
+ else:
+ return None
+
+ def create(self, commit=True):
+ giver = Profile.objects.get(pk=self.user_id)
+ wisher = Profile.objects.get(pk=self.other_user_id)
+ record = Record.objects.get(pk=self.record_id)
+
+ RecordResponse._validate(giver, wisher, record)
+
+ thank_you = self._find_corresponding_thank_you(wisher, record)
+ if thank_you:
+ self.thank_you = thank_you
+ message = self._generate_accept_message(giver, wisher, record)
+ record.mark_completed()
+ else:
+ message = self._generate_request_message(giver, wisher, record)
+
+ if commit:
+ message.save()
+
+ self.message = message
+ self.save()
+
+ if thank_you:
+ record.save()
+
+class ThankYou(RecordResponse):
+ """ Assertion or acknowledgement that I recieved a gift/had my wish fulfilled """
+
+ response = models.OneToOneField(RecordResponse, parent_link=True)
+ youre_welcome = models.ForeignKey('records.YoureWelcome', null=True, blank=True)
+ note = models.TextField()
+
+ objects = models.Manager()
+
+ @models.permalink
+ def get_absolute_url(self):
+ return ('thanks', (), {'pk': self.id})
+
+ @property
+ def has_reply(self):
+ try:
+ return self.youre_welcome is not None
+ except ObjectDoesNotExist:
+ return False
+
+ def _generate_message(self, giver, wisher, record):
+ return Message(
+ subject = 'Thanks for the %s!' % record.short_title,
+ body = self.note,
+ sender = wisher.user,
+ recipient = giver.user,
+ record = record
+ )
+
+ def _find_corresponding_youre_welcome(self, giver, record):
+ welcomes = YoureWelcome.objects.filter(record=record,user=giver).order_by('-pk')
+ if len(welcomes) > 0:
+ return welcomes[0]
+ else:
+ return None
+
+ def create(self, commit=True):
+ giver = Profile.objects.get(pk=self.other_user_id)
+ wisher = Profile.objects.get(pk=self.user_id)
+ record = Record.objects.get(pk=self.record_id)
+
+ RecordResponse._validate(giver, wisher, record)
+
+ message = self._generate_message(giver, wisher, record)
+
+ welcome = self._find_corresponding_youre_welcome(giver, record)
+ if welcome:
+ self.youre_welcome = welcome
+ record.mark_completed()
+
+ if commit:
+ message.save()
+
+ self.message = message
+ self.save()
+
+ if welcome:
+ record.save()
View
3  records/urls.py
@@ -16,7 +16,8 @@
url(r'^gifts$', GiftListView.as_view()),
url(r'^gifts/(?P<slug>[\w-]+)/$', GiftListView.as_view(), name='giftlist'),
- url(r'fulfill/(?P<pk>[0-9]+)', mark_fulfilled),
+ url(r'^thank', CreateThankYouView.as_view()),
+ url(r'^welcome', CreateYoureWelcomeView.as_view()),
# messaging
url(r'^messages/compose_embedded$', compose,
View
27 records/views.py
@@ -11,19 +11,12 @@
from profiles.models import Profile
-from records.models import Record, Wish, Gift, Category
-from records.forms import WishForm, GiftForm
+from records.models import Record, Wish, Gift, Category, YoureWelcome, ThankYou
+from records.forms import WishForm, GiftForm, YoureWelcomeForm, ThankYouForm
from messaging.models import Message
from utils import requires_login
-def mark_fulfilled(request, pk):
- record = get_object_or_404(Record, pk=pk, user=request.user)
- if record.status == 0: # active
- record.status = 2 # completed
- record.save()
- return redirect(request.user.profile)
-
class UserCreateView(CreateView):
""" A generic view that attaches the current user to the form """
@@ -41,7 +34,6 @@ def get_form_kwargs(self):
# Attach user
if self.request.method in ('POST', 'PUT'):
kwargs['user'] = self.request.user.profile
-
return kwargs
class UserUpdateView(UpdateView):
@@ -80,6 +72,20 @@ class UpdateWishView(UserUpdateView):
template_name = 'records/add_wish.html'
success_url = '/wish/%(id)s'
+@requires_login
+class CreateYoureWelcomeView(UserCreateView):
+ model = YoureWelcome
+ form_class = YoureWelcomeForm
+ template_name = 'records/add_youre_welcome.html'
+ success_url = '/'
+
+@requires_login
+class CreateThankYouView(UserCreateView):
+ model = ThankYou
+ form_class = ThankYouForm
+ template_name = 'records/add_thank_you.html'
+ success_url = '/'
+
class RecordListView(ListView):
def get_queryset(self):
slug = self.kwargs.get('slug')
@@ -119,7 +125,6 @@ class WishDetailView(RecordMessageMixin, DetailView):
model = Wish
subject_format = '''Your wish for "{}"'''
-
class GiftListView(RecordListView):
model = Gift
context_object_name = "gifts"
View
9 templates/messaging/view.html
@@ -25,6 +25,15 @@
<hr />
{% ifequal message.recipient.pk user.pk %}
+ {% if record_response %}
+ {% if record_response.thankyou and not record_response.thankyou.has_reply %}
+ <a href="/welcome?record={{ message.record.pk }}&other_user={{ message.sender.pk }}"><strong>{% trans "Accept Thank-You Note" %}</strong></a>
+ {% endif %}
+ {% if record_response.yourewelcome and not record_response.yourewelcome.has_reply %}
+ <a href="/thank?record={{ message.record.pk }}&other_user={{ message.sender.pk }}"><strong>{% trans "Write Thank-You Note" %}</strong></a>
+ {% endif %}
+ {% endif %}
+
<a href="{% url messaging_reply message.id %}"><strong>{% trans "Reply" %}</strong></a>
{% endifequal %}
<a href="{% url messaging_delete message.id %}"><strong>{% trans "Delete" %}</strong></a>
View
24 templates/records/gift_detail.html
@@ -24,7 +24,7 @@
</p>
<p>
{% if gift.status_name == 'Active' %}
- <strong><a href="fulfill/{{ gift.pk }}">Mark taken.</a></strong>
+ <strong><a href="/welcome?record={{ gift.pk }}">Request a thank-you note</a></strong>
{% else %}
<strong>{{ gift.status_name }}</strong>
{% endif %}
@@ -32,11 +32,23 @@
{# TODO: Show messages for this wish? #}
{% else %}
{% if user.is_authenticated %}
- <form method="post">
- {% csrf_token %}
- <textarea rows="4" cols="55" name="body"></textarea>
- <input type="submit" value="Send">
- </form>
+ {% if gift.status_name == 'Active' %}
+ <p>
+ <strong><a href="/thank?record={{ gift.pk }}&other_user={{ gift.user.pk }}">Write a thank-you note</a></strong>
+ </p>
+ {% endif %}
+
+ <p>
+ <strong><a href="#" onclick="$('#send_message_form').toggle();">Send a message about this gift</a></strong>
+ </p>
+
+ <div id="send_message_form" style="display: none;">
+ <form method="post">
+ {% csrf_token %}
+ <textarea rows="4" cols="55" name="body"></textarea>
+ <input type="submit" value="Send">
+ </form>
+ </div>
{% else %}
<a href="{% url openid-login %}">Log in</a> to send a message about this item!
{% endif %}
View
24 templates/records/wish_detail.html
@@ -32,7 +32,7 @@
</p>
<p>
{% if wish.status_name == 'Active' %}
- <strong><a href="fulfill/{{ wish.pk }}">Mark fulfilled!</a></strong>
+ <strong><a href="/thank?record={{ wish.pk }}">Write a thank-you note</a></strong>
{% else %}
<strong>{{ wish.status_name }}</strong>
{% endif %}
@@ -40,11 +40,23 @@
{# TODO: Show messages for this wish? #}
{% else %}
{% if user.is_authenticated %}
- <form method="post">
- {% csrf_token %}
- <textarea rows="4" cols="55" name="body"></textarea>
- <input type="submit" value="Send">
- </form>
+ {% if wish.status_name == 'Active' %}
+ <p>
+ <strong><a href="/welcome?record={{ wish.pk }}&other_user={{ wish.user.pk }}">Request a thank-you note</a></strong>
+ </p>
+ {% endif %}
+
+ <p>
+ <strong><a href="#" onclick="$('#send_message_form').toggle();">Send a message about this wish</a></strong>
+ </p>
+
+ <div id="send_message_form" style="display: none;">
+ <form method="post">
+ {% csrf_token %}
+ <textarea rows="4" cols="55" name="body"></textarea>
+ <input type="submit" value="Send">
+ </form>
+ </div>
{% else %}
<a href="{% url openid-login %}">Log in</a> to send a message about this item!
{% endif %}
Please sign in to comment.
Something went wrong with that request. Please try again.