Converting replies from original messages to reply ids. Also delaying emails on new replies and reshares so authors can change typos. Also dong a much better job with checking for dupe emails so no dupes ever get sent out.

This commit is contained in:
Samuel Clay 2012-07-27 18:58:35 -07:00
parent dcbb305f6e
commit 8eb106612a
10 changed files with 102 additions and 33 deletions

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
from bson.objectid import ObjectId
from apps.social.models import MSharedStory
class Migration(DataMigration):
def forwards(self, orm):
stories = MSharedStory.objects.filter(has_replies=True)
story_count = stories.count()
print " ---> %s stories with replies" % story_count
for i, story in enumerate(stories):
print " ---> %s/%s: %s replies" % (i+1, story_count, len(story.replies))
replies = []
for reply in story.replies:
if not reply.reply_id:
reply.reply_id = ObjectId()
replies.append(reply)
story.replies = replies
story.save()
def backwards(self, orm):
"Write your backwards methods here."
models = {
}
complete_apps = ['social']
symmetrical = True

View file

@ -1025,13 +1025,16 @@ class MSocialSubscription(mongo.Document):
return social_subs
class MCommentReply(mongo.EmbeddedDocument):
user_id = mongo.IntField()
publish_date = mongo.DateTimeField()
comments = mongo.StringField()
liking_users = mongo.ListField(mongo.IntField())
reply_id = mongo.ObjectIdField()
user_id = mongo.IntField()
publish_date = mongo.DateTimeField()
comments = mongo.StringField()
email_sent = mongo.BooleanField(default=False)
liking_users = mongo.ListField(mongo.IntField())
def to_json(self):
reply = {
'reply_id': self.reply_id,
'user_id': self.user_id,
'publish_date': relative_timesince(self.publish_date),
'date': self.publish_date,
@ -1041,6 +1044,8 @@ class MCommentReply(mongo.EmbeddedDocument):
meta = {
'ordering': ['publish_date'],
'id_field': 'reply_id',
'allow_inheritance': False,
}
@ -1069,6 +1074,7 @@ class MSharedStory(mongo.Document):
mute_email_users = mongo.ListField(mongo.IntField())
liking_users = mongo.ListField(mongo.IntField())
emailed_reshare = mongo.BooleanField(default=False)
emailed_replies = mongo.ListField(mongo.ObjectIdField())
meta = {
'collection': 'shared_stories',
@ -1079,7 +1085,11 @@ class MSharedStory(mongo.Document):
'ordering': ['shared_date'],
'allow_inheritance': False,
}
def __unicode__(self):
user = User.objects.get(pk=self.user_id)
return "%s: %s (%s)%s%s" % (user.username, self.story_title[:20], self.story_feed_id, ': ' if self.has_comments else '', self.comments[:20])
@property
def guid_hash(self):
return hashlib.sha1(self.story_guid).hexdigest()
@ -1475,19 +1485,33 @@ class MSharedStory(mongo.Document):
user_ids.add(self.user_id)
return list(user_ids)
def reply_for_id(self, reply_id):
for reply in self.replies:
if reply.reply_id == reply_id:
return reply
def send_emails_for_new_reply(self, reply_id):
if reply_id in self.emailed_replies:
logging.debug(" ***> Already sent reply email: %s on %s" % (reply_id, self))
return
def send_emails_for_new_reply(self, reply_user_id):
reply = self.reply_for_id(reply_id)
if not reply:
logging.debug(" ***> Reply doesn't exist: %s on %s" % (reply_id, self))
return
notify_user_ids = self.notify_user_ids()
if reply_user_id in notify_user_ids:
notify_user_ids.remove(reply_user_id)
reply_user = User.objects.get(pk=reply_user_id)
reply_user_profile = MSocialProfile.get_user(reply_user_id)
if reply.user_id in notify_user_ids:
notify_user_ids.remove(reply.user_id)
reply_user = User.objects.get(pk=reply.user_id)
reply_user_profile = MSocialProfile.get_user(reply.user_id)
sent_emails = 0
story_feed = Feed.objects.get(pk=self.story_feed_id)
comment = self.comments_with_author()
profile_user_ids = set([comment['user_id']])
reply_user_ids = [reply['user_id'] for reply in comment['replies']]
reply_user_ids = list(r['user_id'] for r in comment['replies'])
profile_user_ids = profile_user_ids.union(reply_user_ids)
if self.source_user_id:
profile_user_ids.add(self.source_user_id)
@ -1534,8 +1558,15 @@ class MSharedStory(mongo.Document):
sent_emails, len(notify_user_ids),
'' if len(notify_user_ids) == 1 else 's',
self.story_title[:30]))
self.emailed_replies.append(reply.reply_id)
self.save()
def send_email_for_reshare(self):
if self.emailed_reshare:
logging.debug(" ***> Already sent reply email: %s" % self)
return
reshare_user = User.objects.get(pk=self.user_id)
reshare_user_profile = MSocialProfile.get_user(self.user_id)
original_user = User.objects.get(pk=self.source_user_id)

