Commits

Sergiy Kuzmenko committed 1f5dfc2

initial import

  • Participants

Comments (0)

Files changed (16)

+syntax: glob
+*.pyc
+*~
+*.kpf
+xdjango stands either for "extended Django" or "experimental Django". The choice
+is yours. This is a hodge-podge of miscellaneous idioms and shortcuts
+that are not part of the standard Django distribution.
+
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(name='xDjango',
+      version='0.1-alpha',
+      description='Experimental Django extentions',
+      author='Sergiy Kuzmenko',
+      author_email='sergiy@kuzmenko.org',
+      packages=['xdjango', 'xdjango.db', 'xdjango.db.models', 'xdjango.utils'],
+      classifiers=[
+        'Development Status :: 3 - Alpha',
+        'Framework :: Django',
+        'Intended Audience :: Developers',
+        'License :: Public Domain',
+      ]
+     )

File tests/modeltests/__init__.py

Empty file added.

File tests/modeltests/forms.py

+#!/usr/bin/env python
+
+from django.forms.models import ModelForm
+from .models import Employee
+
+class EmployeeForm(ModelForm):
+    class Meta:
+        model = Employee

File tests/modeltests/models.py

+from xdjango.db.models.fields import WeekdaySetField, DEFAULT_WEEKDAY_CHOICES
+from django.db import models
+
+class LimitedWeekdaySetField(WeekdaySetField):
+    weekday_choices = DEFAULT_WEEKDAY_CHOICES[4:] # Fri, Sat and Sun
+
+class Employee(models.Model):
+    name = models.CharField(max_length=64)
+    work_days = WeekdaySetField(blank=True)
+    preferred_weekends = LimitedWeekdaySetField(blank=False)

File tests/modeltests/tests.py

+from __future__ import absolute_import
+
+from xdjango.utils.datastruc import WeekdaySet
+from django.test import TestCase
+from django.core.exceptions import ValidationError
+from .models import Employee
+from .forms import EmployeeForm
+
+class WeekdaySetFieldTestCase(TestCase):
+    
+    def test_model_form_validation(self):
+        """ Passing invalid value to WeekdaySetField. """
+        form = EmployeeForm({
+            'name': 'Some Name',
+            'work_days': ['0','1','2','3','4','5','6'],
+            'preferred_weekends': ['4','5','6']
+        })
+        self.assertTrue(form.is_valid())
+        
+        # 7 and 8 out of range
+        form = EmployeeForm({
+            'name': 'Some Name',
+            'work_days': ['6','7'],
+            'preferred_weekends': ['4','5','6']
+        })
+        self.assertFalse(form.is_valid())
+        
+        # 1 is not allowed for preferred_weekends
+        form = EmployeeForm({
+            'name': 'Some Name',
+            'work_days': ['0','1','2','3','4','5','6'],
+            'preferred_weekends': ['1'],
+        })
+        self.assertFalse(form.is_valid())
+   
+    def test_to_python(self):
+        """ Make sure we get correct WeekdaySetField values. """
+        employee = Employee.objects.create(name="Some name", work_days=[0, 1], preferred_weekends=[4, 5, 6])
+        self.assertEqual(type(employee.work_days), WeekdaySet)
+        self.assertEqual(str(employee.work_days), "01")
+        self.assertEqual(unicode(employee.work_days), "Monday, Tuesday")
+        self.assertEqual(type(employee.preferred_weekends), WeekdaySet)
+        self.assertEqual(str(employee.preferred_weekends), "456")
+        self.assertEqual(unicode(employee.preferred_weekends), "Friday, Saturday, Sunday")

File tests/runtests.py

+#!/usr/bin/env python
+import os
+import sys
+
+LIB_PATH = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
+sys.path.insert(0, LIB_PATH)
+
+def run_tests(verbosity, interactive, failfast, test_labels):
+    from django.test.utils import setup_test_environment
+    from django.test.simple import DjangoTestSuiteRunner
+    setup_test_environment()
+    runner = DjangoTestSuiteRunner(verbosity, interactive, failfast)
+    return runner.run_tests(test_labels)
+    
+
+if __name__ == "__main__":
+    from optparse import OptionParser
+    usage = "%prog [options] [module module module ...]"
+    parser = OptionParser(usage=usage)
+    parser.add_option(
+        '-v','--verbosity', action='store', dest='verbosity', default='1',
+        type='choice', choices=['0', '1', '2', '3'],
+        help='Verbosity level; 0=minimal output, 1=normal output, 2=all '
+             'output')
+    parser.add_option(
+        '--noinput', action='store_false', dest='interactive', default=True,
+        help='Tells Django to NOT prompt the user for input of any kind.')
+    parser.add_option(
+        '--failfast', action='store_true', dest='failfast', default=False,
+        help='Tells Django to stop running the test suite after first failed '
+             'test.')
+    parser.add_option(
+        '--settings',
+        help='Python path to settings module, e.g. "myproject.settings". If '
+             'this isn\'t provided, the DJANGO_SETTINGS_MODULE environment '
+             'variable will be used.')
+
+    options, args = parser.parse_args()
+    if options.settings:
+        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
+    elif "DJANGO_SETTINGS_MODULE" not in os.environ:
+        parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
+                      "Set it or use --settings.")
+    
+    failures = run_tests(int(options.verbosity), options.interactive, options.failfast, args)
+    if failures:
+        sys.exit(failures)

