Showing interactions in module. Adding interactivity.

This commit is contained in:
Samuel Clay 2012-04-10 17:28:00 -07:00
parent 6916aa179b
commit b9d8b91f6d
9 changed files with 212 additions and 40 deletions

View file

@ -179,41 +179,55 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
class MInteraction(mongo.Document): class MInteraction(mongo.Document):
user_id = mongo.IntField() user_id = mongo.IntField()
activity_date = mongo.DateTimeField(default=datetime.datetime.now) date = mongo.DateTimeField(default=datetime.datetime.now)
activity_type = mongo.StringField() activity = mongo.StringField()
activity_content = mongo.StringField() title = mongo.StringField()
content = mongo.StringField()
activity_user_id = mongo.IntField() activity_user_id = mongo.IntField()
feed_id = mongo.IntField()
content_id = mongo.StringField()
meta = { meta = {
'collection': 'interactions', 'collection': 'interactions',
'indexes': [('user_id', 'activity_date')], 'indexes': [('user_id', 'date'), 'activity'],
'allow_inheritance': False, 'allow_inheritance': False,
'index_drop_dups': True, 'index_drop_dups': True,
'ordering': ['-date'],
} }
def __unicode__(self): def __unicode__(self):
user = User.objects.get(pk=self.user_id) user = User.objects.get(pk=self.user_id)
return "%s on %s: %s - %s" % (user.username, self.activity_date, activity_user = self.activity_user_id and User.objects.get(pk=self.activity_user_id)
self.activity_type, self.activity_content[:20]) return "<%s> %s on %s: %s - %s" % (user.username, activity_user and activity_user.username, self.date,
self.activity, self.content and self.content[:20])
@classmethod @classmethod
def new_follow(cls, follower_user_id, followee_user_id): def new_follow(cls, follower_user_id, followee_user_id):
cls.objects.create(user_id=followee_user_id, cls.objects.create(user_id=followee_user_id,
activity_user_id=follower_user_id, activity_user_id=follower_user_id,
activity_type='follow') activity='follow')
@classmethod @classmethod
def new_reply(cls, user_id, reply_user_id, reply_content): def new_comment_reply(cls, user_id, reply_user_id, reply_content):
cls.objects.create(user_id=user_id, cls.objects.create(user_id=user_id,
activity_user_id=reply_user_id, activity_user_id=reply_user_id,
activity_type='reply', activity='comment_reply',
activity_content=reply_content) content=reply_content)
@classmethod @classmethod
def new_starred_story(cls, user_id, story_title, story_feed_id): def new_reply_reply(cls, user_id, reply_user_id, reply_content):
feed = Feed.objects.get(pk=story_feed_id)
cls.objects.create(user_id=user_id, cls.objects.create(user_id=user_id,
activity_type='star', activity_user_id=reply_user_id,
activity_content=story_title) activity='reply_reply',
content=reply_content)
@classmethod
def new_starred_story(cls, user_id, story_title, story_feed_id, story_id):
cls.objects.create(user_id=user_id,
activity='star',
content=story_title,
feed_id=story_feed_id,
content_id=story_id)
def create_profile(sender, instance, created, **kwargs): def create_profile(sender, instance, created, **kwargs):

View file

@ -22,9 +22,10 @@ from collections import defaultdict
from operator import itemgetter from operator import itemgetter
from apps.recommendations.models import RecommendedFeed from apps.recommendations.models import RecommendedFeed
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds
from apps.analyzer.models import apply_classifier_authors, apply_classifier_tags
from apps.analyzer.models import get_classifiers_for_user from apps.analyzer.models import get_classifiers_for_user
from apps.profile.models import Profile from apps.profile.models import Profile, MInteraction
from apps.reader.models import UserSubscription, UserSubscriptionFolders, MUserStory, Feature from apps.reader.models import UserSubscription, UserSubscriptionFolders, MUserStory, Feature
from apps.reader.forms import SignupForm, LoginForm, FeatureForm from apps.reader.forms import SignupForm, LoginForm, FeatureForm
from apps.rss_feeds.models import MFeedIcon from apps.rss_feeds.models import MFeedIcon
@ -1199,6 +1200,10 @@ def mark_story_as_starred(request):
defaults=story_values) defaults=story_values)
if created: if created:
logging.user(request, "~FCStarring: ~SB%s" % (story[0].story_title[:50])) logging.user(request, "~FCStarring: ~SB%s" % (story[0].story_title[:50]))
MInteraction.new_starred_story(user_id=request.user.pk,
story_title=story[0].story_title,
story_feed_id=feed_id,
content_id=starred_story.story_guid)
else: else:
logging.user(request, "~FC~BRAlready stared:~SN~FC ~SB%s" % (story[0].story_title[:50])) logging.user(request, "~FC~BRAlready stared:~SN~FC ~SB%s" % (story[0].story_title[:50]))
else: else:

