diff --git a/apps/analyzer/models.py b/apps/analyzer/models.py index ff8bef923..1c779c844 100644 --- a/apps/analyzer/models.py +++ b/apps/analyzer/models.py @@ -2,7 +2,6 @@ from django.db import models from django.contrib.auth.models import User import datetime from apps.rss_feeds.models import Feed, Story, StoryAuthor, Tag -from apps.reader.models import UserSubscription, UserStory class FeatureCategory(models.Model): @@ -37,6 +36,7 @@ class ClassifierTitle(models.Model): def __unicode__(self): return '%s: %s (%s)' % (self.user, self.title, self.feed) + class ClassifierAuthor(models.Model): user = models.ForeignKey(User) score = models.SmallIntegerField() @@ -47,7 +47,13 @@ class ClassifierAuthor(models.Model): def __unicode__(self): return '%s: %s (%s)' % (self.user, self.author.author_name, self.feed) - + + def apply_classifier(self, story): + if story['author'] == self.author: + return True + return False + + class ClassifierFeed(models.Model): user = models.ForeignKey(User) score = models.SmallIntegerField() @@ -58,6 +64,12 @@ class ClassifierFeed(models.Model): def __unicode__(self): return '%s: %s' % (self.user, self.feed) + def apply_classifier(self, story): + if self.feed == story.feed: + return True + return False + + class ClassifierTag(models.Model): user = models.ForeignKey(User) score = models.SmallIntegerField() @@ -67,4 +79,32 @@ class ClassifierTag(models.Model): creation_date = models.DateTimeField(auto_now=True) def __unicode__(self): - return '%s: %s (%s)' % (self.user, self.tag.name, self.feed) \ No newline at end of file + return '%s: %s (%s)' % (self.user, self.tag.name, self.feed) + +def apply_classifier_titles(classifiers, story): + for classifier in classifiers: + if classifier.title in story['story_title']: + # print 'Titles: (%s) %s -- %s' % (classifier.title in story['story_title'], classifier.title, story['story_title']) + return classifier.score + return 0 + +def apply_classifier_feeds(classifiers, feed): + for classifier in classifiers: + if classifier.feed == feed: + # print 'Feeds: %s -- %s' % (classifier.feed, feed) + return classifier.score + return 0 + +def apply_classifier_authors(classifiers, story): + for classifier in classifiers: + if classifier.author.id == story['story_author_id']: + # print 'Authors: %s -- %s' % (classifier.author.id, story['story_author_id']) + return classifier.score + return 0 + +def apply_classifier_tags(classifiers, story): + for classifier in classifiers: + if classifier.tag.name in story['story_tags']: + # print 'Tags: (%s) %s -- %s' % (classifier.tag.name in story['story_tags'], classifier.tag.name, story['story_tags']) + return classifier.score + return 0 \ No newline at end of file diff --git a/apps/analyzer/views.py b/apps/analyzer/views.py index f99c527ed..f72ac0091 100644 --- a/apps/analyzer/views.py +++ b/apps/analyzer/views.py @@ -23,6 +23,7 @@ def index(requst): pass @require_POST +@json.json_view def save_classifier_story(request): post = request.POST facets = post.getlist('facet') @@ -65,9 +66,10 @@ def save_classifier_story(request): original_story=story) response = dict(code=code, message=message, payload=payload) - return HttpResponse(response) + return response @require_POST +@json.json_view def save_classifier_publisher(request): post = request.POST facets = post.getlist('facet') @@ -101,4 +103,4 @@ def save_classifier_publisher(request): feed=feed) response = dict(code=code, message=message, payload=payload) - return HttpResponse(response) \ No newline at end of file + return response \ No newline at end of file diff --git a/apps/reader/models.py b/apps/reader/models.py index 386654434..ddce516e2 100644 --- a/apps/reader/models.py +++ b/apps/reader/models.py @@ -5,17 +5,22 @@ import random from django.core.cache import cache from apps.rss_feeds.models import Feed, Story from utils import feedparser, object_manager, json +from apps.analyzer.models import ClassifierFeed, ClassifierAuthor, ClassifierTag, ClassifierTitle +from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags + +DAYS_OF_UNREAD = 14 class UserSubscription(models.Model): user = models.ForeignKey(User) feed = models.ForeignKey(Feed) - last_read_date = models.DateTimeField(default=datetime.datetime.now()-datetime.timedelta(days=7)) - mark_read_date = models.DateTimeField(default=datetime.datetime.now()-datetime.timedelta(days=7)) - unread_count = models.IntegerField(default=0) - unread_count_updated = models.DateTimeField( - default=datetime.datetime(2000,1,1) - ) - scores = models.CharField(max_length=256) + last_read_date = models.DateTimeField(default=datetime.datetime.now() + - datetime.timedelta(days=DAYS_OF_UNREAD)) + mark_read_date = models.DateTimeField(default=datetime.datetime.now() + - datetime.timedelta(days=DAYS_OF_UNREAD)) + unread_count_neutral = models.IntegerField(default=0) + unread_count_positive = models.IntegerField(default=0) + unread_count_negative = models.IntegerField(default=0) + unread_count_updated = models.DateTimeField(default=datetime.datetime(2000,1,1)) def __unicode__(self): return '[' + self.feed.feed_title + '] ' @@ -61,7 +66,7 @@ class UserSubscription(models.Model): # readstories.delete() def stories_newer_lastread(self): - return self.feed.new_stories_since_date(self.last_read_date) + return self.feed.new_stories_since_date(self.last_read_date).count() def stories_between_lastread_allread(self): story_count = Story.objects.filter( @@ -82,15 +87,51 @@ class UserSubscription(models.Model): new_subscription.save() def calculate_feed_scores(self): - scores = [] - for i in range(20): - # [0, 0, 2, 4, 5 ..] - scores.append(random.randint(0, 20)) + feed_scores = dict(negative=0, neutral=0, positive=0) + date_delta = datetime.datetime.now()-datetime.timedelta(days=DAYS_OF_UNREAD) + if date_delta < self.mark_read_date: + date_delta = self.mark_read_date + read_stories = UserStory.objects.select_related('story')\ + .filter(user=self.user, + feed=self.feed, + story__story_date__gte=date_delta) + stories_db = Story.objects.filter(story_date__gte=date_delta, + story_feed=self.feed)\ + .exclude(id__in=[rs.story.id for rs in read_stories]) + + stories = self.feed.format_stories(stories_db) + classifier_feeds = ClassifierFeed.objects.filter(user=self.user, feed=self.feed) + classifier_authors = ClassifierAuthor.objects.filter(user=self.user, feed=self.feed) + classifier_titles = ClassifierTitle.objects.filter(user=self.user, feed=self.feed) + classifier_tags = ClassifierTag.objects.filter(user=self.user, feed=self.feed) + + scores = { + 'feed': apply_classifier_feeds(classifier_feeds, self.feed), + } + + for story in stories: + scores.update({ + 'author': apply_classifier_authors(classifier_authors, story), + 'tags': apply_classifier_tags(classifier_tags, story), + 'title': apply_classifier_titles(classifier_titles, story), + }) + + max_score = max(scores['feed'], scores['author'], scores['tags'], scores['title']) + min_score = min(scores['feed'], scores['author'], scores['tags'], scores['title']) + if max_score > 0: + feed_scores['positive'] += 1 + if min_score < 0: + feed_scores['negative'] += 1 + if max_score == 0 and min_score == 0: + feed_scores['neutral'] += 1 + + self.unread_count_positive = feed_scores['positive'] + self.unread_count_neutral = feed_scores['neutral'] + self.unread_count_negative = feed_scores['negative'] - self.scores = json.encode(scores) self.save() - return scores + return def get_scores(self): scores = json.decode(self.scores) diff --git a/apps/reader/views.py b/apps/reader/views.py index 65e190eaf..a83165514 100644 --- a/apps/reader/views.py +++ b/apps/reader/views.py @@ -19,6 +19,8 @@ from django.core import serializers from django.utils.safestring import mark_safe from django.views.decorators.cache import cache_page from djangologging.decorators import suppress_logging_output +from apps.analyzer.models import ClassifierFeed, ClassifierAuthor, ClassifierTag, ClassifierTitle +from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags import logging import datetime import threading @@ -61,11 +63,10 @@ def load_feeds(request): feeds = [] folders = [] for sub in us: - # logging.info("UserSub Scores: %s" % sub.user_sub.scores) - feed_scores = sub.user_sub.scores or "[]" - sub.feed.scores = json.decode(feed_scores) try: - sub.feed.unread_count = sub.user_sub.unread_count + sub.feed.unread_count_positive = sub.user_sub.unread_count_positive + sub.feed.unread_count_neutral = sub.user_sub.unread_count_positive + sub.user_sub.unread_count_neutral + sub.feed.unread_count_negative = sub.user_sub.unread_count_positive + sub.user_sub.unread_count_neutral + sub.user_sub.unread_count_negative except: logging.warn("Subscription %s does not exist outside of Folder." % (sub.feed)) sub.delete() @@ -107,13 +108,20 @@ def load_single_feed(request): if force_update: feed.update(force_update) - usersub = UserSubscription.objects.get(user=user, feed=feed.id) + # Get intelligence classifier for user + classifier_feeds = ClassifierFeed.objects.filter(user=user, feed=feed) + classifier_authors = ClassifierAuthor.objects.filter(user=user, feed=feed) + classifier_titles = ClassifierTitle.objects.filter(user=user, feed=feed) + classifier_tags = ClassifierTag.objects.filter(user=user, feed=feed) + + usersub = UserSubscription.objects.get(user=user, feed=feed) # print "Feed: %s %s" % (feed, usersub) logging.debug("Feed: " + feed.feed_title) userstory = UserStory.objects.filter( user=user, - feed=feed.id + feed=feed.id, + read_date__gt=usersub.mark_read_date ).values() for story in stories: for o in userstory: @@ -125,8 +133,16 @@ def load_single_feed(request): story['read_status'] = 1 elif not story.get('read_status') and story['story_date'] > usersub.last_read_date: story['read_status'] = 0 + story['intelligence'] = { + 'feed': apply_classifier_feeds(classifier_feeds, feed), + 'author': apply_classifier_authors(classifier_authors, story), + 'tags': apply_classifier_tags(classifier_tags, story), + 'title': apply_classifier_titles(classifier_titles, story), + } # logging.debug("Story: %s" % story) + # Intelligence + all_tags = Tag.objects.filter(feed=feed)\ .annotate(stories_count=Count('story'))\ .order_by('-stories_count')[:20] @@ -138,7 +154,7 @@ def load_single_feed(request): feed_authors = [(author.author_name, author.stories_count) for author in all_authors\ if author.stories_count > 1] - context = dict(stories=stories, feed_tags=feed_tags, feed_authors=feed_authors, intelligence={}) + context = dict(stories=stories, feed_tags=feed_tags, feed_authors=feed_authors) data = json.encode(context) return HttpResponse(data, mimetype='application/json') diff --git a/apps/rss_feeds/models.py b/apps/rss_feeds/models.py index d0b25e221..336ba6a1d 100644 --- a/apps/rss_feeds/models.py +++ b/apps/rss_feeds/models.py @@ -46,9 +46,9 @@ class Feed(models.Model): return time.time() - time.mktime(self.last_update.timetuple()) def new_stories_since_date(self, date): - story_count = Story.objects.filter(story_date__gte=date, - story_feed=self).count() - return story_count + stories = Story.objects.filter(story_date__gte=date, + story_feed=self) + return stories def add_feed(self, feed_address, feed_link, feed_title): print locals() @@ -165,21 +165,29 @@ class Feed(models.Model): def get_stories(self, offset=0, limit=25): stories = cache.get('feed_stories:%s-%s-%s' % (self.id, offset, limit), []) - + if not stories: - stories_db = Story.objects.filter(story_feed=self).select_related('story_author')[offset:offset+limit] - for story_db in stories_db: - story = story_db.__dict__ - story_tags = story_db.tags.all() - story['story_tags'] = [tag.name for tag in story_tags] - story['short_parsed_date'] = format_story_link_date__short(story['story_date']) - story['long_parsed_date'] = format_story_link_date__long(story['story_date']) - story['story_authors'] = story_db.story_author.author_name - stories.append(story) + stories_db = Story.objects.filter(story_feed=self)\ + .select_related('story_author')[offset:offset+limit] + stories = self.format_stories(stories_db) cache.set('feed_stories:%s-%s-%s' % (self.id, offset, limit), stories) return stories + def format_stories(self, stories_db): + stories = [] + + for story_db in stories_db: + story = story_db.__dict__ + story_tags = story_db.tags.all() + story['story_tags'] = [tag.name for tag in story_tags] + story['short_parsed_date'] = format_story_link_date__short(story['story_date']) + story['long_parsed_date'] = format_story_link_date__long(story['story_date']) + story['story_authors'] = story_db.story_author.author_name + stories.append(story) + + return stories + def get_tags(self, entry): fcat = [] if entry.has_key('tags'): diff --git a/media/css/reader.css b/media/css/reader.css index 3296e2647..f9bd20d8d 100644 --- a/media/css/reader.css +++ b/media/css/reader.css @@ -152,7 +152,6 @@ a img { } #feed_list .feed_title { display: block; - font-weight: bold; padding: 4px 42px 2px 45px; text-decoration: none; color: #272727; @@ -161,10 +160,6 @@ a img { overflow: hidden; } -#feed_list .feed.no_unread_items .feed_title { - font-weight: normal; -} - #feed_list .feed.selected { background: #f6a828 url('../theme/images/ui-bg_highlight-hard_35_f6a828_1x100.png') 0 50% repeat-x; border-top: 1px solid #A8A8A8; @@ -178,8 +173,45 @@ a img { color: #FFF; padding: 0 6px; background-color: #8eb6e8; + display: none; } +#feed_list .unread_count_positive { + background-color: #559F4D; +} + +#feed_list .unread_count_neutral { + color: #303030; + background-color: #F9C72A; +} + +#feed_list .unread_count_negative { + background-color: #CC2A2E; +} + +#feed_list.unread_view_positive .feed.unread_positive .unread_count_positive { + display: block; +} + +#feed_list.unread_view_neutral .feed.unread_neutral .unread_count_neutral { + display: block; +} + +#feed_list.unread_view_negative .feed.unread_negative .unread_count_negative { + display: block; +} + +#feed_list.unread_view_positive .feed.unread_positive { + font-weight: bold; +} + +#feed_list.unread_view_neutral .feed.unread_neutral { + font-weight: bold; +} + +#feed_list.unread_view_negative .feed.unread_negative { + font-weight: bold; +} /* ================ */ /* = Story Titles = */ @@ -272,6 +304,18 @@ a img { background: transparent url('../img/icons/silk/bullet_orange.png') no-repeat 6px 50%; } +#story_titles .story.NB-story-positive { + background: transparent url('../img/icons/silk/bullet_green.png') no-repeat 6px 50%; +} + +#story_titles .story.NB-story-neutral { + background: transparent url('../img/icons/silk/bullet_yellow.png') no-repeat 6px 50%; +} + +#story_titles .story.NB-story-negative { + background: transparent url('../img/icons/silk/bullet_red.png') no-repeat 6px 50%; +} + #story_titles .story.NB-story-hover { background-color: #f0f0f0; } @@ -547,7 +591,7 @@ a img { .NB-taskbar .NB-task-view-page-to-feed-arrow { position: absolute; top: 8px; - left: 64px; + right: -8px; width: 16px; height: 16px; background: transparent url('../img/icons/silk/arrow_switch.png') no-repeat center 0px; @@ -645,6 +689,7 @@ a img { cursor: pointer; } .NB-taskbar .task_button.task_view_page { + position: relative; cursor: pointer; background: transparent url('../img/icons/silk/application_view_tile.png') no-repeat 8px 8px; } @@ -931,3 +976,40 @@ form.opml_import_form input { .simplemodal-wrap { overflow: auto; } + +/* ======================= */ +/* = Intelligence Slider = */ +/* ======================= */ + +.NB-taskbar-intelligence { + width: 100px; + position: absolute; + right: 18px; + top: 16px; + font-size: .5em; +} + +.NB-taskbar-intelligence .NB-taskbar-intelligence-indicator { + position: absolute; + top: -16px; + width: 12px; + height: 12px; +} + +.NB-taskbar-intelligence .NB-taskbar-intelligence-negative { + right: 94px; + background: transparent url(../img/icons/silk/bullet_red.png) no-repeat 0 0; +} + +.NB-taskbar-intelligence .NB-taskbar-intelligence-neutral { + right: 45px; + background: transparent url(../img/icons/silk/bullet_yellow.png) no-repeat 0 0; +} + +.NB-taskbar-intelligence .NB-taskbar-intelligence-positive { + right: -4px; + background: transparent url(../img/icons/silk/bullet_green.png) no-repeat 0 0; +} + +.NB-intelligence-slider { +} \ No newline at end of file diff --git a/media/js/newsblur/reader.js b/media/js/newsblur/reader.js index 433b77922..dbd712fac 100644 --- a/media/js/newsblur/reader.js +++ b/media/js/newsblur/reader.js @@ -8,6 +8,7 @@ this.$account_menu = $('.menu_button'); this.$feed_view = $('.NB-feed-story-view'); this.$story_iframe = $('.NB-feed-frame'); + this.$intelligence_slider = $('.NB-intelligence-slider'); this.model = NEWSBLUR.AssetModel.reader(); this.options = {}; @@ -37,6 +38,7 @@ this.handle_keystrokes(); // this.setup_taskbar_nav_left(); this.setup_feed_page_iframe_load(); + this.load_intelligence_slider(); }; NEWSBLUR.Reader.prototype = { @@ -49,14 +51,19 @@ var read = story.read_status ? 'read' : ''; + var score = this.compute_story_score(story); + var score_color = 'neutral'; + if (score > 0) score_color = 'positive'; + if (score < 0) score_color = 'negative'; var $story_tags = $.make('span', { className: 'NB-storytitles-tags'}); + for (var t in story.story_tags) { var tag = story.story_tags[t]; var $tag = $.make('span', { className: 'NB-storytitles-tag'}, tag).corners('4px'); $story_tags.append($tag); break; } - var $story_title = $.make('div', { className: 'story ' + read }, [ + var $story_title = $.make('div', { className: 'story ' + read + ' NB-story-' + score_color }, [ $.make('a', { href: story.story_permalink, className: 'story_title' }, [ $.make('span', { className: 'NB-storytitles-title' }, story.story_title), $.make('span', { className: 'NB-storytitles-author' }, story.story_authors), @@ -71,6 +78,22 @@ return $story_title; }, + compute_story_score: function(story) { + var score = 0; + var score_max = Math.max(story.intelligence['title'], + story.intelligence['author'], + story.intelligence['tags']); + var score_min = Math.min(story.intelligence['title'], + story.intelligence['author'], + story.intelligence['tags']); + if (score_max > 0) score = score_max; + else if (score_min < 0) score = score_min; + + if (score == 0) score = story.intelligence['feed']; + + return score; + }, + // ======== // = Page = // ======== @@ -211,9 +234,9 @@ $current_story = $('.story:first', this.$story_titles); $next_story = $current_story; } else if (direction == 1) { - $next_story = $current_story.next('.story'); + $next_story = $current_story.nextAll('.story:visible').eq(0); } else if (direction == -1) { - $next_story = $current_story.prev('.story'); + $next_story = $current_story.prevAll('.story:visible').eq(0); } var story_id = $('.story_id', $next_story).text(); @@ -307,7 +330,7 @@ var callback = function() { var $feed_list = self.$feed_list.empty(); var folders = self.model.folders; - + $('#story_taskbar').css({'display': 'block'}); // NEWSBLUR.log(['Subscriptions', {'folders':folders}]); for (fo in folders) { @@ -317,15 +340,38 @@ $.make('div', { className: 'feeds' }) ]); for (f in feeds) { - var $feed = $.make('div', { className: 'feed' }, [ - $.make('span', { className: 'unread_count' }, ''+feeds[f].unread_count), + var unread_class = ''; + if (feeds[f].unread_count_positive) { + unread_class += ' unread_positive'; + } + if (feeds[f].unread_count_neutral) { + unread_class += ' unread_neutral'; + } + if (feeds[f].unread_count_negative) { + unread_class += ' unread_negative'; + } + var $feed = $.make('div', { className: 'feed ' + unread_class }, [ + $.make('span', { + className: 'unread_count unread_count_positive ' + + (feeds[f].unread_count_positive + ? "unread_count_full" + : "unread_count_empty") + }, ''+feeds[f].unread_count_positive), + $.make('span', { + className: 'unread_count unread_count_neutral ' + + (feeds[f].unread_count_neutral + ? "unread_count_full" + : "unread_count_empty") + }, ''+feeds[f].unread_count_neutral), + $.make('span', { + className: 'unread_count unread_count_negative ' + + (feeds[f].unread_count_negative + ? "unread_count_full" + : "unread_count_empty") + }, ''+feeds[f].unread_count_negative), $.make('img', { className: 'feed_favicon', src: self.google_favicon_url + feeds[f].feed_link }), $.make('span', { className: 'feed_title' }, feeds[f].feed_title) ]).data('feed_id', feeds[f].id); - if (feeds[f].unread_count <= 0) { - $('.unread_count', $feed).css('display', 'none'); - $feed.addClass('no_unread_items'); - } $('.feeds', $folder).append($feed); } $feed_list.append($folder); @@ -778,7 +824,7 @@ var $feed_view = this.$feed_view; var $story = this.find_story_in_feed_view(story); - NEWSBLUR.log(['scroll_to_story_in_story_feed', story, $story]); + // NEWSBLUR.log(['scroll_to_story_in_story_feed', story, $story]); if ($story && $story.length) { if (this.story_view == 'feed' || this.page_view_showing_feed_view) { @@ -835,7 +881,7 @@ if (story) { var self = this; var $story = this.find_story_in_story_frame(story); - NEWSBLUR.log(['Prefetching story', s, story, $story]); + // NEWSBLUR.log(['Prefetching story', s, story, $story]); setTimeout(function() { // NEWSBLUR.log(['Fetching next story', s]); @@ -864,10 +910,10 @@ var $story, $stories = [], title_words, shortened_title, $reduced_stories = []; if (story.id in this.cache.iframe_stories || this.flags.story_frame_prefetched) { - NEWSBLUR.log(['Cached story frame', this.cache.iframe_stories[story.id], story]); + // NEWSBLUR.log(['Cached story frame', this.cache.iframe_stories[story.id], story]); return this.cache.iframe_stories[story.id]; } else { - NEWSBLUR.log(['Cache story frame miss', this.cache.iframe_stories, story]); + // NEWSBLUR.log(['Cache story frame miss', this.cache.iframe_stories, story]); } var $iframe_contents = $iframe.contents(); @@ -1021,12 +1067,49 @@ mark_story_as_read: function(story_id, $story_title) { var self = this; var feed_id = this.active_feed; - + var $feed_list = this.$feed_list; + var $feed = $('.feed.selected', $feed_list); + var callback = function() { + var unread_count_positive = parseInt($('.unread_count_positive', $feed).text(), 10); + var unread_count_neutral = parseInt($('.unread_count_neutral', $feed).text(), 10); + var unread_count_negative = parseInt($('.unread_count_negative', $feed).text(), 10); + NEWSBLUR.log(['marked read', unread_count_positive, unread_count_neutral, unread_count_negative, $story_title.is('.NB-story-positive'), $story_title.is('.NB-story-neutral'), $story_title.is('.NB-story-negative')]); + + if ($story_title.is('.NB-story-positive')) { + $('.unread_count_positive', $feed).text(unread_count_positive-1); + $('.unread_count_neutral', $feed).text(unread_count_neutral-1); + $('.unread_count_negative', $feed).text(unread_count_negative-1); + if (unread_count_positive == 1) { + $feed.removeClass('unread_positive'); + } else { + $feed.addClass('unread_positive'); + } + } else if ($story_title.is('.NB-story-neutral')) { + $('.unread_count_neutral', $feed).text(unread_count_neutral-1); + $('.unread_count_negative', $feed).text(unread_count_negative-1); + if (unread_count_neutral == 1) { + $feed.removeClass('unread_positive'); + $feed.removeClass('unread_neutral'); + } else { + $feed.addClass('unread_neutral'); + } + } else if ($story_title.is('.NB-story-negative')) { + $('.unread_count_negative', $feed).text(unread_count_negative-1); + if (unread_count_negative == 1) { + $feed.removeClass('unread_positive'); + $feed.removeClass('unread_neutral'); + $feed.removeClass('unread_negative'); + } else { + $feed.addClass('unread_negative'); + } + } + return; }; $story_title.addClass('read'); + if (NEWSBLUR.Globals.is_authenticated) { this.model.mark_story_as_read(story_id, feed_id, callback); } @@ -1373,7 +1456,83 @@ $taskbar_button.removeClass('active'); $('.taskbar_menu', $taskbar_button).removeShadow(); $(document).unbind('click.taskbar_menu'); + }, + + // ================ + // = Intelligence = + // ================ + + load_intelligence_slider: function() { + var self = this; + var $slider = this.$intelligence_slider; + var unread_view = NEWSBLUR.Globals.unread_view; + + this.switch_feed_view_unread_view(unread_view); + + $slider.slider({ + range: 'max', + min: -1, + max: 1, + step: 1, + value: NEWSBLUR.Globals.unread_view, + slide: function(e, ui) { + self.switch_feed_view_unread_view(ui.value); + }, + stop: function(e, ui) { + self.save_profile('unread_view', ui.value); + self.show_correct_story_titles_in_unread_view(); + } + }); + }, + + switch_feed_view_unread_view: function(unread_view) { + var $feed_list = this.$feed_list; + var unread_view_name = this.get_unread_view_name(unread_view); + + $feed_list.removeClass('unread_view_positive') + .removeClass('unread_view_neutral') + .removeClass('unread_view_negative') + .addClass('unread_view_'+unread_view_name); + }, + + get_unread_view_name: function(unread_view) { + return (unread_view > 0 + ? 'positive' + : unread_view < 0 + ? 'negative' + : 'neutral'); + }, + + show_correct_story_titles_in_unread_view: function() { + var unread_view = NEWSBLUR.Globals.unread_view; + var $story_titles = this.$story_titles; + var unread_view_name = this.get_unread_view_name(unread_view); + var $stories_show, $stories_hide; + + if (unread_view_name == 'positive') { + $stories_show = $('.story').filter('.NB-story-positive'); + $stories_hide = $('.story').filter('.NB-story-neutral,.NB-story-negative'); + } else if (unread_view_name == 'neutral') { + $stories_show = $('.story').filter('.NB-story-positive,.NB-story-neutral'); + $stories_hide = $('.story').filter('.NB-story-negative'); + } else if (unread_view_name == 'negative') { + $stories_show = $('.story').filter('.NB-story-positive,.NB-story-neutral,.NB-story-negative'); + $stories_hide = $(); + } + + // NEWSBLUR.log(['showing correct stories', unread_view_name, $stories_show, $stories_hide]); + $stories_show.slideDown(500); + $stories_hide.slideUp(500); + }, + + // =========== + // = Profile = + // =========== + + save_profile: function(key, value) { + NEWSBLUR.Globals[key] = value; } + }; })(jQuery); diff --git a/settings.py b/settings.py index 21e294ad2..8a2cfa5c0 100644 --- a/settings.py +++ b/settings.py @@ -173,7 +173,8 @@ COMPRESS_JS = { 'js/jquery.ajaxupload.js', 'js/jquery.simplemodal-1.3.js', 'js/jquery.color.js', - 'js/jquery-ui-1.7.2.custom.min.js', + 'js/jquery.ui.core.js', + 'js/jquery.ui.slider.js', 'js/jquery.layout.js', 'js/jquery.fieldselection.js', @@ -190,6 +191,7 @@ COMPRESS_CSS = { 'all': { 'source_filenames': ( 'css/reader.css', + 'css/jquery-ui/jquery.theme.css', ), 'output_filename': 'release/all-compressed-?.css' } diff --git a/templates/base.html b/templates/base.html index 0320cf24a..854eed398 100644 --- a/templates/base.html +++ b/templates/base.html @@ -16,7 +16,8 @@ NEWSBLUR.Globals = { 'is_authenticated': {% if user.is_authenticated %}true{% else %}false{% endif %}, 'is_anonymous': {% if user.is_anonymous %}true{% else %}false{% endif %}, - 'username': "{{ user.username|safe }}" + 'username': "{{ user.username|safe }}", + 'unread_view': 0{# user.profile.unread_view|safe #} }; diff --git a/templates/reader/feeds.xhtml b/templates/reader/feeds.xhtml index 30d2e6027..da6080d4c 100644 --- a/templates/reader/feeds.xhtml +++ b/templates/reader/feeds.xhtml @@ -10,6 +10,12 @@
  • +
    +
    +
    +
    +
    +
    @@ -56,10 +62,11 @@
    -