Merge branch 'master' into social

* master:
  Fixing broken opml export due to missing feed icons.
  Fixing slight bug in how active premiums were applied to feed update times.
  Shift+U switches between read and unread. Thanks to @MLACitation: https://twitter.com/mlacitation/status/155371014560288768
  Adding active_premium count. Setting a minimum of 1 hour fetches for feeds with any active premiums.
  Allowing username refinement in fetching starred stories.
  Increasing timeout on gunicorn to 2 min.
  Adding management command to fetch starred stories on command.
This commit is contained in:
Samuel Clay 2012-01-10 18:15:09 -08:00
commit febee37b27
8 changed files with 143 additions and 11 deletions

View file

View file

@ -0,0 +1,29 @@
from django.core.management.base import BaseCommand
from django.conf import settings
from django.contrib.auth.models import User
from apps.feed_import.models import GoogleReaderImporter
from optparse import make_option
from utils.management_functions import daemonize
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option("-d", "--daemon", dest="daemonize", action="store_true"),
make_option('-u', '--username', type='str', dest='username'),
make_option('-c', '--count', type='int', dest='count', default=1000),
make_option('-V', '--verbose', action='store_true',
dest='verbose', default=False, help='Verbose output.'),
)
def handle(self, *args, **options):
if options['daemonize']:
daemonize()
settings.LOG_TO_STREAM = True
try:
user = User.objects.get(username__icontains=options['username'])
except User.MultipleObjectsReturned:
user = User.objects.get(username=options['username'])
reader_importer = GoogleReaderImporter(user)
reader_importer.import_starred_items(count=options['count'])

View file

@ -0,0 +1,82 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Feed.active_premium_subscribers'
db.add_column('feeds', 'active_premium_subscribers', self.gf('django.db.models.fields.IntegerField')(default=-1, db_index=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'Feed.active_premium_subscribers'
db.delete_column('feeds', 'active_premium_subscribers')
models = {
'rss_feeds.duplicatefeed': {
'Meta': {'object_name': 'DuplicateFeed'},
'duplicate_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'duplicate_feed_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'duplicate_addresses'", 'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'rss_feeds.feed': {
'Meta': {'ordering': "['feed_title']", 'object_name': 'Feed', 'db_table': "'feeds'"},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'active_premium_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1', 'db_index': 'True'}),
'active_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1', 'db_index': 'True'}),
'average_stories_per_month': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'branch_from_feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']", 'null': 'True', 'blank': 'True'}),
'creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'days_to_trim': ('django.db.models.fields.IntegerField', [], {'default': '90'}),
'etag': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'exception_code': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'favicon_color': ('django.db.models.fields.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}),
'favicon_not_found': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'feed_address': ('django.db.models.fields.URLField', [], {'max_length': '255'}),
'feed_address_locked': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'feed_link': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '1000', 'null': 'True', 'blank': 'True'}),
'feed_link_locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'feed_title': ('django.db.models.fields.CharField', [], {'default': "'[Untitled]'", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'fetched_once': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_feed_exception': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'has_page': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'has_page_exception': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'hash_address_and_link': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_load_time': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'min_to_decay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'next_scheduled_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'num_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'premium_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'queued_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'stories_last_month': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'rss_feeds.feeddata': {
'Meta': {'object_name': 'FeedData'},
'feed': ('utils.fields.AutoOneToOneField', [], {'related_name': "'data'", 'unique': 'True', 'to': "orm['rss_feeds.Feed']"}),
'feed_classifier_counts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'feed_tagline': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'popular_authors': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
'popular_tags': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'story_count_history': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
},
'rss_feeds.feedloadtime': {
'Meta': {'object_name': 'FeedLoadtime'},
'date_accessed': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'loadtime': ('django.db.models.fields.FloatField', [], {})
}
}
complete_apps = ['rss_feeds']

View file