View file

@ -12,6 +12,7 @@ from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifie
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags
from apps.analyzer.models import get_classifiers_for_user from apps.analyzer.models import get_classifiers_for_user
from apps.reader.models import MUserStory, UserSubscription from apps.reader.models import MUserStory, UserSubscription
from apps.profile.models import MInteraction
from utils import json_functions as json from utils import json_functions as json
from utils import log as logging from utils import log as logging
from utils import PyRSS2Gen as RSS from utils import PyRSS2Gen as RSS
@ -252,14 +253,25 @@ def save_comment_reply(request):
reply.comments = reply_comments reply.comments = reply_comments
shared_story.replies.append(reply) shared_story.replies.append(reply)
shared_story.save() shared_story.save()
logging.user(request, "~FCReplying to comment in: ~SB~FM%s (~FB%s~FM)" % (story.story_title[:50], reply_comments[:100])) logging.user(request, "~FCReplying to comment in: ~SB~FM%s (~FB%s~FM)" % (story.story_title[:50], reply_comments[:100]))
comment = shared_story.comments_with_author() comment = shared_story.comments_with_author()
profile_user_ids = set([comment['user_id']]) profile_user_ids = set([comment['user_id']])
profile_user_ids = profile_user_ids.union([reply['user_id'] for reply in comment['replies']]) reply_user_ids = [reply['user_id'] for reply 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 = MSocialProfile.objects.filter(user_id__in=list(profile_user_ids))
profiles = [profile.to_json(compact=True) for profile in profiles] profiles = [profile.to_json(compact=True) for profile in profiles]
# Interaction for every other replier and original commenter
MInteraction.new_comment_reply(user_id=comment['user_id'],
reply_user_id=request.user.pk,
reply_content=reply_comments)
for user_id in reply_user_ids.difference([comment['user_id']]):
MInteraction.new_reply_reply(user_id=user_id,
reply_user_id=request.user.pk,
reply_content=reply_comments)
return {'code': code, 'comment': comment, 'user_profiles': profiles} return {'code': code, 'comment': comment, 'user_profiles': profiles}
def shared_stories_public(request, username): def shared_stories_public(request, username):
@ -299,7 +311,7 @@ def profile(request):
def load_user_profile(request): def load_user_profile(request):
social_profile, _ = MSocialProfile.objects.get_or_create(user_id=request.user.pk) social_profile, _ = MSocialProfile.objects.get_or_create(user_id=request.user.pk)
social_services, _ = MSocialServices.objects.get_or_create(user_id=request.user.pk) social_services, _ = MSocialServices.objects.get_or_create(user_id=request.user.pk)
return { return {
'services': social_services, 'services': social_services,
'user_profile': social_profile.to_json(full=True), 'user_profile': social_profile.to_json(full=True),

View file

@ -7707,3 +7707,60 @@ background: transparent;
color: #909090; color: #909090;
text-transform: uppercase; text-transform: uppercase;
} }
/* ======================= */
/* = Interactions Module = */
/* ======================= */
.NB-interactions {
list-style: none;
padding: 0;
margin: 12px 0;
}
.NB-interaction {
list-style: none;
position: relative;
margin: 0;
padding: 6px 12px 6px 36px;
border-bottom: 1px solid #F0F0F0;
overflow: hidden;
}
.NB-interaction:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 0;
}
.NB-interaction-photo {
position: absolute;
width: 16px;
height: 16px;
border-radius: 3px;
left: 12px;
top: 10px;
cursor: pointer;
}
.NB-interaction-date {
color: #B0B0B0;
font-size: 11px;
float: right;
text-transform: uppercase;
padding: 4px 0 4px 4px;
}
.NB-interaction-title {
font-size: 13px;
line-height: 18px;
color: #404040;
padding: 2px 0 0 0;
}
.NB-interaction-content {
font-size: 11px;
padding-top: 2px;
line-height: 14px;
color: #808080;
}
.NB-interaction-username {
cursor: pointer;
}
.NB-interaction-starred-story-title {
cursor: pointer;
}

