[Python-modules-commits] [django-recurrence] 02/13: Import django-recurrence_1.3.0.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Tue May 3 18:41:14 UTC 2016
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository django-recurrence.
commit 9701c091ddfd818b6a42e2b4dbdb4c3b28497833
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Tue May 3 19:16:23 2016 +0200
Import django-recurrence_1.3.0.orig.tar.gz
---
MANIFEST.in | 3 +
PKG-INFO | 4 +-
README.md | 55 ++++++++
django_recurrence.egg-info/PKG-INFO | 4 +-
django_recurrence.egg-info/SOURCES.txt | 21 ++-
docs/changelog.rst | 32 +++--
docs/conf.py | 10 +-
docs/github.py | 71 +++++++++++
docs/installation.rst | 27 ++--
docs/usage/getting_started.rst | 28 ++++
pytest.ini | 3 +
recurrence/base.py | 4 +-
recurrence/fields.py | 34 ++---
setup.py | 4 +-
tests/__init__.py | 0
tests/models.py | 10 ++
tests/settings.py | 55 ++++++++
tests/test_exclusions.py | 83 ++++++++++++
tests/test_fields.py | 159 +++++++++++++++++++++++
tests/test_magic_methods.py | 63 +++++++++
tests/test_managers_recurrence.py | 136 ++++++++++++++++++++
tests/test_managers_rule.py | 97 ++++++++++++++
tests/test_nulls.py | 27 ++++
tests/test_occurrences.py | 211 +++++++++++++++++++++++++++++++
tests/test_recurrences_without_limits.py | 141 +++++++++++++++++++++
tests/test_saving.py | 183 +++++++++++++++++++++++++++
tests/test_serialization.py | 69 ++++++++++
tests/test_to_text.py | 53 ++++++++
tests/test_to_weekday.py | 50 ++++++++
tests/test_weekday.py | 24 ++++
30 files changed, 1609 insertions(+), 52 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 6d4985b..10c1d7a 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,8 @@
+include README.md
include LICENSE
include MANIFEST.in
+include pytest.ini
recursive-include docs *
recursive-include recurrence/static *
recursive-include recurrence/locale *
+recursive-include tests *
diff --git a/PKG-INFO b/PKG-INFO
index 6827869..b946ee6 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-recurrence
-Version: 1.2.0
+Version: 1.3.0
Summary: Django utility wrapping dateutil.rrule
Home-page: UNKNOWN
Author: Tamas Kemenczy
@@ -16,11 +16,11 @@ Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Requires: Django
Requires: pytz
Requires: python_dateutil
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4316194
--- /dev/null
+++ b/README.md
@@ -0,0 +1,55 @@
+# django-recurrence
+
+django-recurrence is a utility for working with recurring dates in
+Django. Documentation is available at
+https://django-recurrence.readthedocs.org/.
+
+It provides:
+
+- Recurrence/Rule objects using a subset of rfc2445 (wraps
+ `dateutil.rrule`) for specifying recurring date/times;
+- `RecurrenceField` for storing recurring datetimes in the database;
+- JavaScript widget.
+
+`RecurrenceField` provides a Django model field which serializes
+recurrence information for storage in the database.
+
+For example - say you were storing information about a university
+course in your app. You could use a model like this:
+
+```python
+import recurrence.fields
+
+class Course(models.Model):
+ title = models.CharField(max_length=200)
+ start = models.TimeField()
+ end = models.TimeField()
+ recurrences = recurrence.fields.RecurrenceField()
+```
+
+You'll notice that I'm storing my own start and end time. The
+recurrence field only deals with _recurrences_ not with specific time
+information. I have an event that starts at 2pm. Its recurrences
+would be "every Friday". For this to work, you'll need to put the
+`recurrence` application into your `INSTALLED_APPS`
+
+## Running the tests
+
+Our test coverage is currently fairly poor (we're working on it!),
+but you can run the tests by making sure you've got the test
+requirements installed:
+
+ pip install -r requirements_test.txt
+
+Once you've done that, you can run the tests using:
+
+ make test
+
+You can generate a coverage report by running:
+
+ make coverage
+
+You can run tests on multiple versions of Python and Django by
+installing tox (`pip install tox`) and running:
+
+ tox
diff --git a/django_recurrence.egg-info/PKG-INFO b/django_recurrence.egg-info/PKG-INFO
index 6827869..b946ee6 100644
--- a/django_recurrence.egg-info/PKG-INFO
+++ b/django_recurrence.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-recurrence
-Version: 1.2.0
+Version: 1.3.0
Summary: Django utility wrapping dateutil.rrule
Home-page: UNKNOWN
Author: Tamas Kemenczy
@@ -16,11 +16,11 @@ Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Requires: Django
Requires: pytz
Requires: python_dateutil
diff --git a/django_recurrence.egg-info/SOURCES.txt b/django_recurrence.egg-info/SOURCES.txt
index c37aab7..74b02ac 100644
--- a/django_recurrence.egg-info/SOURCES.txt
+++ b/django_recurrence.egg-info/SOURCES.txt
@@ -1,5 +1,7 @@
LICENSE
MANIFEST.in
+README.md
+pytest.ini
setup.py
django_recurrence.egg-info/PKG-INFO
django_recurrence.egg-info/SOURCES.txt
@@ -11,6 +13,7 @@ docs/Makefile
docs/changelog.rst
docs/conf.py
docs/contributing.rst
+docs/github.py
docs/index.rst
docs/installation.rst
docs/make.bat
@@ -46,4 +49,20 @@ recurrence/locale/nl/LC_MESSAGES/djangojs.po
recurrence/static/recurrence/css/recurrence.css
recurrence/static/recurrence/img/recurrence-calendar-icon.png
recurrence/static/recurrence/js/recurrence-widget.js
-recurrence/static/recurrence/js/recurrence.js
\ No newline at end of file
+recurrence/static/recurrence/js/recurrence.js
+tests/__init__.py
+tests/models.py
+tests/settings.py
+tests/test_exclusions.py
+tests/test_fields.py
+tests/test_magic_methods.py
+tests/test_managers_recurrence.py
+tests/test_managers_rule.py
+tests/test_nulls.py
+tests/test_occurrences.py
+tests/test_recurrences_without_limits.py
+tests/test_saving.py
+tests/test_serialization.py
+tests/test_to_text.py
+tests/test_to_weekday.py
+tests/test_weekday.py
\ No newline at end of file
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 4ea5a97..c0c7012 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,21 +1,31 @@
Changelog
=========
-1.2.0 (as yet unreleased)
--------------------------
+1.3.0
+-----
+
+* Drop official support for Django 1.4, Django 1.5, Django 1.6 and
+ Python 2.6 (no changes have been made to deliberately break older
+ versions, but older versions will not be tested going forward);
+* Add official support for Django 1.8 and Django 1.9 (:issue:`62`);
+* Fix for a bug in ``Rule`` creation where the weekday parameter is
+ an instance of ``Weekday`` rather than an integer (:issue:`57`).
+
+1.2.0
+-----
* Added an option for events to occur on the fourth of a given
- weekday of the month (#29);
+ weekday of the month (:issue:`29`);
* Fixed an off-by-one bug in the ``to_text`` method for events
- happening on a regular month each year (#30);
+ happening on a regular month each year (:issue:`30`);
* Fixed a bug in the JavaScript widget where the date for monthly
events on a fixed date of the month had the description rendered
incorrectly if the day selected was more than the number of days in
- the current calendar month (#31);
-* Added a French translation (#32) - this may be backwards
+ the current calendar month (:issue:`31`);
+* Added a French translation (:issue:`32`) - this may be backwards
incompatible if have overriden the widget JavaScript such that
there is no ``language_code`` member of your recurrence object;
-* Added a Spanish translation (#49);
+* Added a Spanish translation (:issue:`49`);
* Added database migrations - running ``python manage.py migrate
recurrence --fake`` should be sufficient for this version - nothing
has changed about the database schema between 1.1.0 and 1.2.0;
@@ -30,11 +40,11 @@ Changelog
* Removed ``RecurrenceModelField`` and ``RecurrenceModelDescriptor``,
which don't appear to have worked as expected for some time.
* Fixed a bug introduced in 1.0.3 which prevented the
- django-recurrence JavaScript from working (#27).
+ django-recurrence JavaScript from working (:issue:`27`).
* Don't raise ``ValueError`` if you save ``None`` into a
- ``RecurrenceField`` with ``null=False`` (#22), for consistency with
- other field types.
-* Make sure an empty recurrence object is falsey (#25).
+ ``RecurrenceField`` with ``null=False`` (:issue:`22`), for
+ consistency with other field types.
+* Make sure an empty recurrence object is falsey (:issue:`25`).
* Fix a copy-paste error in ``to_recurrence_object`` which prevented
exclusion rules from being populated correctly.
* Fix a typo in ``create_from_recurrence_object`` which prevented it
diff --git a/docs/conf.py b/docs/conf.py
index 97cd742..aa58d91 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -28,7 +28,7 @@ if not on_rtd:
# 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('.'))
+sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
@@ -37,7 +37,7 @@ if not on_rtd:
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['github']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -60,9 +60,9 @@ copyright = u'2014, django-recurrence developers'
# built documents.
#
# The short X.Y version.
-version = '1.2.0'
+version = '1.3.0'
# The full version, including alpha/beta/rc tags.
-release = '1.2.0'
+release = '1.3.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -249,3 +249,5 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
+
+github_project_url = 'https://github.com/django-recurrence/django-recurrence/'
diff --git a/docs/github.py b/docs/github.py
new file mode 100644
index 0000000..2f7f153
--- /dev/null
+++ b/docs/github.py
@@ -0,0 +1,71 @@
+from docutils import nodes, utils
+from docutils.parsers.rst.roles import set_classes
+
+
+# With thanks to Doug Hellman for writing
+# https://doughellmann.com/blog/2010/05/09/defining-custom-roles-in-sphinx/
+# - this code is derived from an example BitBucket configuration.
+
+
+def make_issue_node(rawtext, app, slug, options):
+ """Create a link to a GitHub issue.
+
+ :param rawtext: Text being replaced with link node.
+ :param app: Sphinx application context
+ :param slug: ID of the thing to link to
+ :param options: Options dictionary passed to role func.
+ """
+ #
+ try:
+ base = app.config.github_project_url
+ if not base:
+ raise AttributeError
+ except AttributeError, err:
+ raise ValueError('github_project_url configuration value is not set (%s)' % str(err))
+
+ slash = '/' if base[-1] != '/' else ''
+ ref = base + slash + 'issues/' + slug + '/'
+ set_classes(options)
+ node = nodes.reference(rawtext, '#' + utils.unescape(slug), refuri=ref,
+ **options)
+ return node
+
+
+def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ """Link to a GitHub issue.
+
+ Returns 2 part tuple containing list of nodes to insert into the
+ document and a list of system messages. Both are allowed to be
+ empty.
+
+ :param name: The role name used in the document.
+ :param rawtext: The entire markup snippet, with role.
+ :param text: The text marked with the role.
+ :param lineno: The line number where rawtext appears in the input.
+ :param inliner: The inliner instance that called us.
+ :param options: Directive options for customization.
+ :param content: The directive content for customization.
+ """
+ try:
+ issue_num = int(text)
+ if issue_num <= 0:
+ raise ValueError
+ except ValueError:
+ msg = inliner.reporter.error(
+ 'GitHub issue number must be a number greater than or equal to 1; '
+ '"%s" is invalid.' % text, line=lineno)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+ app = inliner.document.settings.env.app
+ node = make_issue_node(rawtext, app, str(issue_num), options)
+ return [node], []
+
+
+def setup(app):
+ """Install the plugin.
+
+ :param app: Sphinx application context.
+ """
+ app.add_role('issue', ghissue_role)
+ app.add_config_value('github_project_url', None, 'env')
+ return
diff --git a/docs/installation.rst b/docs/installation.rst
index ddffb0c..558db34 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -27,28 +27,26 @@ Then, make sure ``recurrence`` is in your ``INSTALLED_APPS`` setting:
Supported Django and Python versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Currently, django-recurrence supports Python 2.6, Python 2.7, Python
-3.3 and Python 3.4. Python 3 support is experimental (we run our
-tests against Python 3, but have not yet tried it in production).
+Currently, django-recurrence supports Python 2.7, Python 3.3, Python
+3.4 and Python 3.5.
-django-recurrence works with Django from versions 1.4 to 1.7 (though
-note that Django 1.4 does not support Python 3, Django 1.7 does not
-support Python 2.6, and Python 3.4 is only supported with Django
-1.7).
+django-recurrence works with Django from versions 1.7 to 1.9.
Set up internationalization
---------------------------
.. note::
- If you just want to use the ``en`` translation, you can skip this
- step.
+ This step is currently mandatory, but may be bypassed with an
+ extra bit of javascript. See [issue
+ #47](https://github.com/django-recurrence/django-recurrence/issues/47)
+ for details.
-If you want to use a translation of django-recurrence other than
-``en``, you'll need to ensure django-recurrence's JavaScript can
+Using a translation of django-recurrence other than
+``en`` requires that django-recurrence's JavaScript can
access the translation strings. This is handled with Django's built
-in ``javascript_catalog`` view, which you install by adding the
-following to your ``urls.py`` file:
+in ``javascript_catalog`` view, which you must install by adding the
+following to your project ``urls.py`` file:
.. code-block:: python
@@ -75,3 +73,6 @@ to ensure you also have ``django.contrib.staticfiles`` in your
``INSTALLED_APPS`` setting, and run::
python manage.py collectstatic
+
+.. note::
+ After collecting static files, you can use {{ form.media }} to include recurrence's static files within your templates.
diff --git a/docs/usage/getting_started.rst b/docs/usage/getting_started.rst
index ff19200..55011dd 100644
--- a/docs/usage/getting_started.rst
+++ b/docs/usage/getting_started.rst
@@ -25,3 +25,31 @@ Using this form it's possible to specify relatively complex
recurrence rules - such as an event that happens every third Thursday
of the month, unless that Thursday happens to be the 21st of the
month, and so on.
+
+
+Form Usage
+----------------------
+
+.. code-block:: python
+
+ from django import forms
+ from .models import Course
+
+ class CourseForm(forms.ModelForm):
+ class Meta:
+ model = Course
+ fields = ('title', 'recurrences',)
+
+.. note::
+
+ Be sure to add {{ form.media }} to your template or statically link recurrence.css and recurrence.js.
+
+.. code-block:: html
+
+
+ <form method="POST" class="post-form">
+ {% csrf_token %}
+ {{ form.media }}
+ {{ form }}
+ <button type="submit">Submit</button>
+ </form>
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..66886cb
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+DJANGO_SETTINGS_MODULE = tests.settings
+addopts = --tb=short
diff --git a/recurrence/base.py b/recurrence/base.py
index 94ca639..6a8ba66 100644
--- a/recurrence/base.py
+++ b/recurrence/base.py
@@ -289,7 +289,7 @@ class Recurrence(object):
"""
def __init__(
self, dtstart=None, dtend=None,
- rrules=[], exrules=[], rdates=[], exdates=[]
+ rrules=(), exrules=(), rdates=(), exdates=()
):
"""
Create a new recurrence.
@@ -842,7 +842,7 @@ def serialize(rule_or_recurrence):
if rule.interval != 1:
values.append((u'INTERVAL', [str(int(rule.interval))]))
if rule.wkst:
- values.append((u'WKST', [Rule.weekdays[rule.wkst]]))
+ values.append((u'WKST', [Rule.weekdays[getattr(rule.wkst, 'number', rule.wkst)]]))
if rule.count is not None:
values.append((u'COUNT', [str(rule.count)]))
diff --git a/recurrence/fields.py b/recurrence/fields.py
index 7f58ebb..783d52b 100644
--- a/recurrence/fields.py
+++ b/recurrence/fields.py
@@ -1,6 +1,6 @@
from django.db.models import fields
-from django.db.models.fields.subclassing import SubfieldBase
-from django.utils.six import string_types, with_metaclass
+from django.utils.six import string_types
+from django.db.models.fields.subclassing import Creator
import recurrence
from recurrence import forms
@@ -14,30 +14,34 @@ except ImportError:
pass
-class RecurrenceField(with_metaclass(SubfieldBase, fields.Field)):
- """
- Field that stores a `recurrence.base.Recurrence` object to the
- database.
- """
+# Do not use SubfieldBase meta class because is removed in Django 1.10
+
+class RecurrenceField(fields.Field):
+ """Field that stores a `recurrence.base.Recurrence` to the database."""
def get_internal_type(self):
return 'TextField'
def to_python(self, value):
- if value is None:
- return value
- if isinstance(value, recurrence.Recurrence):
+ if value is None or isinstance(value, recurrence.Recurrence):
return value
value = super(RecurrenceField, self).to_python(value) or u''
return recurrence.deserialize(value)
- def get_db_prep_value(self, value, connection=None, prepared=False):
- if isinstance(value, string_types):
- value = recurrence.deserialize(value)
- return recurrence.serialize(value)
+ def from_db_value(self, value, *args, **kwargs):
+ return self.to_python(value)
+
+ def get_prep_value(self, value):
+ if not isinstance(value, string_types):
+ value = recurrence.serialize(value)
+ return value
+
+ def contribute_to_class(self, cls, *args, **kwargs):
+ super(RecurrenceField, self).contribute_to_class(cls, *args, **kwargs)
+ setattr(cls, self.name, Creator(self))
def value_to_string(self, obj):
- return self.get_db_prep_value(self._get_val_from_obj(obj))
+ return self.get_prep_value(self._get_val_from_obj(obj))
def formfield(self, **kwargs):
defaults = {
diff --git a/setup.py b/setup.py
index 89afa08..6043929 100644
--- a/setup.py
+++ b/setup.py
@@ -43,7 +43,7 @@ else:
setup(
name='django-recurrence',
- version='1.2.0',
+ version='1.3.0',
license='BSD',
description='Django utility wrapping dateutil.rrule',
@@ -59,11 +59,11 @@ setup(
'Operating System :: OS Independent',
'Programming Language :: Python',
"Programming Language :: Python :: 2",
- 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
),
requires=(
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/models.py b/tests/models.py
new file mode 100644
index 0000000..7bc99b2
--- /dev/null
+++ b/tests/models.py
@@ -0,0 +1,10 @@
+from django.db import models
+from recurrence.fields import RecurrenceField
+
+
+class EventWithNoNulls(models.Model):
+ recurs = RecurrenceField(null=False)
+
+
+class EventWithNulls(models.Model):
+ recurs = RecurrenceField(null=True)
diff --git a/tests/settings.py b/tests/settings.py
new file mode 100644
index 0000000..5d966e5
--- /dev/null
+++ b/tests/settings.py
@@ -0,0 +1,55 @@
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': 'test_db.sqlite'
+ }
+}
+
+MIDDLEWARE_CLASSES = (
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+ 'django.contrib.auth.context_processors.auth',
+ 'django.core.context_processors.debug',
+ 'django.core.context_processors.i18n',
+ 'django.core.context_processors.media',
+ 'django.core.context_processors.request',
+ 'django.core.context_processors.static',
+ 'django.core.context_processors.tz',
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'recurrence',
+ 'tests',
+)
+
+# ROOT_URLCONF = 'tests.urls'
+
+PASSWORD_HASHERS = (
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher',
+)
+
+STATIC_URL = '/fakestatictrees/'
+MEDIA_ROOT = 'tests/testmedia/'
+
+TIME_ZONE = 'Europe/London'
+LANGUAGE_CODE = 'en-us'
+SITE_ID = 1
+USE_I18N = True
+USE_L10N = True
+USE_TZ = True
+SECRET_KEY = 'thisbagismadefromrecycledmaterial'
+TEMPLATE_DEBUG = True
diff --git a/tests/test_exclusions.py b/tests/test_exclusions.py
new file mode 100644
index 0000000..dd41ad1
--- /dev/null
+++ b/tests/test_exclusions.py
@@ -0,0 +1,83 @@
+from datetime import datetime
+from recurrence import Recurrence, Rule
+import recurrence
+
+
+def test_exclusion_date():
+ rule = Rule(
+ recurrence.DAILY
+ )
+
+ pattern = Recurrence(
+ dtstart=datetime(2014, 1, 2, 0, 0, 0),
+ dtend=datetime(2014, 1, 4, 0, 0, 0),
+ rrules=[rule],
+ exdates=[
+ datetime(2014, 1, 3, 0, 0, 0)
+ ]
+ )
+
+ occurrences = [
+ instance for instance in
+ pattern.occurrences()
+ ]
+
+ assert occurrences == [
+ datetime(2014, 1, 2, 0, 0, 0),
+ datetime(2014, 1, 4, 0, 0, 0),
+ ]
+
+ assert 2 == pattern.count()
+
+
+def test_exclusion_date_no_limits():
+ pattern = Recurrence(
+ rdates=[
+ datetime(2014, 1, 1, 0, 0, 0),
+ datetime(2014, 1, 2, 0, 0, 0),
+ ],
+ exdates=[
+ datetime(2014, 1, 2, 0, 0, 0)
+ ]
+ )
+
+ occurrences = [
+ instance for instance in
+ pattern.occurrences()
+ ]
+
+ assert occurrences == [
+ datetime(2014, 1, 1, 0, 0, 0),
+ ]
+
+ assert 1 == pattern.count()
+
+
+def test_exclusion_rule():
+ inclusion_rule = Rule(
+ recurrence.DAILY
+ )
+
+ exclusion_rule = Rule(
+ recurrence.WEEKLY,
+ byday=recurrence.THURSDAY
+ )
+
+ pattern = Recurrence(
+ dtstart=datetime(2014, 1, 2, 0, 0, 0),
+ dtend=datetime(2014, 1, 4, 0, 0, 0),
+ rrules=[inclusion_rule],
+ exrules=[exclusion_rule]
+ )
+
+ occurrences = [
+ instance for instance in
+ pattern.occurrences()
+ ]
+
+ assert occurrences == [
+ datetime(2014, 1, 3, 0, 0, 0),
+ datetime(2014, 1, 4, 0, 0, 0),
+ ]
+
+ assert 2 == pattern.count()
diff --git a/tests/test_fields.py b/tests/test_fields.py
new file mode 100644
index 0000000..33d59ae
--- /dev/null
+++ b/tests/test_fields.py
@@ -0,0 +1,159 @@
+from datetime import datetime
+from django import forms
+from recurrence import Recurrence, Rule
+from recurrence.forms import RecurrenceField
+import pytest
+import recurrence
+
+
+def test_clean_normal_value():
+ field = RecurrenceField()
+ value = "RRULE:FREQ=WEEKLY;BYDAY=TU"
+
+ obj = field.clean(value)
+
+ assert len(obj.rrules) == 1
+ assert obj.rrules[0].to_text() == "weekly, each Tuesday"
+
+
+def test_clean_invalid_value():
+ field = RecurrenceField()
+ value = "RRULE:FREQS=WEEKLY"
+
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == "bad parameter: FREQS"
+
+
+def test_strip_dtstart_and_dtend_if_required():
+ rule = Rule(
+ recurrence.WEEKLY
+ )
+
+ limits = Recurrence(
+ dtstart=datetime(2014, 1, 1, 0, 0, 0),
+ dtend=datetime(2014, 2, 3, 0, 0, 0),
+ rrules=[rule]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField()
+ cleaned_value = field.clean(value)
+ assert cleaned_value == limits
+ assert cleaned_value.dtstart == datetime(2014, 1, 1, 0, 0, 0)
+ assert cleaned_value.dtend == datetime(2014, 2, 3, 0, 0, 0)
+
+ field = RecurrenceField(accept_dtstart=False, accept_dtend=False)
+ cleaned_value = field.clean(value)
+ assert cleaned_value != limits
+ assert cleaned_value.dtstart is None
+ assert cleaned_value.dtend is None
+
+
+def test_check_max_rrules():
+ rule = Rule(
+ recurrence.WEEKLY
+ )
+
+ limits = Recurrence(
+ rrules=[rule]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField(max_rrules=0)
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == "Max rules exceeded. The limit is 0"
+
+
+def test_check_max_exrules():
+ rule = Rule(
+ recurrence.WEEKLY
+ )
+
+ limits = Recurrence(
+ exrules=[rule]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField(max_exrules=0)
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == ("Max exclusion rules exceeded. "
+ "The limit is 0")
+
+
+def test_check_max_rdates():
+ limits = Recurrence(
+ rdates=[
+ datetime(2014, 1, 1, 0, 0, 0),
+ datetime(2014, 1, 2, 0, 0, 0),
+ ]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField(max_rdates=2)
+ field.clean(value)
+
+ field = RecurrenceField(max_rdates=1)
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == "Max dates exceeded. The limit is 1"
+
+
+def test_check_max_exdates():
+ limits = Recurrence(
+ exdates=[
+ datetime(2014, 1, 1, 0, 0, 0),
+ datetime(2014, 1, 2, 0, 0, 0),
+ ]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField(max_exdates=2)
+ field.clean(value)
+
+ field = RecurrenceField(max_exdates=1)
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == ("Max exclusion dates exceeded. "
+ "The limit is 1")
+
+
+def test_check_allowable_frequencies():
+ rule = Rule(
+ recurrence.WEEKLY
+ )
+
+ limits = Recurrence(
+ rrules=[rule]
+ )
+
+ value = recurrence.serialize(limits)
+
+ field = RecurrenceField(frequencies=[
+ recurrence.WEEKLY
+ ])
+ field.clean(value)
+
+ field = RecurrenceField(frequencies=[
+ recurrence.YEARLY
+ ])
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == "Invalid frequency."
+
+ limits = Recurrence(
+ exrules=[rule]
+ )
+
+ value = recurrence.serialize(limits)
+
+ with pytest.raises(forms.ValidationError) as e:
+ field.clean(value)
+ assert e.value.messages[0] == "Invalid frequency."
diff --git a/tests/test_magic_methods.py b/tests/test_magic_methods.py
new file mode 100644
index 0000000..f76811a
--- /dev/null
+++ b/tests/test_magic_methods.py
@@ -0,0 +1,63 @@
+from datetime import datetime
+from recurrence import Recurrence, Rule
+import recurrence
+
+
+def test_truthiness_with_single_rrule():
+ rule = Rule(
+ recurrence.DAILY
+ )
+
+ object = Recurrence(
+ rrules=[rule]
+ )
+
+ assert bool(object)
+
+
+def test_truthiness_with_single_exrule():
+ rule = Rule(
+ recurrence.DAILY
+ )
+
+ object = Recurrence(
+ exrules=[rule]
+ )
+
+ assert bool(object)
+
+
+def test_truthiness_with_single_rdate():
+ object = Recurrence(
+ rdates=[datetime(2014, 12, 31, 0, 0, 0)]
+ )
+
+ assert bool(object)
+
+
+def test_truthiness_with_single_exdate():
+ object = Recurrence(
+ exdates=[datetime(2014, 12, 31, 0, 0, 0)]
+ )
+
+ assert bool(object)
+
+
+def test_truthiness_with_dtstart():
+ object = Recurrence(
+ dtstart=datetime(2014, 12, 31, 0, 0, 0)
+ )
+
+ assert bool(object)
+
+
+def test_truthiness_with_dtend():
+ object = Recurrence(
+ dtend=datetime(2014, 12, 31, 0, 0, 0)
+ )
+
+ assert bool(object)
+
+
+def test_falsiness_with_empty_recurrence_object():
+ assert not bool(Recurrence())
diff --git a/tests/test_managers_recurrence.py b/tests/test_managers_recurrence.py
new file mode 100644
index 0000000..8854079
--- /dev/null
+++ b/tests/test_managers_recurrence.py
@@ -0,0 +1,136 @@
+from datetime import datetime
+from django.utils.timezone import make_aware
+from recurrence import choices
+from recurrence.models import Date, Recurrence, Rule
... 1041 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-recurrence.git
More information about the Python-modules-commits
mailing list