View file

@ -19,9 +19,9 @@ class EmailNewFollower(Task):
class EmailCommentReplies(Task):
def run(self, shared_story_id, reply_user_id):
def run(self, shared_story_id, reply_id):
shared_story = MSharedStory.objects.get(id=shared_story_id)
shared_story.send_emails_for_new_reply(reply_user_id)
shared_story.send_emails_for_new_reply(reply_id)
class EmailStoryReshares(Task):

View file

@ -2,6 +2,7 @@ import time
import datetime
import zlib
import random
from bson.objectid import ObjectId
from django.shortcuts import get_object_or_404, render_to_response
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
@ -364,8 +365,8 @@ def mark_story_as_shared(request):
if service not in shared_story.posted_to_services:
PostToService.delay(shared_story_id=shared_story.id, service=service)
if shared_story.source_user_id and not shared_story.emailed_reshare and shared_story.comments:
EmailStoryReshares.delay(shared_story_id=shared_story.id)
if shared_story.source_user_id and shared_story.comments:
EmailStoryReshares.apply_async(kwargs=dict(shared_story_id=shared_story.id), countdown=60)
if format == 'html':
stories = MSharedStory.attach_users_to_stories(stories, profiles)
@ -435,6 +436,7 @@ def save_comment_reply(request):
comment_user_id = request.POST['comment_user_id']
reply_comments = request.POST.get('reply_comments')
original_message = request.POST.get('original_message')
reply_id = request.POST.get('reply_id')
format = request.REQUEST.get('format', 'json')
if not reply_comments:
@ -448,12 +450,14 @@ def save_comment_reply(request):
reply.publish_date = datetime.datetime.now()
reply.comments = reply_comments
if original_message:
if reply_id:
replies = []
for story_reply in shared_story.replies:
if (story_reply.user_id == reply.user_id and
strip_tags(story_reply.comments) == original_message):
story_reply.reply_id == ObjectId(reply_id)):
reply.publish_date = story_reply.publish_date
reply.reply_id = story_reply.reply_id
original_message = story_reply.comments
replies.append(reply)
else:
replies.append(story_reply)
@ -461,6 +465,7 @@ def save_comment_reply(request):
logging.user(request, "~FCUpdating comment reply in ~FM%s: ~SB~FB%s~FM" % (
shared_story.story_title[:20], reply_comments[:30]))
else:
reply.reply_id = ObjectId()
logging.user(request, "~FCReplying to comment in: ~FM%s: ~SB~FB%s~FM" % (
shared_story.story_title[:20], reply_comments[:30]))
shared_story.replies.append(reply)
@ -468,7 +473,7 @@ def save_comment_reply(request):
comment = shared_story.comments_with_author()
profile_user_ids = set([comment['user_id']])
reply_user_ids = [reply['user_id'] for reply in comment['replies']]
reply_user_ids = list(r['user_id'] for r in comment['replies'])
profile_user_ids = profile_user_ids.union(reply_user_ids)
profiles = MSocialProfile.objects.filter(user_id__in=list(profile_user_ids))
profiles = [profile.to_json(compact=True) for profile in profiles]
@ -496,8 +501,9 @@ def save_comment_reply(request):
original_message=original_message,
social_feed_id=comment_user_id,
story_id=story_id)
if not original_message:
EmailCommentReplies.delay(shared_story_id=shared_story.id, reply_user_id=request.user.pk)
EmailCommentReplies.apply_async(kwargs=dict(shared_story_id=shared_story.id,
reply_id=reply.reply_id), countdown=60)
if format == 'html':
comment = MSharedStory.attach_users_to_comment(comment, profiles)

View file