@ -46,6 +46,7 @@ class Feed(models.Model):
num_subscribers = models.IntegerField(default=-1)
active_subscribers = models.IntegerField(default=-1, db_index=True)
premium_subscribers = models.IntegerField(default=-1)
active_premium_subscribers = models.IntegerField(default=-1, db_index=True)
branch_from_feed = models.ForeignKey('Feed', blank=True, null=True, db_index=True)
last_update = models.DateTimeField(db_index=True)
fetched_once = models.BooleanField(default=False)
@ -95,7 +96,7 @@ class Feed(models.Model):
if include_favicon:
try:
feed_icon = MFeedIcon.objects.filter(feed_id=self.pk).first()
feed_icon = MFeedIcon.objects.get(feed_id=self.pk)
feed['favicon'] = feed_icon.data
except MFeedIcon.DoesNotExist:
pass
@ -371,6 +372,14 @@ class Feed(models.Model):
)
self.premium_subscribers = premium_subs.count()
active_premium_subscribers = UserSubscription.objects.filter(
feed__in=feed_ids,
active=True,
user__profile__is_premium=True,
user__profile__last_seen_on__gte=SUBSCRIBER_EXPIRE
)
self.active_premium_subscribers = active_premium_subscribers.count()
self.save()
if verbose:
@ -756,15 +765,15 @@ class Feed(models.Model):
from apps.reader.models import MUserStory
trim_cutoff = 500
if self.active_subscribers <= 1 and self.premium_subscribers < 1:
trim_cutoff = 50
elif self.active_subscribers <= 3 and self.premium_subscribers < 2:
trim_cutoff = 100
elif self.active_subscribers <= 5 and self.premium_subscribers < 3:
elif self.active_subscribers <= 3 and self.premium_subscribers < 2:
trim_cutoff = 150
elif self.active_subscribers <= 5 and self.premium_subscribers < 3:
trim_cutoff = 200
elif self.active_subscribers <= 10 and self.premium_subscribers < 4:
trim_cutoff = 250
trim_cutoff = 300
elif self.active_subscribers <= 25 and self.premium_subscribers < 5:
trim_cutoff = 350
trim_cutoff = 400
stories = MStory.objects(
story_feed_id=self.pk,
).order_by('-story_date')
@ -934,7 +943,7 @@ class Feed(models.Model):
# print 'New/updated story: %s' % (story),
return story_in_system, story_has_changed
def get_next_scheduled_update(self, force=False):
def get_next_scheduled_update(self, force=False, verbose=True):
if self.min_to_decay and not force:
random_factor = random.randint(0, self.min_to_decay) / 4
return self.min_to_decay, random_factor
@ -976,7 +985,15 @@ class Feed(models.Model):
elif self.last_load_time >= 200:
slow_punishment = 6 * self.last_load_time
total = max(4, int(updates_per_day_delay + subscriber_bonus + slow_punishment))
# print "[%s] %s (%s-%s), %s, %s: %s" % (self, updates_per_day_delay, updates_per_day, self.num_subscribers, subscriber_bonus, slow_punishment, total)
if self.active_premium_subscribers > 0:
total = min(total, 60) # 1 hour minimum for premiums
if verbose:
print "[%s] %s (%s/%s/%s/%s), %s, %s: %s" % (self, updates_per_day_delay,
self.num_subscribers, self.active_subscribers,
self.premium_subscribers, self.active_premium_subscribers,
subscriber_bonus, slow_punishment, total)
random_factor = random.randint(0, total) / 4
return total, random_factor*2

View file

@ -97,6 +97,7 @@ def load_feed_statistics(request, feed_id):
stats['last_load_time'] = feed.last_load_time
stats['premium_subscribers'] = feed.premium_subscribers
stats['active_subscribers'] = feed.active_subscribers
stats['active_premium_subscribers'] = feed.active_premium_subscribers
# Classifier counts
stats['classifier_counts'] = json.decode(feed.data.feed_classifier_counts)

View file

@ -12,6 +12,6 @@ accesslog = "/home/sclay/newsblur/logs/production.log"
errorlog = "/home/sclay/newsblur/logs/errors.log"
loglevel = "debug"
name = "newsblur"
timeout = 60
timeout = 120
max_requests = 1000
workers = numCPUs()

View file

@ -7088,8 +7088,11 @@
});
$document.bind('keydown', 'shift+u', function(e) {
e.preventDefault();
if (self.active_story) {
var story_id = self.active_story.id;
var story_id = self.active_story.id;
console.log(["self.active_story", self.active_story]);
if (self.active_story && !self.active_story.read_status) {
self.mark_story_as_read(story_id);
} else if (self.active_story && self.active_story.read_status) {
self.mark_story_as_unread(story_id);
}
});