forked from Tivix/django-common
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sumit Chachra
committed
Mar 15, 2011
0 parents
commit a1af1b0
Showing
18 changed files
with
704 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.pyc | ||
*.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
http://github.com/Tivix/django-cron/contributors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2011 Tivix, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
=========== | ||
django-cron | ||
=========== | ||
|
||
Overview | ||
-------- | ||
|
||
Django-common consists of the following things: | ||
|
||
- A middleware that makes sure your web-app runs either on or without 'www' in the domain. | ||
|
||
- A ``SessionManagerBase`` base class, that helps in keeping your session related code object-oriented and clean! See session.py for usage details. | ||
|
||
- Some custom db fields that you can use in your models including a ``UniqueHashField`` and ``RandomHashField``. | ||
|
||
- Bunch of helpful functions in helper.py | ||
|
||
- A ``render_form_field`` template tag that makes rendering form fields easy and DRY. | ||
|
||
- A couple of dry response classes: ``JsonResponse`` and ``XMLResponse`` in the django_common.http that can be used in views that give json/xml responses. | ||
|
||
|
||
Installation | ||
------------ | ||
|
||
- Install django_common (ideally in your virtualenv!) using pip or simply getting a copy of the code and putting it in a | ||
directory in your codebase. | ||
|
||
- Add ``django_common`` to your Django settings ``INSTALLED_APPS``:: | ||
INSTALLED_APPS = [ | ||
# ... | ||
"django_common", | ||
] | ||
|
||
- Add the following to your settings.py with appropriate values: | ||
|
||
- IS_DEV | ||
- IS_PROD | ||
- DOMAIN_NAME | ||
- WWW_ROOT | ||
|
||
- Add ``common_settings`` to your Django settings ``TEMPLATE_CONTEXT_PROCESSORS``:: | ||
TEMPLATE_CONTEXT_PROCESSORS = [ | ||
# ... | ||
"common_settings", | ||
] | ||
|
||
- Add ``WWWRedirectMiddleware`` if required to the list of middlewares:: | ||
MIDDLEWARE_CLASSES = [ | ||
# ... | ||
"WWWRedirectMiddleware", | ||
] | ||
|
||
|
||
This open-source app is brought to you by Tivix, Inc. ( http://tivix.com/ ) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import logging | ||
|
||
from django.contrib.auth.backends import ModelBackend | ||
from django.contrib.auth.models import User | ||
|
||
|
||
class EmailBackend(ModelBackend): | ||
def authenticate(self, username=None, password=None): | ||
""" | ||
"username" being passed is really email address and being compared to as such. | ||
""" | ||
try: | ||
user = User.objects.get(email=username) | ||
if user.check_password(password): | ||
return user | ||
except (User.DoesNotExist, User.MultipleObjectsReturned): | ||
logging.warn('Unsuccessful login attempt using username/email: %s' % username) | ||
|
||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.conf import settings as django_settings | ||
|
||
|
||
def common_settings(request): | ||
return { | ||
'domain_name': django_settings.DOMAIN_NAME, | ||
'www_root': django_settings.WWW_ROOT, | ||
|
||
'is_dev': django_settings.IS_DEV, | ||
'is_prod': django_settings.IS_PROD, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
from django.core import exceptions, validators | ||
from django.db.models import fields | ||
from django.template.defaultfilters import slugify | ||
from django.db import models | ||
from django.core.serializers.json import DjangoJSONEncoder | ||
from django.utils import simplejson as json | ||
|
||
from south.modelsinspector import add_introspection_rules | ||
from django_common.helper import md5_hash | ||
|
||
|
||
class JSONField(models.TextField): | ||
""" | ||
JSONField is a generic textfield that neatly serializes/unserializes JSON objects seamlessly | ||
""" | ||
|
||
# Used so to_python() is called | ||
__metaclass__ = models.SubfieldBase | ||
|
||
def to_python(self, value): | ||
"""Convert our string value to JSON after we load it from the DB""" | ||
|
||
if value == "": | ||
return None | ||
|
||
try: | ||
if isinstance(value, basestring): | ||
return json.loads(value) | ||
except ValueError: | ||
pass | ||
|
||
return value | ||
|
||
def get_db_prep_save(self, value): | ||
"""Convert our JSON object to a string before we save""" | ||
|
||
if value == "": | ||
return None | ||
|
||
if isinstance(value, dict): | ||
value = json.dumps(value, cls=DjangoJSONEncoder) | ||
|
||
return super(JSONField, self).get_db_prep_save(value) | ||
|
||
class UniqueSlugField(fields.SlugField): | ||
""" | ||
Represents a self-managing sluf field, that makes sure that the slug value is unique on the db table. Slugs by | ||
default get a db_index on them. The "Unique" in the class name is a misnomer since it does support unique=False | ||
@requires "prepopulate_from" in the constructor. This could be a field or a function in the model class which is using | ||
this field | ||
Defaults update_on_save to False | ||
Taken and edited from: http://www.djangosnippets.org/snippets/728/ | ||
""" | ||
def __init__(self, prepopulate_from='id', *args, **kwargs): | ||
if kwargs.get('update_on_save'): | ||
self.__update_on_save = kwargs.pop('update_on_save') | ||
else: | ||
self.__update_on_save = False | ||
self.prepopulate_from = prepopulate_from | ||
super(UniqueSlugField, self).__init__(*args, **kwargs) | ||
|
||
def pre_save(self, model_instance, add): | ||
prepopulate_field = getattr(model_instance, self.prepopulate_from) | ||
if callable(prepopulate_field): | ||
prepopulate_value = prepopulate_field() | ||
else: | ||
prepopulate_value = prepopulate_field | ||
|
||
# if object has an id, and not to update on save, then return existig model instance's slug value | ||
if getattr(model_instance, 'id') and not self.__update_on_save: | ||
return getattr(model_instance, self.name) | ||
|
||
# if this is a previously saved object, and current instance's slug is same as one being proposed | ||
if getattr(model_instance, 'id') and getattr(model_instance, self.name) == prepopulate_value: | ||
return self.__set_and_return(model_instance, self.name, slugify(prepopulate_value)) | ||
|
||
# if a unique slug is not required (not the default of course) | ||
if not self.unique: | ||
return self.__set_and_return(model_instance, self.name, slugify(prepopulate_value)) | ||
|
||
return self.__unique_slug(model_instance.__class__, model_instance, self.name, | ||
prepopulate_value) | ||
|
||
def __unique_slug(self, model, model_instance, slug_field, slug_value): | ||
orig_slug = slug = slugify(slug_value) | ||
index = 1 | ||
while True: | ||
try: | ||
model.objects.get(**{slug_field: slug}) | ||
index += 1 | ||
slug = orig_slug + '-' + str(index) | ||
except model.DoesNotExist: | ||
return self.__set_and_return(model_instance, slug_field, slug) | ||
|
||
def __set_and_return(self, model_instance, slug_field, slug): | ||
setattr(model_instance, slug_field, slug) | ||
return slug | ||
|
||
add_introspection_rules([ | ||
( | ||
[UniqueSlugField], # Class(es) these apply to | ||
[], # Positional arguments (not used) | ||
{ # Keyword argument | ||
"prepopulate_from": ["prepopulate_from", {"default": 'id'}], | ||
}, | ||
), | ||
], ["^django_common\.db_fields\.UniqueSlugField"]) | ||
|
||
|
||
class RandomHashField(fields.CharField): | ||
""" | ||
Store a random hash for a certain model field. | ||
@param update_on_save optional field whether to update this hash or not, everytime the model instance is saved | ||
""" | ||
def __init__(self, update_on_save=False, *args, **kwargs): | ||
#TODO: args & kwargs serve no purpose but to make django evolution to work | ||
self.update_on_save = update_on_save | ||
super(fields.CharField, self).__init__(max_length=128, unique=True, blank=False, null=False, db_index=True, default=md5_hash()) | ||
|
||
def pre_save(self, model_instance, add): | ||
if not add and not self.update_on_save: | ||
return getattr(model_instance, self.name) | ||
|
||
random_hash = md5_hash() | ||
setattr(model_instance, self.name, random_hash) | ||
return random_hash | ||
|
||
add_introspection_rules([ | ||
( | ||
[RandomHashField], # Class(es) these apply to | ||
[], # Positional arguments (not used) | ||
{ # Keyword argument | ||
"update_on_save": ["update_on_save", {"default": False}], | ||
}, | ||
), | ||
], ["^django_common\.db_fields\.RandomHashField"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
"Some common routines that can be used throughout the code." | ||
import hashlib, os, logging, re, datetime, threading | ||
|
||
from django.http import Http404 | ||
from django.utils import simplejson | ||
from django.utils.encoding import force_unicode | ||
from django.template import Context, RequestContext | ||
from django.template.loader import get_template | ||
from django.core import exceptions | ||
|
||
from apps.common.tzinfo import utc, Pacific | ||
|
||
|
||
class AppException(exceptions.ValidationError): | ||
"""Base class for exceptions used in our system. | ||
A common base class permits application code to distinguish between exceptions raised in our code from ones raised | ||
in libraries. | ||
""" | ||
pass | ||
|
||
class InvalidContentType(AppException): | ||
def __init__(self, file_types, msg=None): | ||
msg = msg or 'Only the following file content types are permitted: %s' % str(file_types) | ||
super(self.__class__, self).__init__(msg) | ||
self.file_types = file_types | ||
|
||
class FileTooLarge(AppException): | ||
def __init__(self, file_size_kb, msg=None): | ||
msg = msg or 'Files may not be larger than %s KB' % file_size_kb | ||
super(self.__class__, self).__init__(msg) | ||
self.file_size = file_size_kb | ||
|
||
def is_among(value, *possibilities): | ||
"Ensure that the method that has been used for the request is one of the expected ones (e.g., GET or POST)." | ||
for possibility in possibilities: | ||
if value == possibility: | ||
return True | ||
raise Exception, 'A different request value was encountered than expected: %s' % value | ||
|
||
def form_errors_serialize(form): | ||
errors = {} | ||
for field in form.fields.keys(): | ||
if form.errors.has_key(field): | ||
if form.prefix: | ||
errors['%s-%s' % (form.prefix, field)] = force_unicode(form.errors[field]) | ||
else: | ||
errors[field] = force_unicode(form.errors[field]) | ||
|
||
if form.non_field_errors(): | ||
errors['non_field_errors'] = force_unicode(form.non_field_errors()) | ||
return {'errors': errors} | ||
|
||
def json_response(data={ }, errors=[ ], success=True): | ||
data.update({ | ||
'errors': errors, | ||
'success': len(errors) == 0 and success, | ||
}) | ||
return simplejson.dumps(data) | ||
|
||
def sha224_hash(): | ||
return hashlib.sha224(os.urandom(224)).hexdigest() | ||
|
||
def sha1_hash(): | ||
return hashlib.sha1(os.urandom(224)).hexdigest() | ||
|
||
def md5_hash(image=None, max_length=None): | ||
# TODO: Figure out how much entropy is actually needed, and reduce the current number of bytes if possible if doing | ||
# so will result in a performance improvement. | ||
if max_length: | ||
assert max_length > 0 | ||
|
||
ret = hashlib.md5(image or os.urandom(224)).hexdigest() | ||
return ret if not max_length else ret[:max_length] | ||
|
||
def start_thread(target, *args): | ||
t = threading.Thread(target=target, args=args) | ||
t.setDaemon(True) | ||
t.start() | ||
|
||
def send_mail(subject, message, from_email, recipient_emails): | ||
import django.core.mail | ||
try: | ||
logging.debug('Sending mail to: %s' % recipient_emails) | ||
logging.debug('Message: %s' % message) | ||
django.core.mail.send_mail(subject, message, from_email, recipient_emails) | ||
except Exception, e: | ||
# TODO: Raise error again so that more information is included in the logs? | ||
logging.error('Error sending message [%s] from %s to %s %s' % (subject, from_email, recipient_emails, e)) | ||
|
||
def send_mail_in_thread(subject, message, from_email, recipient_emails): | ||
start_thread(send_mail, subject, message, from_email, recipient_emails) | ||
|
||
def send_mail_using_template(subject, template_name, from_email, recipient_emails, context_map, in_thread=False): | ||
t = get_template(template_name) | ||
message = t.render(Context(context_map)) | ||
if in_thread: | ||
return send_mail_in_thread(subject, message, from_email, recipient_emails) | ||
else: | ||
return send_mail(subject, message, from_email, recipient_emails) | ||
|
||
def utc_to_pacific(timestamp): | ||
return timestamp.replace(tzinfo=utc).astimezone(Pacific) | ||
|
||
def pacific_to_utc(timestamp): | ||
return timestamp.replace(tzinfo=Pacific).astimezone(utc) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from StringIO import StringIO | ||
|
||
from django.core.urlresolvers import reverse | ||
from django.http import HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, Http404 | ||
from django.utils import simplejson | ||
|
||
|
||
class JsonResponse(HttpResponse): | ||
def __init__(self, data={ }, errors=[ ], success=True): | ||
""" | ||
data is a map, errors a list | ||
""" | ||
json = json_response(data=data, errors=errors, success=success) | ||
super(JsonResponse, self).__init__(json, mimetype='application/json') | ||
|
||
def json_response(data={ }, errors=[ ], success=True): | ||
data.update({ | ||
'errors': errors, | ||
'success': len(errors) == 0 and success, | ||
}) | ||
return simplejson.dumps(data) | ||
|
||
|
||
class XMLResponse(HttpResponse): | ||
def __init__(self, data): | ||
""" | ||
data is the entire xml body/document | ||
""" | ||
super(XMLResponse, self).__init__(data, mimetype='text/xml') |
Oops, something went wrong.