File tests/settings_sqlite.py

+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3'
+    },
+}
+
+SECRET_KEY = '~~~~~ secret key for testing ~~~~~'
+
+INSTALLED_APPS = (
+    'modeltests',
+)
+

File xdjango/__init__.py

Empty file added.

File xdjango/db/__init__.py

Empty file added.

File xdjango/db/models/__init__.py

Empty file added.

File xdjango/db/models/fields.py

+from django.db import models
+from django.forms.widgets import CheckboxSelectMultiple
+from django.forms.fields import MultipleChoiceField
+from django.utils.translation import ugettext_lazy as _
+from django.utils.text import capfirst
+from django.utils.dates import WEEKDAYS
+from xdjango.utils.datastruc import WeekdaySet
+
+DEFAULT_WEEKDAY_CHOICES = [ (x, WEEKDAYS[x]) for x in range(0,7) ]
+
+
+class WeekdaySetField(models.Field):
+    """ At db level WeekdaySetField stores ISO weekdays as strings representing
+    ordered number sequence, e.g., "01" for "Monday and Tuesday" and "" for
+    empty weekday set.
+    
+    To limit weekday choices (e.g., if you need to make sure that only Monday
+    to Friday are selectable), subclass WeekdaySetField and give it a custom
+    weekday_choices attribute:
+    
+    MyWeekdaySetField(WeekdaySetField):
+        weekday_choices = my_weekday_choices
+    
+    """
+    # TODO: select first week day dynamically based on user locale
+    
+    weekday_choices = DEFAULT_WEEKDAY_CHOICES
+    
+    default_error_messages = {
+        "invalid_weekday" : _("Weekday must be in the range 0-6")
+    }
+    
+    __metaclass__ = models.SubfieldBase
+    
+    def __init__(self, *args, **kwargs):
+        kwargs.update({"max_length" : 7,"choices" : None})
+        super(WeekdaySetField, self).__init__(*args, **kwargs)
+
+    def get_internal_type(self):
+        return "CharField"
+
+    def to_python(self, value):
+        if isinstance(value, (list, basestring)):
+            return WeekdaySet(value)
+        return value        
+    
+    def get_prep_value(self, value):
+        return str(value)
+    
+    def formfield(self, **kwargs):
+        defaults = { 'widget' : CheckboxSelectMultiple, 'choices': self.weekday_choices }
+        defaults.update(kwargs)
+        return super(WeekdaySetField, self).formfield(MultipleChoiceField, **defaults)
+    

File xdjango/tests.py

+from django.test import TestCase
+from xdjango.models.fields import WeekdaySetField
+
+# TODO: test WeekdaySetField with default args
+
+# TODO: test WeekdaySetField with default args with custom weekday choice
+

File xdjango/utils/__init__.py

Empty file added.

File xdjango/utils/datastruc.py

+# common data structure definitions
+from django.utils.translation import ugettext_lazy as _
+from django.utils.dates import WEEKDAYS
+
+class WeekdaySet(set):
+    """ A weekday aware set subclass.
+    
+    >>> wds = WeekdaySet("123")
+    >>> wds.issubset(WeekdaySet([1,2,3,4,5,0]))
+    True
+    >>> wds.add(0)
+    >>> wds
+    WeekdaySet([0, 1, 2, 3])
+    >>> wds | WeekdaySet([5,6])
+    WeekdaySet([0, 1, 2, 3, 5, 6])
+    
+    Attempt to add or initialize WeekdaySet with an invalid weekday value will
+    raise ValueError:
+    
+    >>> wds.add(9)
+    Traceback (most recent call last):
+        ...
+    ValueError: Weekday is not in the range [0-6]
+    
+    """
+    
+    def __init__(self, weekdays=[]):
+        super(WeekdaySet, self).__init__(self._normalize_iterable(weekdays))
+    
+    def _normalize_element(self, value):
+        " Validates a single weekday value and returns its integer representation. "
+        v = int(value)
+        if v>6 or v<0:
+            raise ValueError("Weekday is not in the range [0-6]")
+        return v
+    
+    
+    def _normalize_iterable(self, value):
+        " Converts and validates an iterable to be used as input to __init__"
+        return [self._normalize_element(x) for x in value]
+    
+    
+    def add(self, elem):
+        super(WeekdaySet, self).add(self._normalize_element(elem))
+    
+    
+    def human_readable(self, name_mapping=WEEKDAYS, separator=", "):
+        """
+        Returns a human readable representation of weekday sequence. Takes two
+        optional arguments for weekday name mapping and output separator. If name
+        mapping omitted, weekday names will be automatically translated based on
+        current locale.
+        
+        >>> wd = WeekdaySet([5,6])
+        >>> wd.human_readable(["MON","TUE","WED","THU","FRI","SAT","SUN"], " & ")
+        'SAT & SUN'
+        """
+        return separator.join([unicode(name_mapping[x]) for x in sorted(self)])
+    
+    
+    def __str__(self):
+        """
+        >>> str(WeekdaySet([5,6]))
+        '56'
+        """
+        return "".join([str(x) for x in sorted(self)])
+    
+    
+    def __unicode__(self):
+        return unicode(self.human_readable())
+
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()