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):
user_id = mongo.IntField()
activity_date = mongo.DateTimeField(default=datetime.datetime.now)
activity_type = mongo.StringField()
activity_content = mongo.StringField()
date = mongo.DateTimeField(default=datetime.datetime.now)
activity = mongo.StringField()
title = mongo.StringField()
content = mongo.StringField()
activity_user_id = mongo.IntField()
feed_id = mongo.IntField()
content_id = mongo.StringField()
meta = {
'collection': 'interactions',
'indexes': [('user_id', 'activity_date')],
'indexes': [('user_id', 'date'), 'activity'],
'allow_inheritance': False,
'index_drop_dups': True,
'ordering': ['-date'],
}
def __unicode__(self):
user = User.objects.get(pk=self.user_id)
return "%s on %s: %s - %s" % (user.username, self.activity_date,
self.activity_type, self.activity_content[:20])
activity_user = self.activity_user_id and User.objects.get(pk=self.activity_user_id)
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
def new_follow(cls, follower_user_id, followee_user_id):
cls.objects.create(user_id=followee_user_id,
activity_user_id=follower_user_id,
activity_type='follow')
activity='follow')
@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,
activity_user_id=reply_user_id,
activity_type='reply',
activity_content=reply_content)
activity='comment_reply',
content=reply_content)
@classmethod
def new_starred_story(cls, user_id, story_title, story_feed_id):
feed = Feed.objects.get(pk=story_feed_id)
def new_reply_reply(cls, user_id, reply_user_id, reply_content):
cls.objects.create(user_id=user_id,
activity_type='star',
activity_content=story_title)
activity_user_id=reply_user_id,
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):

View file

@ -22,9 +22,10 @@ from collections import defaultdict
from operator import itemgetter
from apps.recommendations.models import RecommendedFeed
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.profile.models import Profile
from apps.profile.models import Profile, MInteraction
from apps.reader.models import UserSubscription, UserSubscriptionFolders, MUserStory, Feature
from apps.reader.forms import SignupForm, LoginForm, FeatureForm
from apps.rss_feeds.models import MFeedIcon
@ -1199,6 +1200,10 @@ def mark_story_as_starred(request):
defaults=story_values)
if created:
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:
logging.user(request, "~FC~BRAlready stared:~SN~FC ~SB%s" % (story[0].story_title[:50]))
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 get_classifiers_for_user
from apps.reader.models import MUserStory, UserSubscription
from apps.profile.models import MInteraction
from utils import json_functions as json
from utils import log as logging
from utils import PyRSS2Gen as RSS
@ -252,14 +253,25 @@ def save_comment_reply(request):
reply.comments = reply_comments
shared_story.replies.append(reply)
shared_story.save()
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()
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 = [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}
def shared_stories_public(request, username):
@ -299,7 +311,7 @@ def profile(request):
def load_user_profile(request):
social_profile, _ = MSocialProfile.objects.get_or_create(user_id=request.user.pk)
social_services, _ = MSocialServices.objects.get_or_create(user_id=request.user.pk)
return {
'services': social_services,
'user_profile': social_profile.to_json(full=True),

View file

@ -7707,3 +7707,60 @@ background: transparent;
color: #909090;
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) {
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'
});
},

View file

@ -7567,6 +7567,23 @@
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 =====================================================
var clicked = false;

View file

@ -6,7 +6,8 @@ NEWSBLUR.ReaderSocialProfile = function(user_id, options) {
this.options = $.extend({}, defaults, options);
this.model = NEWSBLUR.AssetModel.reader();
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);
};
@ -19,7 +20,7 @@ _.extend(NEWSBLUR.ReaderSocialProfile.prototype, {
this.make_modal();
this.open_modal();
_.defer(_.bind(this.fetch_profile, this, user_id));
this.$modal.bind('click', $.rescope(this.handle_click, this));
},

View file

@ -1,18 +1,60 @@
{% load utils_tags typogrify_tags statistics_tags %}
{% if interactions %}
<div class="NB-module NB-module-features">
<h5 class="NB-module-header">
<div class="NB-module-header-right">
<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-previous-page NB-disabled"></a>
</div>
Interactions
</h5>
<div class="NB-interactions">
Follow, comments, replies, starred stories.
</div>
<h5 class="NB-module-header">
<div class="NB-module-header-right">
<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-previous-page NB-disabled"></a>
</div>
Interactions
</h5>
</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 import template
from django.contrib.auth.models import User
from django.conf import settings
from utils.user_functions import get_user
from vendor.timezones.utilities import localtime_for_timezone
from django import template
from apps.reader.forms import FeatureForm
from apps.reader.models import Feature
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()
@ -34,14 +37,32 @@ def render_features_module(context):
@register.inclusion_tag('reader/interactions_module.xhtml', takes_context=True)
def render_interactions_module(context):
user = get_user(context['user'])
interactions = MInteraction.objects.filter(user_id=user.pk)[0:5]
user = get_user(context['user'])
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 {
'user': user,
'interactions': interactions,
'users': users,
'social_profiles': social_profiles,
}
@register.filter
def get(h, key):
print h, key
return h[key]
@register.filter
def get_range( value ):
"""