diff --git a/calloway/apps/api/handlers.py b/calloway/apps/api/handlers.py index 81536e4..ed59e14 100644 --- a/calloway/apps/api/handlers.py +++ b/calloway/apps/api/handlers.py @@ -8,7 +8,7 @@ class TagHandler(BaseHandler): def read(self, request): if 'referer' in request.GET: - bits = filter(None, request.GET['referer'].replace('/admin/','').split('/')) + bits = [_f for _f in request.GET['referer'].replace('/admin/','').split('/') if _f] model = get_model(*bits[:2]) if len(bits) == 2: diff --git a/calloway/apps/api/handlers.py.bak b/calloway/apps/api/handlers.py.bak new file mode 100644 index 0000000..81536e4 --- /dev/null +++ b/calloway/apps/api/handlers.py.bak @@ -0,0 +1,29 @@ +from piston.handler import BaseHandler +from tagging.models import Tag +from django.db.models import get_model + +class TagHandler(BaseHandler): + allowed_methods = ('GET',) + model = Tag + + def read(self, request): + if 'referer' in request.GET: + bits = filter(None, request.GET['referer'].replace('/admin/','').split('/')) + model = get_model(*bits[:2]) + + if len(bits) == 2: + r = [] + for x in Tag.objects.cloud_for_model(model): + r.append({'size':x.font_size,'name':x.name}) + return r + elif len(bits) == 3: + if bits[2] == 'add': + return [] + obj = model.objects.get(pk=bits[2]) + tags = Tag.objects.get_for_object(obj) + if 'tags' in request.GET: + obj.tags = request.GET['tags'] + else: + return tags + + diff --git a/calloway/apps/api/urls.py b/calloway/apps/api/urls.py index ee08852..8f84a20 100644 --- a/calloway/apps/api/urls.py +++ b/calloway/apps/api/urls.py @@ -1,6 +1,6 @@ from django.conf.urls.defaults import * from piston.resource import Resource -from handlers import TagHandler +from .handlers import TagHandler tag_handler = Resource(TagHandler) diff --git a/calloway/apps/api/urls.py.bak b/calloway/apps/api/urls.py.bak new file mode 100644 index 0000000..ee08852 --- /dev/null +++ b/calloway/apps/api/urls.py.bak @@ -0,0 +1,9 @@ +from django.conf.urls.defaults import * +from piston.resource import Resource +from handlers import TagHandler + +tag_handler = Resource(TagHandler) + +urlpatterns = patterns('', + url(r'^tags/?$', tag_handler), +) \ No newline at end of file diff --git a/calloway/apps/custom_registration/backends/email/__init__.py b/calloway/apps/custom_registration/backends/email/__init__.py index cc91448..ae9662f 100644 --- a/calloway/apps/custom_registration/backends/email/__init__.py +++ b/calloway/apps/custom_registration/backends/email/__init__.py @@ -15,7 +15,7 @@ from registration import signals from registration.models import RegistrationProfile, SHA1_RE from registration.backends.default import DefaultBackend -from forms import EmailRegistrationForm +from .forms import EmailRegistrationForm class EmailOrUserNameAuthenticationBackend(ModelBackend): """ @@ -208,21 +208,21 @@ def handle_expired_accounts(): expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) to_delete = [] - print "Processing %s registration profiles..." % str(RegistrationProfile.objects.all().count()) + print("Processing %s registration profiles..." % str(RegistrationProfile.objects.all().count())) for profile in RegistrationProfile.objects.all(): # if registration profile is expired, deactive user. - print "Processing %s" % profile.user + print("Processing %s" % profile.user) # If profile has been activated already, set it to be removed # and move on to next registration profile if profile.activation_key == ACTIVATED: - print "Found Active" + print("Found Active") to_delete.append(profile.pk) continue # If the user has not activated their account and the activation # days have passed, deactivate the user and send an email to user. if profile.user.is_active and profile.user.date_joined + expiration_date <= datetime.datetime.now(): - print "Found Expired" + print("Found Expired") user = profile.user user.is_active = False @@ -246,5 +246,5 @@ def handle_expired_accounts(): # Delete the registration profiles that were set to be deleted, aka # user has already activated their account. - print "Deleting %s registration profiles." % str(len(to_delete)) + print("Deleting %s registration profiles." % str(len(to_delete))) RegistrationProfile.objects.filter(pk__in=to_delete).delete() diff --git a/calloway/apps/custom_registration/backends/email/__init__.py.bak b/calloway/apps/custom_registration/backends/email/__init__.py.bak new file mode 100644 index 0000000..cc91448 --- /dev/null +++ b/calloway/apps/custom_registration/backends/email/__init__.py.bak @@ -0,0 +1,250 @@ +import random, datetime + +from django.db import models +from django.conf import settings +from django.contrib.sites.models import RequestSite +from django.contrib.sites.models import Site +from django.contrib.auth import authenticate +from django.contrib.auth import login +from django.contrib.auth.models import User +from django.contrib.auth.backends import ModelBackend +from django.template.loader import render_to_string +from django.utils.hashcompat import sha_constructor +from django.core.mail import send_mail + +from registration import signals +from registration.models import RegistrationProfile, SHA1_RE +from registration.backends.default import DefaultBackend +from forms import EmailRegistrationForm + +class EmailOrUserNameAuthenticationBackend(ModelBackend): + """ + Authenticates a user against a username or email address. + """ + def authenticate(self, username=None, password=None): + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + try: + user = User.objects.get(email__iexact=username) + except User.DoesNotExist: + return None + + if user and user.check_password(password): + return user + return None + + +class EmailBackend(DefaultBackend): + """ + Allow registration with email address. Upon suppling only an email + address, the user will have an active account and be logged in. User + will then have a certain amount of days in which there account will + stay active. + + User's session will be set to 0 (browser close) timer, from which then + they will have to view the sent email to retrieve the generated password. + + User's account will become inactive after the specified days have passed. + From there they will need to click an activation link, sent initially with + there generated password, in order to keep account active. + + A managment command is provided to check if accounts have passed ther + expiration date and will set user as inactive. + + [custom_registration.managment.commands.check_expired_accounts.py] + + ./manage.py check_expired_accounts + """ + def activate(self, request, activation_key): + """ + Override default activation process. This will activate the user + even if its passed its expiration date. + """ + if SHA1_RE.search(activation_key): + try: + profile = RegistrationProfile.objects.get(activation_key=activation_key) + except RegistrationProfile.DoesNotExist: + return False + + user = profile.user + user.is_active = True + user.save() + profile.activation_key = RegistrationProfile.ACTIVATED + profile.save() + return user + return False + + def register(self, request, **kwargs): + """ + Create and immediately log in a new user. + + Only require a email to register, username is generated + automatically and a password is random generated and emailed + to the user. + + Activation is still required for account uses after specified number + of days. + """ + if Site._meta.installed: + site = Site.objects.get_current() + else: + site = RequestSite(request) + + email = kwargs['email'] + + # Generate random password + password = User.objects.make_random_password() + + # Generate username based off of the email supplied + username = sha_constructor(str(email)).hexdigest()[:30] + + incr = 0 + # Ensure the generated username is in fact unqiue + while User.objects.filter(username=username).count() > 0: + incr += 1 + username = sha_constructor(str(email + str(incr))).hexdigest()[:30] + # Create the active user + new_user = User.objects.create_user(username, email, password) + new_user.save() + + # Create the registration profile, this is still needed because + # the user still needs to activate there account for further users + # after 3 days + registration_profile = RegistrationProfile.objects.create_profile( + new_user) + + # Authenticate and login the new user automatically + auth_user = authenticate(username=username, password=password) + login(request, auth_user) + + # Set the expiration to when the users browser closes so user + # is forced to log in upon next visit, this should force the user + # to check there email for there generated password. + request.session.set_expiry(0) + + # Create a profile instance for the new user if + # AUTH_PROFILE_MODULE is specified in settings + if hasattr(settings, 'AUTH_PROFILE_MODULE') and getattr(settings, 'AUTH_PROFILE_MODULE'): + app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') + model = models.get_model(app_label, model_name) + try: + profile = new_user.get_profile() + except model.DoesNotExist: + profile = model(user=new_user) + profile.save() + + # Custom send activation email + self.send_activation_email( + new_user, registration_profile, password, site) + + # Send user_registered signal + signals.user_registered.send(sender=self.__class__, + user=new_user, + request=request) + return new_user + + def send_activation_email(self, user, profile, password, site): + """ + Custom send email method to supplied the activation link and + new generated password. + """ + ctx_dict = { 'password': password, + 'site': site, + 'activation_key': profile.activation_key, + 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS} + + subject = render_to_string( + 'registration/email/emails/password_subject.txt', + ctx_dict) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + + message = render_to_string('registration/email/emails/password.txt', + ctx_dict) + + try: + user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) + except: + pass + + + def registration_allowed(self, request): + """ + Indicate whether account registration is currently permitted, + based on the value of the setting ``REGISTRATION_OPEN``. This + is determined as follows: + + * If ``REGISTRATION_OPEN`` is not specified in settings, or is + set to ``True``, registration is permitted. + + * If ``REGISTRATION_OPEN`` is both specified and set to + ``False``, registration is not permitted. + + """ + return getattr(settings, 'REGISTRATION_OPEN', True) + + def get_form_class(self, request): + return EmailRegistrationForm + + def post_registration_redirect(self, request, user): + """ + After registration, redirect to the home page or supplied "next" + query string or hidden field value. + + """ + next_url = "/registration/register/complete/" + if "next" in request.GET or "next" in request.POST: + next_url = request.GET.get("next", None) or request.POST.get("next", None) or "/" + + return (next_url, (), {}) + + +def handle_expired_accounts(): + """ + Check of expired accounts. + """ + ACTIVATED = RegistrationProfile.ACTIVATED + expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + + to_delete = [] + print "Processing %s registration profiles..." % str(RegistrationProfile.objects.all().count()) + for profile in RegistrationProfile.objects.all(): + # if registration profile is expired, deactive user. + print "Processing %s" % profile.user + # If profile has been activated already, set it to be removed + # and move on to next registration profile + if profile.activation_key == ACTIVATED: + print "Found Active" + to_delete.append(profile.pk) + continue + + # If the user has not activated their account and the activation + # days have passed, deactivate the user and send an email to user. + if profile.user.is_active and profile.user.date_joined + expiration_date <= datetime.datetime.now(): + print "Found Expired" + user = profile.user + user.is_active = False + + # Send an email notifing user of there account becoming inactive. + site = Site.objects.get_current() + ctx_dict = { 'site': site, + 'activation_key': profile.activation_key} + + subject = render_to_string( + 'registration/email/emails/account_expired_subject.txt', + ctx_dict) + subject = ''.join(subject.splitlines()) + + message = render_to_string( + 'registration/email/emails/account_expired.txt', + ctx_dict) + user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) + + # Only save the user instance after the email is sent. + user.save() + + # Delete the registration profiles that were set to be deleted, aka + # user has already activated their account. + print "Deleting %s registration profiles." % str(len(to_delete)) + RegistrationProfile.objects.filter(pk__in=to_delete).delete() diff --git a/calloway/apps/django_ext/cache.py b/calloway/apps/django_ext/cache.py index 37de085..fe030d0 100644 --- a/calloway/apps/django_ext/cache.py +++ b/calloway/apps/django_ext/cache.py @@ -8,7 +8,7 @@ 'min_compress_len' in inspect.getargspec(cache._cache.set)[0]: class CacheClass(cache.__class__): def add(self, key, value, timeout=None, min_compress_len=150000): - if isinstance(value, unicode): + if isinstance(value, str): value = value.encode('utf-8') # Allow infinite timeouts if timeout is None: @@ -16,7 +16,7 @@ def add(self, key, value, timeout=None, min_compress_len=150000): return self._cache.add(smart_str(key), value, timeout, min_compress_len) def set(self, key, value, timeout=None, min_compress_len=150000): - if isinstance(value, unicode): + if isinstance(value, str): value = value.encode('utf-8') if timeout is None: timeout = self.default_timeout diff --git a/calloway/apps/django_ext/cache.py.bak b/calloway/apps/django_ext/cache.py.bak new file mode 100644 index 0000000..37de085 --- /dev/null +++ b/calloway/apps/django_ext/cache.py.bak @@ -0,0 +1,25 @@ +from django.core.cache import cache +from django.utils.encoding import smart_str +import inspect + +# Check if the cache backend supports min_compress_len. If so, add it. +if cache._cache: + if 'min_compress_len' in inspect.getargspec(cache._cache.add)[0] and \ + 'min_compress_len' in inspect.getargspec(cache._cache.set)[0]: + class CacheClass(cache.__class__): + def add(self, key, value, timeout=None, min_compress_len=150000): + if isinstance(value, unicode): + value = value.encode('utf-8') + # Allow infinite timeouts + if timeout is None: + timeout = self.default_timeout + return self._cache.add(smart_str(key), value, timeout, min_compress_len) + + def set(self, key, value, timeout=None, min_compress_len=150000): + if isinstance(value, unicode): + value = value.encode('utf-8') + if timeout is None: + timeout = self.default_timeout + self._cache.set(smart_str(key), value, timeout, min_compress_len) + + cache.__class__ = CacheClass \ No newline at end of file diff --git a/calloway/apps/django_ext/caseinsensitiveauth.py b/calloway/apps/django_ext/caseinsensitiveauth.py index fb45eb0..87d9908 100644 --- a/calloway/apps/django_ext/caseinsensitiveauth.py +++ b/calloway/apps/django_ext/caseinsensitiveauth.py @@ -4,7 +4,7 @@ from re import _alphanum -CHARS = getattr(settings, 'ALLOWED_USERNAME_CHARS', ''.join(_alphanum.keys()) + '-_') +CHARS = getattr(settings, 'ALLOWED_USERNAME_CHARS', ''.join(list(_alphanum.keys())) + '-_') class CaseInsensitiveModelBackend(object): def authenticate(self, username=None, password=None): diff --git a/calloway/apps/django_ext/caseinsensitiveauth.py.bak b/calloway/apps/django_ext/caseinsensitiveauth.py.bak new file mode 100644 index 0000000..fb45eb0 --- /dev/null +++ b/calloway/apps/django_ext/caseinsensitiveauth.py.bak @@ -0,0 +1,36 @@ +from django.contrib.auth.models import User +from django.conf import settings +from django.db.models.signals import pre_save + +from re import _alphanum + +CHARS = getattr(settings, 'ALLOWED_USERNAME_CHARS', ''.join(_alphanum.keys()) + '-_') + +class CaseInsensitiveModelBackend(object): + def authenticate(self, username=None, password=None): + if '@' in username: # I can has emailz addr + try: + user = User.objects.get(email=username) + if user.check_password(password): + return user + except User.DoesNotExist: + return + else: # I can haz uzerznamez + username = filter(lambda c: c in CHARS, username).lower() + try: + user = User.objects.get(username=username) + if user.check_password(password): + return user + except User.DoesNotExist: + return + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return + +def username_formatter(sender, **kwargs): + kwargs['instance'].username = kwargs['instance'].username.lower() + +pre_save.connect(username_formatter, sender=User) diff --git a/calloway/apps/django_ext/fields.py b/calloway/apps/django_ext/fields.py index f8acab5..c8cf4c4 100644 --- a/calloway/apps/django_ext/fields.py +++ b/calloway/apps/django_ext/fields.py @@ -49,7 +49,7 @@ def iterator(self): else: superiter = super(PKListQuerySet, self).iterator() while True: - yield superiter.next() + yield next(superiter) def _clone(self, *args, **kwargs): c = super(PKListQuerySet, self)._clone(*args, **kwargs) diff --git a/calloway/apps/django_ext/fields.py.bak b/calloway/apps/django_ext/fields.py.bak new file mode 100644 index 0000000..f8acab5 --- /dev/null +++ b/calloway/apps/django_ext/fields.py.bak @@ -0,0 +1,133 @@ +import functools +from django.db.models.fields.related import ManyToManyField, ReverseManyRelatedObjectsDescriptor, ManyRelatedObjectsDescriptor +from django.db.models.query import QuerySet +from django.db.models import signals +from django_ext.cache import cache +from types import MethodType + +CACHE_DURATION = 60 * 30 + +def invalidate_cache(obj, field): + cache.set(obj._get_cache_key(field=field), None, 5) + +def fix_where(where, modified=False): + def wrap_add(f): + @functools.wraps(f) + def add(self, *args, **kwargs): + """ + Wraps django.db.models.sql.where.add to indicate that a new + 'where' condition has been added. + """ + self.modified = True + return f(*args, **kwargs) + return add + where.modified = modified + where.add = MethodType(wrap_add(where.add), where, where.__class__) + return where + + +def get_pk_list_query_set(superclass): + class PKListQuerySet(superclass): + """ + QuerySet that, when unfiltered, fetches objects individually from + the datastore by pk. + + The `pk_list` attribute is a list of primary keys for objects that + should be fetched. + + """ + def __init__(self, pk_list=[], from_cache=False, *args, **kwargs): + super(PKListQuerySet, self).__init__(*args, **kwargs) + self.pk_list = pk_list + self.from_cache = from_cache + self.query.where = fix_where(self.query.where) + + def iterator(self): + if not self.query.where.modified: + for pk in self.pk_list: + yield self.model._default_manager.get(pk=pk) + else: + superiter = super(PKListQuerySet, self).iterator() + while True: + yield superiter.next() + + def _clone(self, *args, **kwargs): + c = super(PKListQuerySet, self)._clone(*args, **kwargs) + c.query.where = fix_where(c.query.where, modified=self.query.where.modified) + c.pk_list = self.pk_list + c.from_cache = self.from_cache + return c + return PKListQuerySet + + +def get_caching_related_manager(superclass, instance, field_name, related_name): + class CachingRelatedManager(superclass): + def all(self): + key = instance._get_cache_key(field=field_name) + qs = super(CachingRelatedManager, self).get_query_set() + PKListQuerySet = get_pk_list_query_set(qs.__class__) + qs = qs._clone(klass=PKListQuerySet) + pk_list = cache.get(key) + if pk_list is None: + pk_list = qs.values_list('pk', flat=True) + cache.add(key, pk_list, CACHE_DURATION) + else: + qs.from_cache = True + qs.pk_list = pk_list + return qs + + def add(self, *objs): + super(CachingRelatedManager, self).add(*objs) + for obj in objs: + invalidate_cache(obj, related_name) + invalidate_cache(instance, field_name) + + def remove(self, *objs): + super(CachingRelatedManager, self).remove(*objs) + for obj in objs: + invalidate_cache(obj, related_name) + invalidate_cache(instance, field_name) + + def clear(self): + objs = list(self.all()) + super(CachingRelatedManager, self).clear() + for obj in objs: + invalidate_cache(obj, related_name) + invalidate_cache(instance, field_name) + return CachingRelatedManager + + +class CachingReverseManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor): + def __get__(self, instance, cls=None): + manager = super(CachingReverseManyRelatedObjectsDescriptor, self).__get__(instance, cls) + + CachingRelatedManager = get_caching_related_manager(manager.__class__, + instance, + self.field.name, + self.field.rel.related_name) + + manager.__class__ = CachingRelatedManager + return manager + + +class CachingManyRelatedObjectsDescriptor(ManyRelatedObjectsDescriptor): + def __get__(self, instance, cls=None): + manager = super(CachingManyRelatedObjectsDescriptor, self).__get__(instance, cls) + + CachingRelatedManager = get_caching_related_manager(manager.__class__, + instance, + self.related.get_accessor_name(), + self.related.field.name) + + manager.__class__ = CachingRelatedManager + return manager + + +class CachingManyToManyField(ManyToManyField): + def contribute_to_class(self, cls, name): + super(CachingManyToManyField, self).contribute_to_class(cls, name) + setattr(cls, self.name, CachingReverseManyRelatedObjectsDescriptor(self)) + + def contribute_to_related_class(self, cls, related): + super(CachingManyToManyField, self).contribute_to_related_class(cls, related) + setattr(cls, related.get_accessor_name(), CachingManyRelatedObjectsDescriptor(related)) \ No newline at end of file diff --git a/calloway/apps/django_ext/management/commands/create_demo_data.py b/calloway/apps/django_ext/management/commands/create_demo_data.py index b7ba6ce..e10f320 100644 --- a/calloway/apps/django_ext/management/commands/create_demo_data.py +++ b/calloway/apps/django_ext/management/commands/create_demo_data.py @@ -32,13 +32,13 @@ def create_nav_bar(self): {"name": "Topics", "parent": None, "title": "Collections of stories on popular topics", "url": "/topics/", "user_type": "E", "path_type": "A", "order": 40} ] for item in nav: - print "Creating Nav Bar Entry: %s" % (item['name']) + print("Creating Nav Bar Entry: %s" % (item['name'])) nav_entry = NavBarEntry(**item) nav_entry.save() def create_categories(self): test_file = os.path.abspath(os.path.join(fixtures_path, 'demo_categories.txt')) - print "Creating categories from %s" % test_file + print("Creating categories from %s" % test_file) import_categories_cmd().execute(test_file) def create_staff(self): @@ -55,7 +55,7 @@ def create_staff(self): lname = random.choice(last_names) username = '%s%s' % (fname[0].lower(), lname.lower()) email = '%s@example.com' % username - print "Creating %s (%s %s)" % (username, fname, lname) + print("Creating %s (%s %s)" % (username, fname, lname)) try: user = User.objects.create_user(username, email, 'password') user.is_staff = True @@ -63,8 +63,8 @@ def create_staff(self): user.last_name = lname user.save() update_staff_member(user, user, True) - except Exception, e: - print 'Skipping due to conflict.' + except Exception as e: + print('Skipping due to conflict.') def create_stories(self): import datetime diff --git a/calloway/apps/django_ext/management/commands/create_demo_data.py.bak b/calloway/apps/django_ext/management/commands/create_demo_data.py.bak new file mode 100644 index 0000000..b7ba6ce --- /dev/null +++ b/calloway/apps/django_ext/management/commands/create_demo_data.py.bak @@ -0,0 +1,113 @@ +import os, random +from django.core.management.base import BaseCommand, CommandError +from categories.management.commands.import_categories import Command as import_categories_cmd + +fixtures_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)),'..','fixtures')) +random.seed() + +class Command(BaseCommand): + def handle(self, *args, **options): + if len(args): + for arg in args: + attr = 'create_%s' % arg + if not hasattr(self, attr): + raise CommandError('Demo data routine for %r not found' % arg) + getattr(self, attr)() + else: + self.create_all_demo_data() + + def create_all_demo_data(self): + self.create_nav_bar() + self.create_categories() + self.create_staff() + self.create_stories() + + def create_nav_bar(self): + from navbar.models import NavBarEntry + nav = [ + {"name": "News", "parent": None, "title": "News stories by topic", "url": "/news/", "user_type": "E", "path_type": "A", "order": 0}, + {"name": "Opinion", "parent": None, "title": "What we think about things", "url": "/opinion/", "user_type": "E", "path_type": "A", "order": 10}, + {"name": "Galleries", "parent": None, "title": "Photo essays on current events", "url": "/photos/galleries/", "user_type": "E", "path_type": "A", "order": 20}, + {"name": "Video", "parent": None, "title": "Video briefs", "url": "/video/", "user_type": "E", "path_type": "A", "order": 30}, + {"name": "Topics", "parent": None, "title": "Collections of stories on popular topics", "url": "/topics/", "user_type": "E", "path_type": "A", "order": 40} + ] + for item in nav: + print "Creating Nav Bar Entry: %s" % (item['name']) + nav_entry = NavBarEntry(**item) + nav_entry.save() + + def create_categories(self): + test_file = os.path.abspath(os.path.join(fixtures_path, 'demo_categories.txt')) + print "Creating categories from %s" % test_file + import_categories_cmd().execute(test_file) + + def create_staff(self): + from django.contrib.auth.models import User + from staff.models import update_staff_member + + first_names = ['Andrew','Beth','Claire','Darcy','Ethan','Isabella', + 'Jacob','Kevin','Madison','Norman','Olivia','Patrick',] + last_names = ['Smith','Johnson','Williams','Jones','Brown','Quick', + 'Patterson','Soarez','Hensley',] + + for i in range(10): + fname = random.choice(first_names) + lname = random.choice(last_names) + username = '%s%s' % (fname[0].lower(), lname.lower()) + email = '%s@example.com' % username + print "Creating %s (%s %s)" % (username, fname, lname) + try: + user = User.objects.create_user(username, email, 'password') + user.is_staff = True + user.first_name = fname + user.last_name = lname + user.save() + update_staff_member(user, user, True) + except Exception, e: + print 'Skipping due to conflict.' + + def create_stories(self): + import datetime + from django.template.defaultfilters import slugify + from django.contrib.sites.models import Site + from django_ext import markov + from stories.models import Story + from categories.models import Category + from staff.models import StaffMember + + chain = markov.MarkovChain() + staff = StaffMember.objects.all()[:] + categories = Category.objects.all()[:] + + # Load the dictionary into the markov chain + sentences = os.path.abspath(os.path.join(fixtures_path, 'demo_story_data.txt')) + for line in open(sentences): + words = line.strip().split() + chain.add(words) + + # Let's generate 10 random stories + for i in range(10): + story_data = {'headline': " ".join(chain.random_output(15))} + story_data['slug'] = slugify(story_data['headline'])[:30] + + # 25% chance of a subhead + if random.randint(1,4) == 1: + story_data['subhead'] = " ".join(chain.random_output(20)) + + story_data['teaser'] = " ".join(chain.random_output()) + story_data['publish_date'] = datetime.datetime.now().date() + story_data['publish_time'] = datetime.datetime.now().time() + story_data['site'] = Site.objects.get_current() + story_data['primary_category'] = random.choice(categories) + + # Create a story with 4-10 paragraphs with 1-4 sentences each + body = [] + for j in range(0, random.randint(4,10)): + p = [] + for i in range(0, random.randint(1,4)): + p.append(" ".join(chain.random_output())) + body.append("
%s
" % " ".join(p)) + story_data['body'] = "\n".join(body) + story = Story(**story_data) + story.save() + story.authors.add(random.choice(staff)) diff --git a/calloway/apps/django_ext/management/commands/delete_contenttypes.py b/calloway/apps/django_ext/management/commands/delete_contenttypes.py index 4676938..d28e830 100644 --- a/calloway/apps/django_ext/management/commands/delete_contenttypes.py +++ b/calloway/apps/django_ext/management/commands/delete_contenttypes.py @@ -6,15 +6,15 @@ class Command(NoArgsCommand): def handle_noargs(self, **options): from django.db import connection - print "Deleting content types and permissions" + print("Deleting content types and permissions") transaction.commit_unless_managed() transaction.enter_transaction_management() transaction.managed(True) cursor = connection.cursor() cursor.execute("DELETE FROM django_content_type", []) - print cursor.fetchall() + print(cursor.fetchall()) cursor.execute("DELETE FROM auth_permission", []) - print cursor.fetchall() + print(cursor.fetchall()) transaction.commit() transaction.leave_transaction_management() - print "Contenttypes deleted" \ No newline at end of file + print("Contenttypes deleted") \ No newline at end of file diff --git a/calloway/apps/django_ext/management/commands/delete_contenttypes.py.bak b/calloway/apps/django_ext/management/commands/delete_contenttypes.py.bak new file mode 100644 index 0000000..4676938 --- /dev/null +++ b/calloway/apps/django_ext/management/commands/delete_contenttypes.py.bak @@ -0,0 +1,20 @@ +from django.core.management.base import NoArgsCommand +from django.db import transaction + +class Command(NoArgsCommand): + help = "Delete contenttypes crapola" + + def handle_noargs(self, **options): + from django.db import connection + print "Deleting content types and permissions" + transaction.commit_unless_managed() + transaction.enter_transaction_management() + transaction.managed(True) + cursor = connection.cursor() + cursor.execute("DELETE FROM django_content_type", []) + print cursor.fetchall() + cursor.execute("DELETE FROM auth_permission", []) + print cursor.fetchall() + transaction.commit() + transaction.leave_transaction_management() + print "Contenttypes deleted" \ No newline at end of file diff --git a/calloway/apps/django_ext/management/commands/e2c.py b/calloway/apps/django_ext/management/commands/e2c.py index c20a492..79de3af 100644 --- a/calloway/apps/django_ext/management/commands/e2c.py +++ b/calloway/apps/django_ext/management/commands/e2c.py @@ -25,7 +25,7 @@ def fix(): transaction.commit_unless_managed() def getr(model): - if isinstance(model, basestring): + if isinstance(model, str): model = get_model(*model.split('.')) def inner(pk): try: @@ -47,13 +47,13 @@ def handle(self, *apps, **options): if len(apps): for app in apps: for o in self.migrate(app): - print '\t%s %s' % (o._meta.model, o.pk) + print('\t%s %s' % (o._meta.model, o.pk)) for app in ORDER: if app == 'staff': fix() - print 'Migrating %s...' % app.title() + print('Migrating %s...' % app.title()) for o in self.migrate(app): - print '\t%s %s' % (o._meta.model, o.pk) + print('\t%s %s' % (o._meta.model, o.pk)) if app == 'staff': fix() @@ -71,7 +71,7 @@ def get_fixtures(self, name): def migrate(self, app): for fixture in self.get_fixtures(app): - print 'Loading %s...' % fixture + print('Loading %s...' % fixture) for obj in load(fixture): if obj['model'] in self.mapping: m = self.mapping[obj['model']].split('.') @@ -97,7 +97,7 @@ def migrate(self, app): except model.DoesNotExist: pass o = model.objects.create(**kw) - for k,v in rel.items(): + for k,v in list(rel.items()): for i in v: getattr(o, k).add(i) yield o @@ -195,7 +195,7 @@ def entry(self, **fields): author = StaffMember.objects.get(user=user) except StaffMember.DoesNotExist: author = StaffMember.objects.create(user=user) - cat = filter(None, map(getr(Category), fields['categories'])) + cat = [_f for _f in map(getr(Category), fields['categories']) if _f] if len(cat): cat = cat[0] else: @@ -226,8 +226,8 @@ def story(self, **fields): 'primary_category': getr(Category)(fields['primary_category'] or 37) or getr(Category)(37), 'kicker': fields['kicker'], }, { - 'authors': map(getr(StaffMember), fields['bylines']), - 'categories': filter(None, [getr(Category)(x or 37) for x in fields['categories']]), + 'authors': list(map(getr(StaffMember), fields['bylines'])), + 'categories': [_f for _f in [getr(Category)(x or 37) for x in fields['categories']] if _f], } def image(self, **fields): @@ -242,7 +242,7 @@ def image(self, **fields): 'height': fields['height'], }, { 'sites': [Site.objects.get_current()], - 'categories': map(getr(Category), fields['categories']), + 'categories': list(map(getr(Category), fields['categories'])), } def profile(self, **fields): @@ -259,5 +259,5 @@ def profile(self, **fields): fields['phone'] = fields.pop('mobile_phone',None) fields['display_phone'] = fields.pop('display_mobile_phone',False) return fields,{ - 'newsletters': filter(None,map(getr('newsletters.newsletter'), n)), + 'newsletters': [_f for _f in map(getr('newsletters.newsletter'), n) if _f], } \ No newline at end of file diff --git a/calloway/apps/django_ext/management/commands/e2c.py.bak b/calloway/apps/django_ext/management/commands/e2c.py.bak new file mode 100644 index 0000000..c20a492 --- /dev/null +++ b/calloway/apps/django_ext/management/commands/e2c.py.bak @@ -0,0 +1,263 @@ +import os +from django.core.management.base import BaseCommand, CommandError +from django.utils.simplejson import load +from django.contrib.sites.models import Site +from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist +from django.db.models import get_model +from django.conf import settings + +from categories.models import Category +from staff.models import StaffMember + +ORDER = ('staff','blogs','entries','stories','images','inlines') + +def fix(): + if settings.DATABASE_ENGINE == 'postgresql_psycopg2': + from django.db import connection, transaction + cursor = connection.cursor() + + # Data modifying operation - commit required + cursor.execute(""" + SELECT setval('"staff_staffmember_id_seq"', coalesce(max("id"), 1), max("id") IS NOT null) FROM "staff_staffmember"; + SELECT setval('"staff_staffmember_sites_id_seq"', coalesce(max("id"), 1), max("id") IS NOT null) FROM "staff_staffmember_sites"; + """) + transaction.commit_unless_managed() + +def getr(model): + if isinstance(model, basestring): + model = get_model(*model.split('.')) + def inner(pk): + try: + return model.objects.get(pk=pk) + except model.DoesNotExist: + return + return inner + +class Command(BaseCommand): + mapping = { + 'news.story': 'stories.story', + 'weblogs.entry': 'viewpoint.entry', + 'weblogs.blog': 'viewpoint.blog', + 'news.storyinlinemapping': 'stories.storyrelation', + 'media.photo': 'massmedia.image', + } + + def handle(self, *apps, **options): + if len(apps): + for app in apps: + for o in self.migrate(app): + print '\t%s %s' % (o._meta.model, o.pk) + for app in ORDER: + if app == 'staff': + fix() + print 'Migrating %s...' % app.title() + for o in self.migrate(app): + print '\t%s %s' % (o._meta.model, o.pk) + if app == 'staff': + fix() + + def get_fixtures(self, name): + adir = 'fixtures' + for f in os.listdir(adir): + if f.startswith(name): + f = os.path.join(adir, f) + if os.path.isfile(f): + return [open(f)] + elif os.path.isdir(f): + l = [os.path.join(f,x) for x in os.listdir(f) if x.endswith('.json')] + return sorted(map(open, l)) + raise IOError('Fixture %r not found' % name) + + def migrate(self, app): + for fixture in self.get_fixtures(app): + print 'Loading %s...' % fixture + for obj in load(fixture): + if obj['model'] in self.mapping: + m = self.mapping[obj['model']].split('.') + else: + m = obj['model'].split('.') + model = get_model(*m) + if not model: + continue + obj['fields'].update(pk=obj['pk']) + try: + kw,rel = getattr(self, m[-1])(**obj['fields']) + except AttributeError: + kw,rel = self.default(**obj['fields']) + except TypeError: + continue + if kw == rel == None: + continue + if not 'pk' in kw: + kw['pk'] = obj['pk'] + try: + model.objects.get(pk=kw['pk']) + continue + except model.DoesNotExist: + pass + o = model.objects.create(**kw) + for k,v in rel.items(): + for i in v: + getattr(o, k).add(i) + yield o + + def default(self, **fields): + return fields, {} + + def storyrelation(self, **fields): + from massmedia.models import Image + from django.contrib.contenttypes.models import ContentType + from stories.models import StoryRelation + ctype = ContentType.objects.get_for_model(Image) + if fields['inline_type'] == 'photo': + story = getr('stories.story')(fields['story']) + if story is None: + return + try: + StoryRelation.objects.get(pk=fields['pk']) + return + except StoryRelation.DoesNotExist: + return { + 'story': story, + 'content_type': ctype, + 'object_id': fields['object_id'], + 'relation_type': 'image' + }, {} + + + def staffmember(self, **fields): + try: + assert fields['email'] + user = User.objects.get( + email=fields['email'], + first_name = fields['first_name'], + last_name = fields['last_name'], + ) + except (User.DoesNotExist, AssertionError): + user = User.objects.get_or_create( + username = 'staffmember%d' % fields['pk'], + email = fields['email'], + first_name = fields['first_name'], + last_name = fields['last_name'], + is_active = False, is_staff = True + )[0] + + return { + 'bio': fields['bio'], + 'first_name': fields['first_name'], + 'last_name': fields['last_name'], + 'photo': fields['mugshot'], + 'is_active': fields['is_active'], + 'email': fields['email'], + 'phone': fields['phone'], + 'slug': fields['slug'], + 'user': user + }, { + 'sites': [Site.objects.get_current()], + } + + def category(self, **fields): + return { + 'rght': fields['rght'], + 'lft': fields['lft'], + 'level': fields['level'], + 'tree_id': fields['tree_id'], + 'slug': fields['slug'], + 'parent': fields['parent'] and getr(Category)(fields['parent']) or None, + 'name': fields['name'], + }, {} + + def blog(self, **fields): + owners = [] + for o in fields['authors']: + user = getr('auth.user')(o) + if user: + try: + owners.append(StaffMember.objects.get(user=user)) + except: + break + return { + 'slug': fields['slug'], + 'title': fields['title'], + 'description': fields['description'] or '', + 'tease': fields.get('tagline', 'none') or 'none', + 'photo': fields['header_image'], + 'creation_date': fields['created_date'], + 'public': fields['display'], + }, { + 'owners': owners, + } + + def entry(self, **fields): + user = getr(User)(fields.pop('author')) + try: + author = StaffMember.objects.get(user=user) + except StaffMember.DoesNotExist: + author = StaffMember.objects.create(user=user) + cat = filter(None, map(getr(Category), fields['categories'])) + if len(cat): + cat = cat[0] + else: + cat = None + return { + 'blog': getr('viewpoint.blog')(fields['blog']), + 'body': fields['body'], + 'author': author, + 'slug': fields['slug'], + 'title': fields['title'], + 'tease': fields['summary'] or 'None', + 'pub_date': fields['pub_date'].split()[0], + 'pub_time': fields['pub_date'].split()[1], + 'category': cat + }, {} + + def story(self, **fields): + return { + 'headline': fields['headline'][:100], + 'publish_date': fields['pub_date'].split()[0], + 'publish_time': fields['pub_date'].split()[1], + 'body': fields['story'], + 'site': Site.objects.get_current(), + 'status': fields['status'], + 'subhead': fields['print_headline'], + 'teaser': fields['tease'], + 'slug': fields['slug'][:50], + 'primary_category': getr(Category)(fields['primary_category'] or 37) or getr(Category)(37), + 'kicker': fields['kicker'], + }, { + 'authors': map(getr(StaffMember), fields['bylines']), + 'categories': filter(None, [getr(Category)(x or 37) for x in fields['categories']]), + } + + def image(self, **fields): + return { + 'creation_date': fields['creation_date'], + 'author': getr(User)(fields['photographer']), + 'one_off_author': fields['one_off_photographer'], + #'credit': fields['credit'], + 'caption': fields['caption'], + 'file': fields['photo'], + 'width': fields['width'], + 'height': fields['height'], + }, { + 'sites': [Site.objects.get_current()], + 'categories': map(getr(Category), fields['categories']), + } + + def profile(self, **fields): + n = fields.pop('newsletters',[]) + fields.pop('mobile_provider') + fields.pop('display_blogs') + fields.pop('avatar') + fields.pop('display_comments') + fields.pop('display_mobile_provider') + fields.pop('display_usercontent') + for k in fields: + if k.startswith('display_'): + fields[k] = fields[k] or False + fields['phone'] = fields.pop('mobile_phone',None) + fields['display_phone'] = fields.pop('display_mobile_phone',False) + return fields,{ + 'newsletters': filter(None,map(getr('newsletters.newsletter'), n)), + } \ No newline at end of file diff --git a/calloway/apps/django_ext/management/commands/generate_reqs.py b/calloway/apps/django_ext/management/commands/generate_reqs.py index abf1302..0493192 100644 --- a/calloway/apps/django_ext/management/commands/generate_reqs.py +++ b/calloway/apps/django_ext/management/commands/generate_reqs.py @@ -68,12 +68,12 @@ def generate_requirements(output_path=None): reqs = set() for app in settings.INSTALLED_APPS: - if app in mapping.keys(): + if app in list(mapping.keys()): reqs |= set(mapping[app]) if output_path is None: - print "--extra-index-url=http://opensource.washingtontimes.com/pypi/simple/" + print("--extra-index-url=http://opensource.washingtontimes.com/pypi/simple/") for item in reqs: - print item + print(item) else: try: out_file = open(output_path, 'w') diff --git a/calloway/apps/django_ext/management/commands/generate_reqs.py.bak b/calloway/apps/django_ext/management/commands/generate_reqs.py.bak new file mode 100644 index 0000000..abf1302 --- /dev/null +++ b/calloway/apps/django_ext/management/commands/generate_reqs.py.bak @@ -0,0 +1,97 @@ +import os +from optparse import make_option + +from django.core.management.base import BaseCommand + +mapping = { + 'admin_tools': ('django-admin-tools==0.2',), + 'admin_tools.dashboard': ('django-admin-tools==0.2',), + 'admin_tools.menu': ('django-admin-tools==0.2',), + 'admin_tools.theming': ('django-admin-tools==0.2',), + 'ban': ('django-ban==0.1', 'ipcalc>=0.1', ), + 'categories': ('django-categories==0.4.3','django-mptt-2==0.3', ), + 'critic': ('critic==0.1.1',), + 'debug_toolbar': ('django-debug-toolbar==0.8.3',), + 'django_extensions': ('django-extensions==0.4.1',), + 'django_memcached': ('django-memcached==0.1.2', 'python-memcached==1.44',), + 'editor': ('django-categories==0.4.3','django-mptt-2==0.3', ), + 'frontendadmin': ('django-frontendadmin==0.4', 'django-livevalidation==0.1.1',), + 'google_analytics': ('google_analytics==0.2',), + 'haystack': ('django-haystack==1.0.1-final',), + 'hiermenu': ('hiermenu==0.1',), + 'livevalidation': ('django-livevalidation==0.1.1',), + 'mailfriend': ('django-mailfriend==1.0',), + 'massmedia': ('massmedia==0.5.1', 'django-tagging==0.3.1', 'IPTCInfo', 'hachoir-metadata', 'hachoir-core', 'hachoir-parser',), + 'memcache_status': ('django-memcache-status==1.0.1',), + 'mptt': ('django-mptt-2==0.3',), + 'mptt_comments': ('django-mptt-comments==0.1.1','django-mptt-2==0.3',), + 'native_tags': ('django-native-tags==0.4',), + 'news_sitemaps': ('django-news-sitemaps==0.1.2',), + 'offensivecontent': ('offensivecontent==0.2.6',), + 'pagination': ('django-pagination==1.0.8',), + 'piston': ('django-piston==0.2.2',), + 'pollit': ('pollit==0.1.1',), + 'positions': ('kamasutra==0.1.5',), + 'pullquote': ('pullquote==0.1.1',), + 'registration': ('django-registration==0.8-alpha-1',), + 'reversion': ('django-reversion==1.2.1',), + 'robots': ('django-robots==0.7.0',), + 'staff': ('django-staff==0.3.2',), + 'staticmediamgr': ('django-staticmediamgr==0.5.3',), + 'stories': ('django-stories==0.3.6', 'BeautifulSoup', 'pytidylib'), + 'synagg': ('django-synagg', 'feedparser==4.1',), + 'tagging': ('django-tagging==0.3.1',), + 'tinymce': ('django-tinymce==1.5.1beta1',), + 'uni_form': ('django-uni-form==0.7',), + 'varnishapp': ('django-varnish==0.1', 'python-varnish==0.1',), + 'versionedcache': ('versionedcache==0.1.0dev',), + 'viewpoint': ('viewpoint==0.6.2',), +} + +unknown = { + 'python-dateutil': 'python-dateutil==1.4.1', + 'django-logjam': 'django-logjam==0.1.1', + 'django-picklefield': 'django-picklefield==0.1', + 'django-profiles': 'django-profiles>=0.2', + 'simplejson': 'simplejson==2.0.9', + 'lxml':'lxml', +} + +def generate_requirements(output_path=None): + """ + Loop through the INSTALLED_APPS and create a set of requirements for pip. + + if output_path is ``None`` then write to standard out, otherwise write + to the path. + """ + from django.conf import settings + reqs = set() + + for app in settings.INSTALLED_APPS: + if app in mapping.keys(): + reqs |= set(mapping[app]) + if output_path is None: + print "--extra-index-url=http://opensource.washingtontimes.com/pypi/simple/" + for item in reqs: + print item + else: + try: + out_file = open(output_path, 'w') + out_file.write("--extra-index-url=http://opensource.washingtontimes.com/pypi/simple/\n") + for item in reqs: + out_file.write("%s\n" % item) + finally: + out_file.close() + + +class Command(BaseCommand): + help = "Writes a requirements file for pip to standard out or a file." + can_import_settings = True + + def handle(self, *args, **options): + if len(args) == 1: + generate_requirements(args[0]) + elif len(args) == 0: + generate_requirements() + else: + raise CommandError("No more than one file path may be specified on the command.") \ No newline at end of file diff --git a/calloway/apps/django_ext/managers.py b/calloway/apps/django_ext/managers.py index 5a8ffdb..c5a405c 100644 --- a/calloway/apps/django_ext/managers.py +++ b/calloway/apps/django_ext/managers.py @@ -54,7 +54,7 @@ class CachingQuerySet(QuerySet): def iterator(self): superiter = super(CachingQuerySet, self).iterator() while True: - obj = superiter.next() + obj = next(superiter) # Use cache.add instead of cache.set to prevent race conditions (see CachingManager) cache.add(obj.cache_key, obj, CACHE_DURATION) yield obj @@ -75,10 +75,10 @@ def get(self, *args, **kwargs): # Punt on anything more complicated than get by pk/id only... if len(kwargs) == 1: - k = kwargs.keys()[0] + k = list(kwargs.keys())[0] if k in ('pk', 'pk__exact', '%s' % self.model._meta.pk.attname, '%s__exact' % self.model._meta.pk.attname): - obj = cache.get(self.model._cache_key(pk=kwargs.values()[0])) + obj = cache.get(self.model._cache_key(pk=list(kwargs.values())[0])) if obj is not None: obj.from_cache = True return obj diff --git a/calloway/apps/django_ext/managers.py.bak b/calloway/apps/django_ext/managers.py.bak new file mode 100644 index 0000000..5a8ffdb --- /dev/null +++ b/calloway/apps/django_ext/managers.py.bak @@ -0,0 +1,87 @@ +from django.db import models +from django.db.models import signals +from django_ext.cache import cache +from django.db.models.query import QuerySet + +CACHE_DURATION = 60 * 30 + +def _cache_key(model, pk, field=None): + if field: + return "%s:%s.%s:%s" % (field, model._meta.app_label, model._meta.module_name, pk) + else: + return "%s.%s:%s" % (model._meta.app_label, model._meta.module_name, pk) + +def _get_cache_key(self, field=None): + return self._cache_key(self.pk, field) + + +class CachingManager(models.Manager): + def __init__(self, use_for_related_fields=True, *args, **kwargs): + self.use_for_related_fields = use_for_related_fields + super(CachingManager, self).__init__(*args, **kwargs) + + def get_query_set(self): + return CachingQuerySet(self.model) + + def contribute_to_class(self, model, name): + signals.post_save.connect(self._post_save, sender=model) + signals.post_delete.connect(self._post_delete, sender=model) + setattr(model, '_cache_key', classmethod(_cache_key)) + setattr(model, '_get_cache_key', _get_cache_key) + setattr(model, 'cache_key', property(_get_cache_key)) + return super(CachingManager, self).contribute_to_class(model, name) + + def _invalidate_cache(self, instance): + """ + Explicitly set a None value instead of just deleting so we don't have any race + conditions where: + Thread 1 -> Cache miss, get object from DB + Thread 2 -> Object saved, deleted from cache + Thread 1 -> Store (stale) object fetched from DB in cache + Five second should be more than enough time to prevent this from happening for + a web app. + """ + cache.set(instance.cache_key, None, 5) + + def _post_save(self, instance, **kwargs): + self._invalidate_cache(instance) + + def _post_delete(self, instance, **kwargs): + self._invalidate_cache(instance) + + +class CachingQuerySet(QuerySet): + def iterator(self): + superiter = super(CachingQuerySet, self).iterator() + while True: + obj = superiter.next() + # Use cache.add instead of cache.set to prevent race conditions (see CachingManager) + cache.add(obj.cache_key, obj, CACHE_DURATION) + yield obj + + def get(self, *args, **kwargs): + """ + Checks the cache to see if there's a cached entry for this pk. If not, fetches + using super then stores the result in cache. + + Most of the logic here was gathered from a careful reading of + ``django.db.models.sql.query.add_filter`` + """ + if self.query.where: + # If there is any other ``where`` filter on this QuerySet just call + # super. There will be a where clause if this QuerySet has already + # been filtered/cloned. + return super(CachingQuerySet, self).get(*args, **kwargs) + + # Punt on anything more complicated than get by pk/id only... + if len(kwargs) == 1: + k = kwargs.keys()[0] + if k in ('pk', 'pk__exact', '%s' % self.model._meta.pk.attname, + '%s__exact' % self.model._meta.pk.attname): + obj = cache.get(self.model._cache_key(pk=kwargs.values()[0])) + if obj is not None: + obj.from_cache = True + return obj + + # Calls self.iterator to fetch objects, storing object in cache. + return super(CachingQuerySet, self).get(*args, **kwargs) \ No newline at end of file diff --git a/calloway/apps/django_ext/markov.py b/calloway/apps/django_ext/markov.py index c9329d2..7649a70 100644 --- a/calloway/apps/django_ext/markov.py +++ b/calloway/apps/django_ext/markov.py @@ -16,7 +16,7 @@ def total_sides(self): def roll(self): random_num = random.randint(0, self.total_sides()) total_pos = 0 - for side, qty in self.items(): + for side, qty in list(self.items()): total_pos += qty if random_num <= total_pos: return side diff --git a/calloway/apps/django_ext/markov.py.bak b/calloway/apps/django_ext/markov.py.bak new file mode 100644 index 0000000..c9329d2 --- /dev/null +++ b/calloway/apps/django_ext/markov.py.bak @@ -0,0 +1,64 @@ +import collections +import random + + +class DynamicDie(collections.defaultdict): + + def __init__(self): + collections.defaultdict.__init__(self, int) + + def add_side(self, side): + self[side] += 1 + + def total_sides(self): + return sum(self.values()) + + def roll(self): + random_num = random.randint(0, self.total_sides()) + total_pos = 0 + for side, qty in self.items(): + total_pos += qty + if random_num <= total_pos: + return side + + +class MarkovChain(collections.defaultdict): + """ Yet another markov chain implementation. + This one differs in the sense that it is able to better support + huge amounts of data since the weighted randomization doesn't rely + on duplicates. + """ + + # Sentinals + # Discussion here: http://stackoverflow.com/questions/1677726 + class START(object):pass + class END(object):pass + + def __init__(self): + collections.defaultdict.__init__(self, DynamicDie) + + def add(self, iterable): + """ Insert an iterable (pattern) item into the markov chain. + The order of the pattern will define more of the chain. + """ + item1 = item2 = MarkovChain.START + for item3 in iterable: + self[(item1, item2)].add_side(item3) + item1 = item2 + item2 = item3 + self[(item1, item2)].add_side(MarkovChain.END) + + def random_output(self, max=100): + """ Generate a list of elements from the markov chain. + The `max` value is in place in order to prevent excessive iteration. + """ + output = [] + item1 = item2 = MarkovChain.START + for i in range(max-3): + item3 = self[(item1, item2)].roll() + if item3 is MarkovChain.END: + break + output.append(item3) + item1 = item2 + item2 = item3 + return output diff --git a/calloway/apps/django_ext/middleware/debug.py b/calloway/apps/django_ext/middleware/debug.py index bf07243..650783e 100644 --- a/calloway/apps/django_ext/middleware/debug.py +++ b/calloway/apps/django_ext/middleware/debug.py @@ -5,14 +5,15 @@ import tempfile from django.conf import settings import hotshot, hotshot.stats +from functools import reduce try: from core.log import logger except ImportError: import logging as logger try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + from io import StringIO class SQLMiddleware: """ @@ -76,7 +77,7 @@ def get_group(self, file): return name[0] def get_summary(self, results_dict, sum): - list = [ (item[1], item[0]) for item in results_dict.items() ] + list = [ (item[1], item[0]) for item in list(results_dict.items()) ] list.sort( reverse = True ) list = list[:40] diff --git a/calloway/apps/django_ext/middleware/debug.py.bak b/calloway/apps/django_ext/middleware/debug.py.bak new file mode 100644 index 0000000..bf07243 --- /dev/null +++ b/calloway/apps/django_ext/middleware/debug.py.bak @@ -0,0 +1,143 @@ +from django.db import connection +import re +from operator import add +import sys +import tempfile +from django.conf import settings +import hotshot, hotshot.stats +try: + from core.log import logger +except ImportError: + import logging as logger +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class SQLMiddleware: + """ + This middleware logs all db queries through the logger. + """ + def process_response(self, request, response): + for query in connection.queries: + logger.debug(query['sql']) + return response + +# Orignal version taken from http://www.djangosnippets.org/snippets/186/ + +words_re = re.compile( r'\s+' ) + +group_prefix_re = [ + re.compile( "^.*/django/[^/]+" ), + re.compile( "^(.*)/[^/]+$" ), # extract module path + re.compile( ".*" ), # catch strange entries +] + +class ProfileMiddleware(object): + """ + Displays hotshot profiling for any view. + http://yoursite.com/yourview/ + + You'll see the profiling results in your log file. + + WARNING: It uses hotshot profiler which is not thread safe. + """ + def process_request(self, request): + self.tmpfile = tempfile.mktemp() + self.prof = hotshot.Profile(self.tmpfile) + + def process_view(self, request, callback, callback_args, callback_kwargs): + # turn on debugging in db backend to capture time + debug = settings.DEBUG + settings.DEBUG = True + + # get number of db queries before we do anything + n = len(connection.queries) + + value = self.prof.runcall(callback, request, *callback_args, **callback_kwargs) + + # compute the db time for the queries just run + queries = len(connection.queries) - n + if queries: + dbTime = reduce(add, [float(q['time']) for q in db.db.queries[n:]]) + else: + dbTime = 0.0 + + # restore debugging setting again + settings.DEBUG = debug + + logger.debug("\nDB Profile\nQueries: " + str(queries) + "\ndb Execution Time: "+str(dbTime)) + return value + + def get_group(self, file): + for g in group_prefix_re: + name = g.findall( file ) + if name: + return name[0] + + def get_summary(self, results_dict, sum): + list = [ (item[1], item[0]) for item in results_dict.items() ] + list.sort( reverse = True ) + list = list[:40] + + res = " tottime\n" + for item in list: + if sum: + res += "%4.1f%% %7.3f %s\n" % ( 100*item[0]/sum, item[0], item[1] ) + else: + res += "%4.1f%% %7.3f %s\n" % ( 0, item[0], item[1] ) + + return res + + def summary_for_files(self, stats_str): + stats_str = stats_str.split("\n")[5:] + + mystats = {} + mygroups = {} + + sum = 0 + + for s in stats_str: + fields = words_re.split(s); + if len(fields) == 7: + time = float(fields[2]) + sum += time + file = fields[6].split(":")[0] + + if not file in mystats: + mystats[file] = 0 + mystats[file] += time + + group = self.get_group(file) + if not group in mygroups: + mygroups[ group ] = 0 + mygroups[ group ] += time + + return " ---- By file ----\n\n" + self.get_summary(mystats,sum) + "\n ---- By group ---\n\n" + self.get_summary(mygroups,sum) + + def process_response(self, request, response): + self.prof.close() + + out = StringIO() + old_stdout = sys.stdout + sys.stdout = out + + stats = hotshot.stats.load(self.tmpfile) + stats.sort_stats('time', 'calls') + stats.print_stats() + + sys.stdout = old_stdout + stats_str = out.getvalue() + + profiling_info = response.content + + if response and response.content and stats_str: + profiling_info = stats_str + + profiling_info = "\n".join(profiling_info.split("\n")[:40]) + + profiling_info += self.summary_for_files(stats_str) + + logger.debug("\n"+profiling_info) + + return response \ No newline at end of file diff --git a/calloway/apps/django_ext/templatetags/fb.py b/calloway/apps/django_ext/templatetags/fb.py index 1f4286d..7416950 100644 --- a/calloway/apps/django_ext/templatetags/fb.py +++ b/calloway/apps/django_ext/templatetags/fb.py @@ -16,7 +16,7 @@ def render(self, context): from django.core.urlresolvers import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) - for k, v in self.kwargs.items()]) + for k, v in list(self.kwargs.items())]) # Try to look up the URL twice: once given the view name, and again # relative to what we guess is the "main" app. If they both fail, @@ -84,7 +84,7 @@ def fburl(parser, token): bits = iter(bits[2:]) for bit in bits: if bit == 'as': - asvar = bits.next() + asvar = next(bits) break else: for arg in bit.split(","): diff --git a/calloway/apps/django_ext/templatetags/fb.py.bak b/calloway/apps/django_ext/templatetags/fb.py.bak new file mode 100644 index 0000000..1f4286d --- /dev/null +++ b/calloway/apps/django_ext/templatetags/fb.py.bak @@ -0,0 +1,98 @@ +from django import template +from django.conf import settings + +from django.utils.encoding import smart_str + +register = template.Library() + +class URLNode(template.Node): + def __init__(self, view_name, args, kwargs, asvar): + self.view_name = view_name + self.args = args + self.kwargs = kwargs + self.asvar = asvar + + def render(self, context): + from django.core.urlresolvers import reverse, NoReverseMatch + args = [arg.resolve(context) for arg in self.args] + kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) + for k, v in self.kwargs.items()]) + + # Try to look up the URL twice: once given the view name, and again + # relative to what we guess is the "main" app. If they both fail, + # re-raise the NoReverseMatch unless we're using the + # {% url ... as var %} construct in which cause return nothing. + url = '' + try: + url = reverse(self.view_name, args=args, kwargs=kwargs) + except NoReverseMatch: + project_name = settings.SETTINGS_MODULE.split('.')[0] + try: + url = reverse(project_name + '.' + self.view_name, + args=args, kwargs=kwargs) + except NoReverseMatch: + if self.asvar is None: + raise + + if self.asvar: + context[self.asvar] = settings.URL_PREFIX + url + return '' + else: + return settings.URL_PREFIX + url + +def fburl(parser, token): + """ + Returns an absolute URL matching given view with its parameters. + + This is a way to define links that aren't tied to a particular URL + configuration:: + + {% url path.to.some_view arg1,arg2,name1=value1 %} + + The first argument is a path to a view. It can be an absolute python path + or just ``app_name.view_name`` without the project name if the view is + located inside the project. Other arguments are comma-separated values + that will be filled in place of positional and keyword arguments in the + URL. All arguments for the URL should be present. + + For example if you have a view ``app_name.client`` taking client's id and + the corresponding line in a URLconf looks like this:: + + ('^client/(\d+)/$', 'app_name.client') + + and this app's URLconf is included into the project's URLconf under some + path:: + + ('^clients/', include('project_name.app_name.urls')) + + then in a template you can create a link for a certain client like this:: + + {% url app_name.client client.id %} + + The URL will look like ``/clients/client/123/``. + """ + bits = token.contents.split(' ') + if len(bits) < 2: + raise template.TemplateSyntaxError("'%s' takes at least one argument" + " (path to a view)" % bits[0]) + viewname = bits[1] + args = [] + kwargs = {} + asvar = None + + if len(bits) > 2: + bits = iter(bits[2:]) + for bit in bits: + if bit == 'as': + asvar = bits.next() + break + else: + for arg in bit.split(","): + if '=' in arg: + k, v = arg.split('=', 1) + k = k.strip() + kwargs[k] = parser.compile_filter(v) + elif arg: + args.append(parser.compile_filter(arg)) + return URLNode(viewname, args, kwargs, asvar) +fburl = register.tag(fburl) \ No newline at end of file diff --git a/calloway/menu.py b/calloway/menu.py index 243caed..4a518cb 100644 --- a/calloway/menu.py +++ b/calloway/menu.py @@ -48,19 +48,19 @@ def get_url(self, url_or_dict): """ Returns the reversed url given a string or dict and prints errors if MENU_DEBUG is enabled """ - if isinstance(url_or_dict, basestring): + if isinstance(url_or_dict, str): url_or_dict = {'viewname': url_or_dict} try: return reverse(**url_or_dict) except NoReverseMatch: if MENU_DEBUG: - print >>stderr,'Unable to reverse URL with kwargs %s' % url_or_dict + print('Unable to reverse URL with kwargs %s' % url_or_dict, file=stderr) def __init__(self, **kwargs): super(DefaultMenu, self).__init__(**kwargs) for section,item in MENU_ITEMS: - if isinstance(item, basestring): + if isinstance(item, str): self.children.append(MenuItem(title=section, url=reverse(item))) else: children = [] diff --git a/calloway/menu.py.bak b/calloway/menu.py.bak new file mode 100644 index 0000000..243caed --- /dev/null +++ b/calloway/menu.py.bak @@ -0,0 +1,72 @@ +from sys import stderr +from django.conf import settings +from django.core.urlresolvers import reverse, NoReverseMatch +from django.utils.translation import ugettext_lazy as _ +from admin_tools.menu.models import * + +# ADMIN_TOOLS_MENU = 'calloway.menu.DefaultMenuMenu' +MENU_DEBUG = getattr(settings, 'ADMIN_TOOLS_MENU_DEBUG', False) + +MENU_ITEMS = getattr(settings, 'ADMIN_TOOLS_MENU_ITEMS', ( + ('Dashboard', 'admin:index'), + ('Content', [ + ('Stories', 'admin:stories_story_changelist'), + ('Polls', 'admin:pollit_poll_changelist'), + ('Blog Entries', 'admin:viewpoint_entry_changelist'), + ('Audio Clips', 'admin:massmedia_audio_changelist'), + ('Collections', 'admin:massmedia_collection_changelist'), + ('Documents', 'admin:massmedia_document_changelist'), + ('Images', 'admin:massmedia_image_changelist'), + ('Videos', 'admin:massmedia_video_changelist'), + ]), + ('Organize', [ + ('Blogs', 'admin:viewpoint_blog_changelist'), + ('Categories', 'admin:categories_category_changelist'), + ('Navigation Bar', 'admin:hiermenu_menu_changelist'), + ('Postions', 'admin:positions_position_changelist'), + ]), + ('Settings', [ + ('Analytics', 'admin:google_analytics_analytics_changelist'), + ('Redirects', 'admin:redirects_redirect_changelist'), + ('Robots', {'viewname':'admin:app_list', 'args':('robots',)}), + ('Syndication Feeds', 'admin:synagg_feed_changelist'), + ]), + ('Comments', [ + ('Comments', 'admin:mptt_comments_mpttcomment_changelist'), + ('Offensive Content', 'admin:offensivecontent_offensivecontent_changelist'), + ]), + ('Users', [ + ('Users', 'admin:auth_user_changelist'), + ('Banned IPs', 'admin:ban_deniedip_changelist'), + ]), +)) + + +class DefaultMenu(Menu): + + def get_url(self, url_or_dict): + """ + Returns the reversed url given a string or dict and prints errors if MENU_DEBUG is enabled + """ + if isinstance(url_or_dict, basestring): + url_or_dict = {'viewname': url_or_dict} + try: + return reverse(**url_or_dict) + except NoReverseMatch: + if MENU_DEBUG: + print >>stderr,'Unable to reverse URL with kwargs %s' % url_or_dict + + def __init__(self, **kwargs): + super(DefaultMenu, self).__init__(**kwargs) + + for section,item in MENU_ITEMS: + if isinstance(item, basestring): + self.children.append(MenuItem(title=section, url=reverse(item))) + else: + children = [] + for title,url in item: + url = self.get_url(url) + if url is None: + continue + children.append(MenuItem(title=title, url=url)) + self.children.append(MenuItem(title=section, children=children)) diff --git a/calloway/settings.py b/calloway/settings.py index 5cca6c4..9347653 100644 --- a/calloway/settings.py +++ b/calloway/settings.py @@ -7,7 +7,7 @@ CALLOWAY_ROOT = os.path.abspath(os.path.dirname(__file__)) #from calloway.default_settings import SITE_ID, APPEND_SLASH, MEDIA_URL, \ -from default_settings import * +from .default_settings import * class DynamicList(list): """Allows for the dynamic list created based on a base set of items, the @@ -25,7 +25,7 @@ class DynamicList(list): def __init__(self, *args): if len(args) > 1: self.DEFAULT_ITEMS = args[:] - elif args and isinstance(args[0], basestring): + elif args and isinstance(args[0], str): self.DEFAULT_ITEMS = [args[0],] elif args: self.DEFAULT_ITEMS = args[0] @@ -39,7 +39,7 @@ def _build_list(self): items = self.DEFAULT_ITEMS[:] items.extend(self.DJANGO_VERS_MAPPING[VERSION[1]]) - for app, val in self.APP_MAPPING.items(): + for app, val in list(self.APP_MAPPING.items()): if app in INSTALLED_APPS: items.extend(val) self.data = items diff --git a/calloway/settings.py.bak b/calloway/settings.py.bak new file mode 100644 index 0000000..5cca6c4 --- /dev/null +++ b/calloway/settings.py.bak @@ -0,0 +1,172 @@ +import os +import sys +import django +django_version = django.VERSION +from django.conf import settings + +CALLOWAY_ROOT = os.path.abspath(os.path.dirname(__file__)) + +#from calloway.default_settings import SITE_ID, APPEND_SLASH, MEDIA_URL, \ +from default_settings import * + +class DynamicList(list): + """Allows for the dynamic list created based on a base set of items, the + django version and what apps are installed""" + DEFAULT_ITEMS = [] + DJANGO_VERS_MAPPING = { + 0: [], + 1: [], + 2: [], + 3: [] + } + APP_MAPPING = { + } + + def __init__(self, *args): + if len(args) > 1: + self.DEFAULT_ITEMS = args[:] + elif args and isinstance(args[0], basestring): + self.DEFAULT_ITEMS = [args[0],] + elif args: + self.DEFAULT_ITEMS = args[0] + self.data = None + + def _build_list(self): + from django.conf import settings + INSTALLED_APPS = settings.INSTALLED_APPS[:] + from django import VERSION + + items = self.DEFAULT_ITEMS[:] + items.extend(self.DJANGO_VERS_MAPPING[VERSION[1]]) + + for app, val in self.APP_MAPPING.items(): + if app in INSTALLED_APPS: + items.extend(val) + self.data = items + + def __len__(self): + if self.data is None: + self._build_list() + return len(self.data) + + def __getitem__(self, key): + if self.data is None: + self._build_list() + return self.data[key] + + def __iter__(self): + if self.data is None: + self._build_list() + return self.data.__iter__() + + def __contains__(self, item): + if self.data is None: + self._build_list() + return self.data.__contains__(item) + + def __repr__(self): + if self.data is None: + self._build_list() + return self.data.__repr__() + +class TemplateContextProcs(DynamicList): + DEFAULT_ITEMS = [ + 'django.core.context_processors.debug', + 'django.core.context_processors.i18n', + 'django.core.context_processors.media', + 'django.core.context_processors.request', + ] + APP_MAPPING = { + 'staticmediamgr': ['staticmediamgr.context_processor.static_url',], + } + DJANGO_VERS_MAPPING = { + 0: ['django.core.context_processors.auth',], + 1: ['django.core.context_processors.auth',], + 2: ['django.core.context_processors.auth',], + 3: ['django.contrib.auth.context_processors.auth', + 'django.core.context_processors.static', + 'django.contrib.messages.context_processors.messages',], + } + +TEMPLATE_CONTEXT_PROCESSORS = TemplateContextProcs() + +class AuthenticationBackends(DynamicList): + DEFAULT_ITEMS = [ + 'django.contrib.auth.backends.ModelBackend', + ] + APP_MAPPING = { + 'django_ext': ['django_ext.caseinsensitiveauth.CaseInsensitiveModelBackend',] + } + +AUTHENTICATION_BACKENDS = AuthenticationBackends() + +class Middleware(DynamicList): + """ + Since the placement of the middleware class is important, and the predecessor + and successor are or can be unknown we are using a tuple consisting of a rank + and value. + + The default classes are each ranked in multiples of 10. This allows plenty + room for other apps to insert their class(es) between them. + + The DJANGO_VER_MAPPING looks at the minor version number of Django to + dynamically insert version-specific middleware classes. + + Each application in the APP_MAPPING dictionary has a value that is a list + of rank-class tuples that can extend the DEFAULT_CLASSES list. + """ + # Maps the minor version number of Django (1.0, 1.1, 1.2) + DJANGO_VERS_MAPPING = { + 0: [(10, 'django.middleware.cache.UpdateCacheMiddleware',), + (110, 'django.middleware.cache.FetchFromCacheMiddleware',),], + 1: [(10, 'django.middleware.cache.UpdateCacheMiddleware',), + (110, 'django.middleware.cache.FetchFromCacheMiddleware',),], + 2: [(34, 'django.middleware.csrf.CsrfViewMiddleware',), + (44, 'django.contrib.messages.middleware.MessageMiddleware',), + (64, 'django.middleware.csrf.CsrfResponseMiddleware',),], + 3: [(34, 'django.middleware.csrf.CsrfViewMiddleware',), + (44, 'django.contrib.messages.middleware.MessageMiddleware',),], + } + + APP_MAPPING = { + 'django_ext': [(36, 'django_ext.middleware.cookie.UsernameInCookieMiddleware',)], + 'debug_toolbar': [(75, 'debug_toolbar.middleware.DebugToolbarMiddleware',)], + 'pagination': [(103, 'pagination.middleware.PaginationMiddleware',)], + 'ban': [(106, 'ban.middleware.DenyMiddleware',)], + } + + DEFAULT_ITEMS = [ + (20, 'django.middleware.common.CommonMiddleware',), + (30, 'django.contrib.sessions.middleware.SessionMiddleware',), + (40, 'django.contrib.auth.middleware.AuthenticationMiddleware',), + (50, 'django.middleware.gzip.GZipMiddleware',), + (60, 'django.middleware.http.ConditionalGetMiddleware',), + (70, 'django.middleware.doc.XViewMiddleware',), + (80, 'django.contrib.redirects.middleware.RedirectFallbackMiddleware',), + (90, 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',), + (100, 'django.middleware.transaction.TransactionMiddleware',), + + ] + + def _build_list(self): + super(Middleware, self)._build_list() + self.data.sort(cmp=lambda x,y: cmp(x[0], y[0])) + self.data = [x[1] for x in self.data] + +MIDDLEWARE_CLASSES = Middleware() + +__all__ = [ + 'SITE_ID', 'APPEND_SLASH', 'MEDIA_URL', 'STATIC_URL', 'ADMIN_MEDIA_PREFIX', + 'TEMPLATE_LOADERS', 'ROOT_URLCONF', 'CALLOWAY_TEMPLATE_DIRS', + 'APPS_DJANGO_BASE', 'APPS_DJANGO13_BASE', 'APPS_DJANGO_TEMPLATE_UTILS', + 'APPS_MESSAGES', 'APPS_CORE', 'APPS_ADMIN', 'APPS_CALLOWAY_DEFAULT', + 'APPS_CACHING', 'APPS_MPTT', 'APPS_STAFF', 'APPS_REVERSION', 'APPS_STORIES', + 'APPS_CATEGORIES', 'APPS_COMMENT_UTILS', 'APPS_FRONTEND_ADMIN', 'APPS_MEDIA', + 'APPS_UTILS', 'APPS_REGISTRATION', 'APPS_TINYMCE', 'APPS_BLOGGING', + 'DJANGO_MEMCACHED_REQUIRE_STAFF', 'TINYMCE_DEFAULT_CONFIG', + 'TINYMCE_ADMIN_FIELDS', 'REVERSION_MODELS', 'VARNISH_WATCHED_MODELS', + 'VARNISH_MANAGEMENT_ADDRS', 'NATIVE_TAGS', 'ADMIN_TOOLS_MENU', + 'INTERNAL_IPS', 'STATIC_MEDIA_PURGE_OLD_FILES', 'DEBUG_TOOLBAR_PANELS', + 'DEBUG_TOOLBAR_CONFIG', 'MIDDLEWARE_CLASSES', 'TEMPLATE_CONTEXT_PROCESSORS', + 'AUTHENTICATION_BACKENDS' + ] diff --git a/doc_src/conf.py b/doc_src/conf.py index b13e735..063f5ee 100644 --- a/doc_src/conf.py +++ b/doc_src/conf.py @@ -38,8 +38,8 @@ master_doc = 'index' # General information about the project. -project = u'Calloway' -copyright = u'2010, The Calloway Project' +project = 'Calloway' +copyright = '2010, The Calloway Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -173,8 +173,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'app.tex', u'Calloway Documentation', - u'calloway project', 'manual'), + ('index', 'app.tex', 'Calloway Documentation', + 'calloway project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/doc_src/conf.py.bak b/doc_src/conf.py.bak new file mode 100644 index 0000000..b13e735 --- /dev/null +++ b/doc_src/conf.py.bak @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# +# app documentation build configuration file, created by +# sphinx-quickstart on Wed Oct 21 13:18:22 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('..')) +import calloway + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Calloway' +copyright = u'2010, The Calloway Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = calloway.get_version() +# The full version, including alpha/beta/rc tags. +release = calloway.get_version() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "