mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-04-13 09:42:01 +00:00
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:
parent
dcbb305f6e
commit
8eb106612a
10 changed files with 102 additions and 33 deletions
33
apps/social/migrations/0003_reply_id.py
Normal file
33
apps/social/migrations/0003_reply_id.py
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue