mirror of
https://github.com/nikdoof/test-auth.git
synced 2025-12-14 06:42:16 +00:00
Reorganise the file structure into a project tree
This commit is contained in:
0
app/reddit/__init__.py
Normal file
0
app/reddit/__init__.py
Normal file
18
app/reddit/admin.py
Normal file
18
app/reddit/admin.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django.contrib import admin
|
||||
from reddit.models import RedditAccount
|
||||
from reddit.forms import RedditAccountForm
|
||||
|
||||
from datetime import date
|
||||
|
||||
class RedditAccountAdmin(admin.ModelAdmin):
|
||||
list_display = ('username', 'user', 'date_created', 'link_karma', 'comment_karma', 'last_update', 'validated', 'is_valid')
|
||||
search_fields = ['username']
|
||||
|
||||
fields = ('user', 'username', 'validated')
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not obj.pk:
|
||||
obj.api_update()
|
||||
obj.save()
|
||||
|
||||
admin.site.register(RedditAccount, RedditAccountAdmin)
|
||||
141
app/reddit/api.py
Normal file
141
app/reddit/api.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import simplejson as json
|
||||
import urllib2
|
||||
import urllib
|
||||
from datetime import datetime
|
||||
import unicodedata
|
||||
|
||||
class NotLoggedIn(Exception):
|
||||
pass
|
||||
|
||||
class LoginError(Exception):
|
||||
pass
|
||||
|
||||
class Comment(dict):
|
||||
""" Abstraction for comment data provided by JSON
|
||||
Comments can be identifed by Kind = 1 """
|
||||
|
||||
|
||||
def __init__(self, data):
|
||||
|
||||
dict.__init__(self)
|
||||
self['kind'] = 1
|
||||
self['id'] = data['id']
|
||||
self['post'] = data['link_id'][3:]
|
||||
self['body'] = data['body']
|
||||
self['ups'] = data['likes']
|
||||
self['downs'] = data['downs']
|
||||
self['subreddit_id'] = data['subreddit_id']
|
||||
self['subreddit'] = data['subreddit']
|
||||
self['author'] = data['author']
|
||||
self['permalink'] = u'http://reddit.com/comments/%s/c/%s' % (self['post'], self['id'])
|
||||
|
||||
def __getattr__(self, name):
|
||||
return dict.__getitem__(self, name)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'/r/%s - %s' % (self['subreddit'], ['self.author'])
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
|
||||
class Message(dict):
|
||||
""" Abstract for a Reddit Message """
|
||||
|
||||
def __init__(self, msg=None):
|
||||
if msg:
|
||||
for k in msg.keys():
|
||||
self[k] = msg[k]
|
||||
|
||||
def __getattr__(self, name):
|
||||
return dict.__getitem__(self, name)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s: %s" % (self.author, self.subject)
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
class Inbox():
|
||||
"""
|
||||
Reddit Inbox class, accesses a user's inbox and provides a iterable
|
||||
list of messages
|
||||
|
||||
>>> inbox = Inbox(username='testuser', password='testpassword')
|
||||
>>> len(inbox)
|
||||
5
|
||||
|
||||
"""
|
||||
|
||||
REDDIT = "http://www.reddit.com"
|
||||
REDDIT_API_LOGIN = "%s/api/login" % REDDIT
|
||||
REDDIT_API_INBOX = "%s/message/inbox/.json?mark=false" % REDDIT
|
||||
REDDIT_API_COMPOSE = "%s/api/compose/" % REDDIT
|
||||
|
||||
def __init__(self, username=None, password=None):
|
||||
if username and password:
|
||||
self.login(username, password)
|
||||
|
||||
def login(self, username, password):
|
||||
data = { 'user': username,
|
||||
'passwd': password,
|
||||
'api_type': 'json' }
|
||||
url = "%s/%s" % (self.REDDIT_API_LOGIN, username)
|
||||
|
||||
jsondoc = json.load(self._url_request(url, data))
|
||||
|
||||
if jsondoc and 'json' in jsondoc:
|
||||
if 'data' in jsondoc['json']:
|
||||
self.login_cookie = jsondoc['json']['data']['cookie']
|
||||
self.modhash = jsondoc['json']['data']['modhash']
|
||||
if self.login_cookie:
|
||||
return True
|
||||
elif 'errors' in jsondoc['json']:
|
||||
raise LoginError(jsondoc['json']['errors'])
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def _inbox_data(self):
|
||||
|
||||
if not self.login_cookie:
|
||||
raise NotLoggedIn
|
||||
|
||||
if not hasattr(self, '__inbox_cache'):
|
||||
inbox = json.load(self._opener.open(self.REDDIT_API_INBOX))['data']
|
||||
|
||||
self.__inbox_cache = []
|
||||
for msg in inbox['children']:
|
||||
self.__inbox_cache.append(Message(msg['data']))
|
||||
|
||||
return self.__inbox_cache
|
||||
|
||||
def _url_request(self, url, data):
|
||||
if not hasattr(self, '_opener'):
|
||||
self._opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
|
||||
urllib2.install_opener(self._opener)
|
||||
|
||||
req = urllib2.Request(url, urllib.urlencode(data))
|
||||
return self._opener.open(req)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._inbox_data)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._inbox_data[name]
|
||||
|
||||
def __iter__(self):
|
||||
return self._inbox_data.__iter__()
|
||||
|
||||
def send(self, to, subject, text):
|
||||
if not hasattr(self, 'login_cookie') or not self.login_cookie:
|
||||
raise NotLoggedIn
|
||||
|
||||
data = { 'to': to,
|
||||
'subject': subject.encode('utf-8'),
|
||||
'text': text.encode('utf-8'),
|
||||
'uh': self.modhash,
|
||||
'thing_id': '' }
|
||||
url = "%s" % (self.REDDIT_API_COMPOSE)
|
||||
|
||||
jsondoc = json.load(self._url_request(url, data))
|
||||
18
app/reddit/forms.py
Normal file
18
app/reddit/forms.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django import forms
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from reddit.models import RedditAccount
|
||||
|
||||
|
||||
class RedditAccountForm(forms.Form):
|
||||
""" Basic Reddit account input form """
|
||||
|
||||
username = forms.CharField(label = u'Reddit Username', max_length=64)
|
||||
|
||||
def clean(self):
|
||||
try:
|
||||
eaccount = RedditAccount.objects.get(username=self.cleaned_data['username'])
|
||||
except RedditAccount.DoesNotExist:
|
||||
return self.cleaned_data
|
||||
else:
|
||||
raise forms.ValidationError("This User ID is already registered")
|
||||
83
app/reddit/migrations/0001_initial.py
Normal file
83
app/reddit/migrations/0001_initial.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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 model 'RedditAccount'
|
||||
db.create_table('reddit_redditaccount', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
|
||||
('username', self.gf('django.db.models.fields.CharField')(max_length=32)),
|
||||
('reddit_id', self.gf('django.db.models.fields.CharField')(max_length=32)),
|
||||
('link_karma', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('comment_karma', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('validated', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)),
|
||||
('date_created', self.gf('django.db.models.fields.DateTimeField')()),
|
||||
('last_update', self.gf('django.db.models.fields.DateTimeField')()),
|
||||
))
|
||||
db.send_create_signal('reddit', ['RedditAccount'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting model 'RedditAccount'
|
||||
db.delete_table('reddit_redditaccount')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'reddit.redditaccount': {
|
||||
'Meta': {'object_name': 'RedditAccount'},
|
||||
'comment_karma': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'date_created': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_update': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'link_karma': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'reddit_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
||||
'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['reddit']
|
||||
0
app/reddit/migrations/__init__.py
Normal file
0
app/reddit/migrations/__init__.py
Normal file
90
app/reddit/models.py
Normal file
90
app/reddit/models.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
import simplejson as json
|
||||
import urllib
|
||||
from datetime import datetime, date
|
||||
from reddit.api import Comment
|
||||
|
||||
|
||||
class RedditAccount(models.Model):
|
||||
"""
|
||||
Represents a User ID on Reddit
|
||||
|
||||
This model can be populated by API update:
|
||||
|
||||
>>> from reddit.models import RedditAccount
|
||||
>>> mod = RedditAccount()
|
||||
>>> mod.username = 'nik_doof'
|
||||
>>> mod.api_update()
|
||||
>>> mod.reddit_id
|
||||
u'1axok'
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(User, blank=True, null=True)
|
||||
|
||||
username = models.CharField("Reddit Username", max_length=32, blank=False)
|
||||
reddit_id = models.CharField("Reddit ID", max_length=32)
|
||||
|
||||
link_karma = models.IntegerField("Link Karma")
|
||||
comment_karma = models.IntegerField("Comment Karma")
|
||||
validated = models.BooleanField("Validated")
|
||||
|
||||
date_created = models.DateTimeField("Date Created")
|
||||
last_update = models.DateTimeField("Last Update from API")
|
||||
|
||||
def api_update(self):
|
||||
try:
|
||||
jsondoc = json.load(urllib.urlopen("http://reddit.com/user/%s/about.json" % self.username))
|
||||
except:
|
||||
raise self.DoesNotExist
|
||||
|
||||
data = jsondoc['data']
|
||||
|
||||
self.link_karma = data['link_karma']
|
||||
self.comment_karma = data['comment_karma']
|
||||
self.reddit_id = data['id']
|
||||
|
||||
self.date_created = datetime.fromtimestamp(data['created_utc'])
|
||||
self.last_update = datetime.now()
|
||||
|
||||
def recent_posts(self):
|
||||
""" Returns the first page of posts visible on the user's profile page """
|
||||
|
||||
try:
|
||||
jsondoc = json.load(urllib.urlopen("http://reddit.com/user/%s.json" % self.username))
|
||||
except:
|
||||
raise self.DoesNotExist
|
||||
|
||||
posts = []
|
||||
for item in jsondoc['data']['children']:
|
||||
if item['kind'] == 't1':
|
||||
posts.append(Comment(item['data']))
|
||||
elif item['kind'] == 't3':
|
||||
posts.append(item['data'])
|
||||
|
||||
return posts
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
if not self.date_created:
|
||||
return False
|
||||
|
||||
# Account 3 months old?
|
||||
if (date.today() - self.date_created.date()).days >= 90:
|
||||
return True
|
||||
|
||||
# Account created after 9/2/10 and before 13/2/10
|
||||
if self.date_created.date() >= date(2010, 2, 9) and self.date_created.date() <= date(2010, 2, 13):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'reddit'
|
||||
ordering = ['username']
|
||||
verbose_name = 'Reddit Account'
|
||||
verbose_name_plural = 'Reddit Accounts'
|
||||
|
||||
def __unicode__(self):
|
||||
return self.username
|
||||
48
app/reddit/tasks.py
Normal file
48
app/reddit/tasks.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from urllib2 import HTTPError, URLError
|
||||
from celery.task import Task
|
||||
from celery.decorators import task
|
||||
from reddit.models import RedditAccount
|
||||
from reddit.api import Inbox, LoginError
|
||||
from django.conf import settings
|
||||
|
||||
class send_reddit_message(Task):
|
||||
|
||||
default_retry_delay = 5 * 60 # retry in 5 minutes
|
||||
ignore_result = True
|
||||
|
||||
def run(self, to, subject, message, **kwargs):
|
||||
logger = self.get_logger(**kwargs)
|
||||
|
||||
logger.info("Sending Reddit message to %s" % to)
|
||||
try:
|
||||
ib = Inbox(username=settings.REDDIT_USER, password=settings.REDDIT_PASSWORD)
|
||||
ib.send(to, subject, message)
|
||||
except (HTTPError, URLError), exc:
|
||||
logger.error("Error sending message, queueing for retry")
|
||||
send_reddit_message.retry(args=[to, subject, message], kwargs=kwargs, exc=exc)
|
||||
pass
|
||||
except LoginError, exc:
|
||||
logger.error("Error logging into Reddit")
|
||||
|
||||
|
||||
@task(ignore_result=True)
|
||||
def process_validations():
|
||||
logger = process_validations.get_logger()
|
||||
try:
|
||||
inbox = Inbox(settings.REDDIT_USER, settings.REDDIT_PASSWORD)
|
||||
for msg in inbox:
|
||||
if not msg.was_comment:
|
||||
try:
|
||||
acc = RedditAccount.objects.get(username__iexact=msg.author)
|
||||
if not acc.validated and msg.subject == "Validation: %s" % acc.user.username:
|
||||
logger.info("Validated %s" % acc.user.username)
|
||||
acc.validated = True
|
||||
acc.save()
|
||||
except RedditAccount.DoesNotExist:
|
||||
continue
|
||||
except (HTTPError, URLError), exc:
|
||||
logger.error("Error with Reddit, aborting.")
|
||||
return
|
||||
except LoginError, exc:
|
||||
logger.error("Error logging into Reddit")
|
||||
return
|
||||
18
app/reddit/templates/reddit/add_reddit_account.html
Normal file
18
app/reddit/templates/reddit/add_reddit_account.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Add Reddit Account{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>This will bind a Reddit account to your Auth Gateway login, this is usually required for application to the
|
||||
corporation</p>
|
||||
<p>Please note, you will be forever tied to this account and posts and comments made on this account will be checked
|
||||
on from time to time</p>
|
||||
<form action="{% url reddit.views.reddit_add %}" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<br />
|
||||
<input type="submit" value="Add Account" />
|
||||
</form>
|
||||
{% endblock %}
|
||||
10
app/reddit/urls.py
Normal file
10
app/reddit/urls.py
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from reddit import views
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^profile/add/reddit', views.reddit_add),
|
||||
(r'^profile/del/reddit/$', views.reddit_del),
|
||||
(r'^profile/del/reddit/(?P<redditid>\d+)/$', views.reddit_del),
|
||||
)
|
||||
|
||||
49
app/reddit/views.py
Normal file
49
app/reddit/views.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render_to_response, redirect
|
||||
from django.template import RequestContext
|
||||
from django.contrib import messages
|
||||
|
||||
from reddit.forms import RedditAccountForm
|
||||
from reddit.models import RedditAccount
|
||||
|
||||
@login_required
|
||||
def reddit_add(request):
|
||||
""" Add a Reddit account to a user's account """
|
||||
|
||||
if request.method == 'POST':
|
||||
form = RedditAccountForm(request.POST)
|
||||
if form.is_valid():
|
||||
acc = RedditAccount()
|
||||
acc.user = request.user
|
||||
acc.username = form.cleaned_data['username']
|
||||
try:
|
||||
acc.api_update()
|
||||
except RedditAccount.DoesNotExist:
|
||||
messages.add_message(request, messages.ERROR, "Error, user %s does not exist on Reddit" % acc.username )
|
||||
return render_to_response('reddit/add_reddit_account.html', locals(), context_instance=RequestContext(request))
|
||||
acc.save()
|
||||
|
||||
messages.add_message(request, messages.INFO, "Reddit account %s successfully added." % acc.username)
|
||||
return redirect('sso.views.profile') # Redirect after POST
|
||||
else:
|
||||
defaults = { 'username': request.user.username, }
|
||||
form = RedditAccountForm(defaults) # An unbound form
|
||||
|
||||
return render_to_response('reddit/add_reddit_account.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def reddit_del(request, redditid=0):
|
||||
""" Delete a Reddit account from a user's account """
|
||||
|
||||
if redditid > 0 :
|
||||
try:
|
||||
acc = RedditAccount.objects.get(id=redditid)
|
||||
except RedditAccount.DoesNotExist:
|
||||
return redirect('sso.views.profile')
|
||||
|
||||
if acc.user == request.user:
|
||||
acc.delete()
|
||||
messages.add_message(request, messages.INFO, "Reddit account successfully deleted.")
|
||||
|
||||
return redirect('sso.views.profile')
|
||||
|
||||
Reference in New Issue
Block a user