View file

@ -1123,7 +1123,10 @@ NEWSBLUR.AssetModel.Reader.prototype = {
}, },
fetch_user_profile: function(user_id, callback) { fetch_user_profile: function(user_id, callback) {
this.make_request('/social/profile', {'user_id': user_id}, callback, callback, { this.make_request('/social/profile', {'user_id': user_id}, _.bind(function(data) {
this.add_user_profiles(data.profiles);
callback(data);
}, this), callback, {
request_type: 'GET' request_type: 'GET'
}); });
}, },

View file

@ -7567,6 +7567,23 @@
self.save_social_comment_reply($comment); self.save_social_comment_reply($comment);
}); });
// = Interactions Module ==========================================
$.targetIs(e, { tagSelector: '.NB-interaction-username' }, function($t, $p){
e.preventDefault();
var user_id = $t.data('userId');
var username = $t.text();
self.model.add_user_profiles([{user_id: user_id, username: username}]);
self.open_social_profile_modal(user_id);
});
$.targetIs(e, { tagSelector: '.NB-interaction-profile-photo' }, function($t, $p){
e.preventDefault();
var user_id = $t.data('userId');
var username = $t.closest('.NB-interaction').find('.NB-interaction-username').text();
self.model.add_user_profiles([{user_id: user_id, username: username}]);
self.open_social_profile_modal(user_id);
});
// = One-offs ===================================================== // = One-offs =====================================================
var clicked = false; var clicked = false;

View file

@ -6,7 +6,8 @@ NEWSBLUR.ReaderSocialProfile = function(user_id, options) {
this.options = $.extend({}, defaults, options); this.options = $.extend({}, defaults, options);
this.model = NEWSBLUR.AssetModel.reader(); this.model = NEWSBLUR.AssetModel.reader();
this.profiles = new NEWSBLUR.Collections.Users(); this.profiles = new NEWSBLUR.Collections.Users();
user_id = _.string.ltrim(user_id, 'social:'); user_id = parseInt(_.string.ltrim(user_id, 'social:'), 10);
console.log(["user_id", user_id]);
this.runner(user_id); this.runner(user_id);
}; };
@ -19,7 +20,7 @@ _.extend(NEWSBLUR.ReaderSocialProfile.prototype, {
this.make_modal(); this.make_modal();
this.open_modal(); this.open_modal();
_.defer(_.bind(this.fetch_profile, this, user_id)); _.defer(_.bind(this.fetch_profile, this, user_id));
this.$modal.bind('click', $.rescope(this.handle_click, this)); this.$modal.bind('click', $.rescope(this.handle_click, this));
}, },

View file