@ -260,7 +260,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
}
},
save_comment_reply: function(story_id, story_feed_id, comment_user_id, reply_comments, original_message, callback, error_callback) {
save_comment_reply: function(story_id, story_feed_id, comment_user_id, reply_comments, reply_id, callback, error_callback) {
var pre_callback = _.bind(function(data) {
if (data.user_profiles) {
this.add_user_profiles(data.user_profiles);
@ -273,7 +273,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
story_feed_id: story_feed_id,
comment_user_id: comment_user_id,
reply_comments: reply_comments,
original_message: original_message
reply_id: reply_id
}, pre_callback, error_callback);
},

View file

@ -95,13 +95,13 @@ NEWSBLUR.SocialPageAssets = Backbone.Router.extend({
}
},
save_comment_reply: function(story_id, story_feed_id, comment_user_id, reply_comments, original_message, callback, error_callback) {
save_comment_reply: function(story_id, story_feed_id, comment_user_id, reply_comments, reply_id, callback, error_callback) {
this.make_request('/social/save_comment_reply', {
story_id: story_id,
story_feed_id: story_feed_id,
comment_user_id: comment_user_id,
reply_comments: reply_comments,
original_message: original_message,
reply_id: reply_id,
format: 'html'
}, callback, error_callback, {
request_type: 'POST'

View file

@ -42,7 +42,7 @@ NEWSBLUR.Views.StoryCommentReply = Backbone.View.extend({
},
edit_reply: function() {
this.options.comment.open_reply({is_editing: true, $reply: this.$el});
this.options.comment.open_reply({is_editing: true, reply: this.model, $reply: this.$el});
}

View file

@ -132,15 +132,13 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
var $form = $.make('div', { className: 'NB-story-comment-reply NB-story-comment-reply-form' }, [
$.make('img', { className: 'NB-story-comment-reply-photo', src: current_user.get('photo_url') }),
$.make('div', { className: 'NB-story-comment-username NB-story-comment-reply-username' }, current_user.get('username')),
$.make('input', { type: 'text', className: 'NB-input NB-story-comment-reply-comments' }),
$.make('input', { type: 'text', className: 'NB-input NB-story-comment-reply-comments', value: options.reply && options.reply.get("comments") }),
$.make('div', { className: 'NB-modal-submit-button NB-modal-submit-green' }, options.is_editing ? 'Save' : 'Post')
]);
this.remove_social_comment_reply_form();
if (options.is_editing && options.$reply) {
var original_message = $('.NB-story-comment-reply-content', options.$reply).text();
$('input', $form).val(original_message);
$form.data('original_message', original_message);
$form.data('reply_id', options.reply.get("reply_id"));
options.$reply.hide().addClass('NB-story-comment-reply-hidden');
options.$reply.after($form);
} else {
@ -172,7 +170,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
var $submit = $(".NB-modal-submit-button", $form);
var comment_user_id = this.model.get('user_id');
var comment_reply = $('.NB-story-comment-reply-comments', $form).val();
var original_message = $form.data('original_message');
var reply_id = $form.data('reply_id');
if (!comment_reply || comment_reply.length <= 1) {
this.remove_social_comment_reply_form();
@ -189,7 +187,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
$submit.addClass('NB-disabled').text('Posting...');
NEWSBLUR.assets.save_comment_reply(this.options.story.id, this.options.story.get('story_feed_id'),
comment_user_id, comment_reply,
original_message,
reply_id,
_.bind(function(data) {
if (this.options.on_social_page) {
this.options.story_comments_view.replace_comment(this.model.get('user_id'), data);

View file

@ -9,6 +9,7 @@ from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.core.mail import mail_admins
from django.db.models.query import QuerySet
from mongoengine.queryset import QuerySet as MongoQuerySet
from bson.objectid import ObjectId
import sys
import datetime
@ -45,7 +46,7 @@ def json_encode(data, *args, **kwargs):
# Same as for lists above.
elif isinstance(data, dict):
ret = _dict(data)
elif isinstance(data, Decimal):
elif isinstance(data, (Decimal, ObjectId)):
# json.dumps() cant handle Decimal
ret = str(data)
elif isinstance(data, models.query.QuerySet):

View file

@ -78,7 +78,7 @@ def _mongodb_decode_wire_protocol(message):
try:
if message[zidx:]:
msg = bson.decode_all(message[zidx:])
except Exception, e:
except InvalidBSON:
msg = 'invalid bson'
return { 'op': op, 'collection': collection_name,
'msg_id': msg_id, 'skip': skip, 'limit': limit,