mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
Adding popularity query email.
This commit is contained in:
parent
52a76e7811
commit
0235cb38b0
11 changed files with 232 additions and 18 deletions
32
apps/analyzer/forms.py
Normal file
32
apps/analyzer/forms.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import re
|
||||
import requests
|
||||
from django import forms
|
||||
from vendor.zebra.forms import StripePaymentForm
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.models import User
|
||||
from apps.profile.models import change_password, blank_authenticate, MGiftCode
|
||||
from apps.social.models import MSocialProfile
|
||||
|
||||
class PopularityQueryForm(forms.Form):
|
||||
email = forms.CharField(widget=forms.TextInput(),
|
||||
label="Your email address",
|
||||
required=False)
|
||||
query = forms.CharField(widget=forms.TextInput(),
|
||||
label="Keywords",
|
||||
required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PopularityQueryForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_email(self):
|
||||
if not self.cleaned_data['email']:
|
||||
raise forms.ValidationError('Please enter in an email address.')
|
||||
|
||||
return self.cleaned_data['email']
|
||||
|
||||
def clean_query(self):
|
||||
if not self.cleaned_data['query']:
|
||||
raise forms.ValidationError('Please enter in a keyword search query.')
|
||||
|
||||
return self.cleaned_data['query']
|
|
@ -1,8 +1,14 @@
|
|||
import datetime
|
||||
import mongoengine as mongo
|
||||
from collections import defaultdict
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.conf import settings
|
||||
from apps.rss_feeds.models import Feed
|
||||
from apps.analyzer.tasks import EmailPopularityQuery
|
||||
from utils import log as logging
|
||||
|
||||
class FeatureCategory(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
|
@ -23,6 +29,44 @@ class Category(models.Model):
|
|||
def __unicode__(self):
|
||||
return '%s (%s)' % (self.category, self.count)
|
||||
|
||||
|
||||
class MPopularityQuery(mongo.Document):
|
||||
email = mongo.StringField()
|
||||
query = mongo.StringField()
|
||||
is_emailed = mongo.BooleanField()
|
||||
creation_date = mongo.DateTimeField(default=datetime.datetime.now)
|
||||
|
||||
meta = {
|
||||
'collection': 'popularity_query',
|
||||
'allow_inheritance': False,
|
||||
}
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s - \"%s\"" % (self.email, self.query)
|
||||
|
||||
def queue_email(self):
|
||||
EmailPopularityQuery.delay(pk=self.pk)
|
||||
|
||||
def send_email(self):
|
||||
filename = Feed.xls_query_popularity(self.query, limit=10)
|
||||
xlsx = open(filename, "r")
|
||||
|
||||
params = {
|
||||
'query': self.query
|
||||
}
|
||||
text = render_to_string('mail/email_popularity_query.txt', params)
|
||||
html = render_to_string('mail/email_popularity_query.xhtml', params)
|
||||
subject = "Keyword popularity spreadsheet: \"%s\"" % self.query
|
||||
msg = EmailMultiAlternatives(subject, text,
|
||||
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
|
||||
to=['<%s>' % (self.email)])
|
||||
msg.attach_alternative(html, "text/html")
|
||||
msg.attach(filename, xlsx.read(), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
msg.send()
|
||||
|
||||
logging.debug(" -> ~BB~FM~SBSent email for popularity query: %s" % self)
|
||||
|
||||
|
||||
class MClassifierTitle(mongo.Document):
|
||||
user_id = mongo.IntField()
|
||||
feed_id = mongo.IntField()
|
||||
|
@ -241,4 +285,4 @@ def sort_classifiers_by_feed(user, feed_ids=None,
|
|||
classifier_titles=classifier_titles[feed_id],
|
||||
classifier_tags=classifier_tags[feed_id])
|
||||
|
||||
return classifiers
|
||||
return classifiers
|
||||
|
|
16
apps/analyzer/tasks.py
Normal file
16
apps/analyzer/tasks.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
import datetime
|
||||
from celery.task import Task
|
||||
from utils import log as logging
|
||||
|
||||
class EmailPopularityQuery(Task):
|
||||
|
||||
def run(self, pk):
|
||||
from apps.analyzer.models import MPopularityQuery
|
||||
|
||||
query = MPopularityQuery.objects.get(pk=pk)
|
||||
logging.user(self.user, "~BB~FCRunning popularity query: ~SB%s" % query)
|
||||
|
||||
query.send_email()
|
||||
query.is_emailed = True
|
||||
query.save()
|
||||
|
|
@ -4,5 +4,6 @@ from apps.analyzer import views
|
|||
urlpatterns = patterns('',
|
||||
(r'^$', views.index),
|
||||
(r'^save/?', views.save_classifier),
|
||||
(r'^popularity/?', views.popularity_query),
|
||||
(r'^(?P<feed_id>\d+)', views.get_classifiers_feed),
|
||||
)
|
||||
|
|
|
@ -3,15 +3,19 @@ from utils import log as logging
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.conf import settings
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
from mongoengine.queryset import NotUniqueError
|
||||
from apps.rss_feeds.models import Feed
|
||||
from apps.reader.models import UserSubscription
|
||||
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
|
||||
from apps.analyzer.models import get_classifiers_for_user
|
||||
from apps.analyzer.models import get_classifiers_for_user, MPopularityQuery
|
||||
from apps.analyzer.forms import PopularityQueryForm
|
||||
from apps.social.models import MSocialSubscription
|
||||
from utils import json_functions as json
|
||||
from utils.user_functions import get_user
|
||||
from utils.user_functions import ajax_login_required
|
||||
from utils.view_functions import render_to
|
||||
|
||||
def index(requst):
|
||||
pass
|
||||
|
@ -116,4 +120,31 @@ def get_classifiers_feed(request, feed_id):
|
|||
response = dict(code=code, payload=payload)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def popularity_query(request):
|
||||
if request.method == 'POST':
|
||||
form = PopularityQueryForm(request.POST)
|
||||
if form.is_valid():
|
||||
logging.user(request.user, "~BC~FRPopularity query: ~SB%s~SN requests \"~SB~FM%s~SN~FR\"" % (request.POST['email'], request.POST['query']))
|
||||
query = MPopularityQuery.objects.create(email=request.POST['email'],
|
||||
query=request.POST['query'])
|
||||
query.queue_email()
|
||||
|
||||
response = render_to_response('analyzer/popularity_query.xhtml', {
|
||||
'success': True,
|
||||
'popularity_query_form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
response.set_cookie('newsblur_popularity_query', request.POST['query'])
|
||||
|
||||
return response
|
||||
else:
|
||||
logging.user(request.user, "~BC~FRFailed popularity query: ~SB%s~SN requests \"~SB~FM%s~SN~FR\"" % (request.POST['email'], request.POST['query']))
|
||||
else:
|
||||
logging.user(request.user, "~BC~FRPopularity query form loading")
|
||||
form = PopularityQueryForm(initial={'query': request.COOKIES.get('newsblur_popularity_query', "")})
|
||||
|
||||
response = render_to_response('analyzer/popularity_query.xhtml', {
|
||||
'popularity_query_form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
return response
|
||||
|
|
|
@ -1590,33 +1590,37 @@ class Feed(models.Model):
|
|||
@classmethod
|
||||
def xls_query_popularity(cls, queries, limit):
|
||||
import xlsxwriter
|
||||
workbook = xlsxwriter.Workbook('NewsBlurPopularity.xlsx')
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
|
||||
if isinstance(queries, unicode):
|
||||
queries = [q.strip() for q in queries.split(',')]
|
||||
|
||||
title = 'NewsBlur-%s.xlsx' % slugify('-'.join(queries))
|
||||
workbook = xlsxwriter.Workbook(title)
|
||||
bold = workbook.add_format({'bold': 1})
|
||||
date_format = workbook.add_format({'num_format': 'mmm d yyyy'})
|
||||
unread_format = workbook.add_format({'font_color': '#E0E0E0'})
|
||||
if isinstance(queries, str):
|
||||
queries = [q.strip() for q in queries.split(',')]
|
||||
|
||||
for query in queries:
|
||||
worksheet = workbook.add_worksheet(query)
|
||||
row = 1
|
||||
col = 0
|
||||
worksheet.write(0, col, 'Publisher', bold)
|
||||
worksheet.write(0, col, 'Publisher', bold)
|
||||
worksheet.set_column(col, col, 15); col += 1
|
||||
worksheet.write(0, col, 'Feed URL', bold)
|
||||
worksheet.set_column(col, col, 20); col += 1
|
||||
worksheet.write(0, col, 'Reach score', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 9); col += 1
|
||||
worksheet.write(0, col, '# subs', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 5); col += 1
|
||||
worksheet.write(0, col, '# readers', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.write(0, col, 'Read %', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.write(0, col, "read %", bold)
|
||||
worksheet.set_column(col, col, 6); col += 1
|
||||
worksheet.write(0, col, '# stories 30d', bold)
|
||||
worksheet.set_column(col, col, 10); col += 1
|
||||
worksheet.write(0, col, '# shared', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 7); col += 1
|
||||
worksheet.write(0, col, '# feed pos', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.write(0, col, '# feed neg', bold)
|
||||
|
@ -1624,9 +1628,9 @@ class Feed(models.Model):
|
|||
worksheet.write(0, col, 'Author', bold)
|
||||
worksheet.set_column(col, col, 15); col += 1
|
||||
worksheet.write(0, col, '# author pos', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 10); col += 1
|
||||
worksheet.write(0, col, '# author neg', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 10); col += 1
|
||||
worksheet.write(0, col, 'Story title', bold)
|
||||
worksheet.set_column(col, col, 30); col += 1
|
||||
worksheet.write(0, col, 'Story URL', bold)
|
||||
|
@ -1638,16 +1642,20 @@ class Feed(models.Model):
|
|||
worksheet.write(0, col, 'Tag Count', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.write(0, col, '# tag pos', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 7); col += 1
|
||||
worksheet.write(0, col, '# tag neg', bold)
|
||||
worksheet.set_column(col, col, 8); col += 1
|
||||
worksheet.set_column(col, col, 7); col += 1
|
||||
popularity = cls.query_popularity(query, limit=limit)
|
||||
|
||||
for feed in popularity:
|
||||
col = 0
|
||||
worksheet.write(row, col, feed['feed_title']); col += 1
|
||||
worksheet.write_url(row, col, feed['feed_url']); col += 1
|
||||
worksheet.write(row, col, feed['reach_score']); col += 1
|
||||
worksheet.write(row, col, "=%s*%s*%s" % (
|
||||
xl_rowcol_to_cell(row, col+2),
|
||||
xl_rowcol_to_cell(row, col+3),
|
||||
xl_rowcol_to_cell(row, col+4),
|
||||
)); col += 1
|
||||
worksheet.write(row, col, feed['num_subscribers']); col += 1
|
||||
worksheet.write(row, col, feed['reader_count']); col += 1
|
||||
worksheet.write(row, col, feed['read_pct']); col += 1
|
||||
|
@ -1688,6 +1696,7 @@ class Feed(models.Model):
|
|||
'value': 0,
|
||||
'format': unread_format})
|
||||
workbook.close()
|
||||
return title
|
||||
|
||||
def find_stories(self, query, order="newest", offset=0, limit=25):
|
||||
story_ids = SearchStory.query(feed_ids=[self.pk], query=query, order=order,
|
||||
|
|
|
@ -70,7 +70,13 @@
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
.NB-static-form .NB-label-right {
|
||||
margin: 0 0 24px 206px;
|
||||
width: 200px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.NB-static-form input.error,
|
||||
|
|
46
templates/analyzer/popularity_query.xhtml
Normal file
46
templates/analyzer/popularity_query.xhtml
Normal file
|
@ -0,0 +1,46 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load typogrify_tags utils_tags %}
|
||||
|
||||
{% block bodyclass %}NB-static{% endblock %}
|
||||
{% block extra_head_js %}
|
||||
{% include_stylesheets "common" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="NB-static-form-wrapper">
|
||||
<div class="NB-delete-form NB-static-form">
|
||||
|
||||
<h2>NewsBlur Publisher Keyword Popularity XLSX Creator for YC Founders</h2>
|
||||
|
||||
<h5>Search for topics across millions of stories from millions of publishers</h5>
|
||||
|
||||
{% if success %}
|
||||
<h4 style="color: #1C6130; text-align: center">Got it!<br>Email should be sent within the next minute.</h4>
|
||||
{% else %}
|
||||
<form action="" method="POST">{% csrf_token %}
|
||||
<div class="NB-fields">
|
||||
{{ popularity_query_form.email.label_tag }}
|
||||
{{ popularity_query_form.email }}
|
||||
|
||||
{{ popularity_query_form.query.label_tag }}
|
||||
{{ popularity_query_form.query }}
|
||||
<h6 class="NB-label-right">Example: <i>phillips hue, wemo, alexa</i></h6>
|
||||
</div>
|
||||
|
||||
{% if popularity_query_form.errors %}
|
||||
<div class="NB-errors">
|
||||
{% for field, error in popularity_query_form.errors.items %}
|
||||
{{ error|safe }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<input type="submit" class="submit-button NB-modal-submit-button NB-modal-submit-green" value="Email it to me"></button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
9
templates/mail/email_popularity_query.txt
Normal file
9
templates/mail/email_popularity_query.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "mail/email_base.txt" %}
|
||||
|
||||
{% load utils_tags %}
|
||||
|
||||
{% block body %}Here's your keyword popularity spreadsheet
|
||||
|
||||
This service is provided for free to YC companies. In the attached spreadsheet, you should note that individual fields have comments where appropriate. Feel free to reply directly if you have any questions.
|
||||
|
||||
- Sam Clay{% endblock body %}
|
19
templates/mail/email_popularity_query.xhtml
Normal file
19
templates/mail/email_popularity_query.xhtml
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "mail/email_base.xhtml" %}
|
||||
|
||||
{% load utils_tags %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<h2 style="color: #282F33; font-size: 18px; font-weight: bold;">
|
||||
Here's your keyword popularity spreadsheet
|
||||
</h2>
|
||||
|
||||
<p style="line-height: 20px;">
|
||||
This service is provided for free to YC companies. In the attached spreadsheet, you should note that individual fields have comments where appropriate. Feel free to reply directly if you have any questions.
|
||||
</p>
|
||||
|
||||
<p style="line-height: 20px;">
|
||||
- Sam Clay
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
1
urls.py
1
urls.py
|
@ -24,6 +24,7 @@ urlpatterns = patterns('',
|
|||
(r'^story/.*?', reader_views.index),
|
||||
(r'^feed/?', social_views.shared_stories_rss_feed_noid),
|
||||
(r'^rss_feeds/', include('apps.rss_feeds.urls')),
|
||||
(r'^analyzer/', include('apps.analyzer.urls')),
|
||||
(r'^classifier/', include('apps.analyzer.urls')),
|
||||
(r'^profile/', include('apps.profile.urls')),
|
||||
(r'^folder_rss/', include('apps.profile.urls')),
|
||||
|
|
Loading…
Add table
Reference in a new issue