@ -1,18 +1,60 @@
{% load utils_tags typogrify_tags statistics_tags %} {% load utils_tags typogrify_tags statistics_tags %}
{% if interactions %}
<div class="NB-module NB-module-features"> <div class="NB-module NB-module-features">
<h5 class="NB-module-header"> <h5 class="NB-module-header">
<div class="NB-module-header-right"> <div class="NB-module-header-right">
<div class="NB-spinner NB-left"></div> <div class="NB-spinner NB-left"></div>
<a href="#" class="NB-module-direction NB-module-next-page NB-javascript"></a> <a href="#" class="NB-module-direction NB-module-next-page NB-javascript"></a>
<a href="#" class="NB-module-direction NB-module-previous-page NB-disabled"></a> <a href="#" class="NB-module-direction NB-module-previous-page NB-disabled"></a>
</div> </div>
Interactions Interactions
</h5> </h5>
<div class="NB-interactions">
Follow, comments, replies, starred stories.
</div>
</div> <ul class="NB-interactions">
{% for interaction in interactions %}
<li class="NB-interaction NB-interaction-{{ interaction.activity }}"
{% if interaction.content_id %}data-content-id="{{ interaction.content_id }}"{% endif %}>
{% if interaction.activity == 'follow' %}
<img class="NB-interaction-photo NB-interaction-profile-photo" src="{{ interaction.photo_url }}" data-user-id="{{ interaction.activity_user_id }}">
<div class="NB-interaction-date">
{{ interaction.date }} ago
</div>
<div class="NB-interaction-title">
<span class="NB-interaction-username NB-splash-link" data-user-id="{{ interaction.activity_user_id }}">
{{ interaction.activity_user.username }}
</span> started following you.
</div>
{% endif %}
{% if interaction.activity == 'star' %}
<img class="NB-interaction-photo" src="/rss_feeds/icon/{{ interaction.feed_id }}">
<div class="NB-interaction-date">
{{ interaction.date }} ago
</div>
<div class="NB-interaction-title">
You starred "<span class="NB-interaction-starred-story-title NB-splash-link">{{ interaction.content|truncatewords:8 }}</span>".
</div>
{% endif %}
{% if interaction.activity == 'comment_reply' or interaction.activity == 'reply_reply' %}
<img class="NB-interaction-photo NB-interaction-profile-photo" src="{{ interaction.photo_url }}" data-user-id="{{ interaction.activity_user_id }}">
<div class="NB-interaction-date">
{{ interaction.date }} ago
</div>
<div class="NB-interaction-title">
<span class="NB-interaction-username NB-splash-link" data-user-id="{{ interaction.activity_user_id }}">
{{ interaction.activity_user.username }}
</span> replied to your {% if interaction.activity == 'comment_reply' %}comment{% else %}reply{% endif %}:
</div>
<div class="NB-interaction-content">
"<span class="NB-interaction-starred-story-title NB-splash-link">{{ interaction.content|truncatewords:16 }}</span>"
</div>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}

View file

@ -1,11 +1,14 @@
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django import template from django.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from utils.user_functions import get_user from django import template
from vendor.timezones.utilities import localtime_for_timezone
from apps.reader.forms import FeatureForm from apps.reader.forms import FeatureForm
from apps.reader.models import Feature from apps.reader.models import Feature
from apps.profile.models import MInteraction from apps.profile.models import MInteraction
from apps.social.models import MSocialProfile
from vendor.timezones.utilities import localtime_for_timezone
from utils.feed_functions import relative_timesince
from utils.user_functions import get_user
register = template.Library() register = template.Library()
@ -34,14 +37,32 @@ def render_features_module(context):
@register.inclusion_tag('reader/interactions_module.xhtml', takes_context=True) @register.inclusion_tag('reader/interactions_module.xhtml', takes_context=True)
def render_interactions_module(context): def render_interactions_module(context):
user = get_user(context['user']) user = get_user(context['user'])
interactions = MInteraction.objects.filter(user_id=user.pk)[0:5] interactions_db = MInteraction.objects.filter(user_id=user.pk)[0:5]
user_ids = [i.activity_user_id for i in interactions_db if i.activity_user_id]
users = dict((u.pk, u) for u in User.objects.filter(pk__in=user_ids))
social_profiles = dict((p.user_id, p) for p in MSocialProfile.objects.filter(user_id__in=user_ids))
interactions = []
for interaction_db in interactions_db:
interaction = interaction_db.to_mongo()
interaction['photo_url'] = getattr(social_profiles.get(interaction_db.activity_user_id), 'photo_url', None)
interaction['activity_user'] = social_profiles.get(interaction_db.activity_user_id)
interaction['date'] = relative_timesince(interaction_db.date)
interactions.append(interaction)
return { return {
'user': user, 'user': user,
'interactions': interactions, 'interactions': interactions,
'users': users,
'social_profiles': social_profiles,
} }
@register.filter
def get(h, key):
print h, key
return h[key]
@register.filter @register.filter
def get_range( value ): def get_range( value ):
""" """