Skip to content

Commit

Permalink
Merge pull request #57 from ambitioninc/develop
Browse files Browse the repository at this point in the history
4.1.0
  • Loading branch information
somewes authored Jun 27, 2023
2 parents 5f9242e + 445a3d0 commit ddcceaa
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 92 deletions.
77 changes: 77 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# copied from django-cte
name: localized_recurrence tests
on:
push:
branches: [master]
pull_request:
branches: [master,develop]

jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ['3.7', '3.8', '3.9']
# Time to switch to pytest or nose2?
# nosetests is broken on 3.10
# AttributeError: module 'collections' has no attribute 'Callable'
# https://github.com/nose-devs/nose/issues/1099
django:
- 'Django~=3.2.0'
- 'Django~=4.0.0'
- 'Django~=4.1.0'
- 'Django~=4.2.0'
experimental: [false]
exclude:
- python: '3.7'
django: 'Django~=4.0.0'
- python: '3.7'
django: 'Django~=4.1.0'
- python: '3.7'
django: 'Django~=4.2.0'

services:
postgres:
image: postgres:latest
env:
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python }}
- name: Setup
run: |
python --version
pip install --upgrade pip wheel setuptools
pip install -r requirements/requirements.txt
pip install -r requirements/requirements-testing.txt
pip install "${{ matrix.django }}"
pip freeze
- name: Run tests
env:
DB_SETTINGS: >-
{
"ENGINE":"django.db.backends.postgresql",
"NAME":"localized_recurrence",
"USER":"postgres",
"PASSWORD":"postgres",
"HOST":"localhost",
"PORT":"5432"
}
run: |
coverage run manage.py test localized_recurrence
coverage report --fail-under=100
continue-on-error: ${{ matrix.experimental }}
- name: Check style
run: flake8 localized_recurrence
33 changes: 4 additions & 29 deletions localized_recurrence/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,10 @@
import re

from django.db.models.fields import IntegerField
import six
from ambition_utils.fields import CastOnAssignFieldMixin


class CastOnAssignDescriptor(object):
"""
A property descriptor which ensures that `field.to_python()` is called on _every_ assignment to the field.
This used to be provided by the `django.db.models.subclassing.Creator` class, which in turn
was used by the deprecated-in-Django-1.10 `SubfieldBase` class, hence the reimplementation here.
https://stackoverflow.com/questions/39392343/
how-do-i-make-a-custom-model-field-call-to-python-when-the-field-is-accessed-imm
"""

def __init__(self, field):
self.field = field

def __get__(self, obj, type=None): # pragma: no cover
if obj is None:
return self
return obj.__dict__[self.field.name]

def __set__(self, obj, value): # pragma: no cover
obj.__dict__[self.field.name] = self.field.to_python(value)


class DurationField(IntegerField):
class DurationField(CastOnAssignFieldMixin, IntegerField):
"""A field to store durations of time with accuracy to the second.
A Duration Field will automatically convert between python
Expand Down Expand Up @@ -60,11 +39,7 @@ class Presentations(models.Model):

def __init__(self, *args, **kwargs):
"""Call out to the super. Makes docs cleaner."""
return super(DurationField, self).__init__(*args, **kwargs)

def contribute_to_class(self, cls, name):
super(DurationField, self).contribute_to_class(cls, name)
setattr(cls, name, CastOnAssignDescriptor(self))
return super().__init__(*args, **kwargs)

def to_python(self, value):
"""Convert a stored duration into a python datetime.timedelta object.
Expand All @@ -76,7 +51,7 @@ def to_python(self, value):
"""
if isinstance(value, timedelta):
v = value
elif isinstance(value, (six.binary_type, six.text_type)):
elif isinstance(value, (bytes, str)):
# The string should be in the form "[D day[s],][H]H:MM:SS[.UUUUUU]"
try:
v = parse_timedelta_string(value)
Expand Down
4 changes: 2 additions & 2 deletions localized_recurrence/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import unicode_literals

from django.db import models, migrations
import timezone_field.fields
import ambition_utils.fields
import localized_recurrence.fields
import datetime

Expand All @@ -19,7 +19,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
('interval', models.CharField(default='DAY', choices=[('DAY', 'Day'), ('WEEK', 'Week'), ('MONTH', 'Month'), ('QUARTER', 'Quarter'), ('YEAR', 'Year')], max_length=18)),
('offset', localized_recurrence.fields.DurationField(default=0)),
('timezone', timezone_field.fields.TimeZoneField(default='UTC')),
('timezone', ambition_utils.fields.TimeZoneField(default='UTC')),
('previous_scheduled', models.DateTimeField(default=datetime.datetime(1970, 1, 1, 0, 0))),
('next_scheduled', models.DateTimeField(default=datetime.datetime(1970, 1, 1, 0, 0))),
],
Expand Down
2 changes: 1 addition & 1 deletion localized_recurrence/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from dateutil.relativedelta import relativedelta
from django.db import models
from timezone_field import TimeZoneField
from ambition_utils.fields import TimeZoneField
import fleming
import pytz

Expand Down
9 changes: 3 additions & 6 deletions localized_recurrence/tests/fields_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
from datetime import timedelta

import django
import six
if six.PY3: # pragma: no cover
from importlib import reload
from importlib import reload
from unittest.mock import MagicMock, patch

from mock import MagicMock, patch

from .. import fields
from localized_recurrence import fields


class DurationFieldToPythonTest(unittest.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion localized_recurrence/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '4.0.0'
__version__ = '4.1.0'
2 changes: 1 addition & 1 deletion requirements/requirements-testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ coverage
coveralls
django-nose
django-dynamic-fixture
mock
flake8
psycopg2
5 changes: 5 additions & 0 deletions requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Django>=3.2
ambition-utils>=3.1.1
fleming>=0.6.0
python-dateutil
pytz
25 changes: 20 additions & 5 deletions settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import json

from django.conf import settings

Expand All @@ -9,22 +10,33 @@ def configure_settings():
"""
if not settings.configured:
# Determine the database settings depending on if a test_db var is set in CI mode or not
test_db = os.environ.get('DB', 'sqlite')
if test_db == 'sqlite':
test_db = os.environ.get('DB', None)
if test_db is None:
db_config = {
'ENGINE': 'django.db.backends.sqlite3'
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'localized_recurrence',
'USER': 'postgres',
'PASSWORD': '',
'HOST': 'db',
}
elif test_db == 'postgres':
db_config = {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres',
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'localized_recurrence',
'USER': 'postgres',
'PASSWORD': '',
'HOST': 'db',
}
else:
raise RuntimeError('Unsupported test DB {0}'.format(test_db))

# Check env for db override (used for github actions)
if os.environ.get('DB_SETTINGS'):
db_config = json.loads(os.environ.get('DB_SETTINGS'))

settings.configure(
TEST_RUNNER='django_nose.NoseTestSuiteRunner',
SECRET_KEY='*',
NOSE_ARGS=['--nocapture', '--nologcapture', '--verbosity=1'],
MIDDLEWARE_CLASSES=(),
DATABASES={
Expand All @@ -39,4 +51,7 @@ def configure_settings():
'localized_recurrence.tests',
),
DEBUG=False,
TIME_ZONE='UTC',
USE_TZ=False,
USE_DEPRECATED_PYTZ=True,
)
31 changes: 15 additions & 16 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ def get_version():
raise RuntimeError('Unable to find version string in {0}.'.format(VERSION_FILE))


def get_lines(file_path):
return open(file_path, 'r').read().split('\n')


install_requires = get_lines('requirements/requirements.txt')
tests_require = get_lines('requirements/requirements-testing.txt')


setup(
name='django-localized-recurrence',
version=get_version(),
Expand All @@ -31,30 +39,21 @@ def get_version():
classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.0',
'Framework :: Django :: 3.1',
'Framework :: Django :: 3.2',
'Framework :: Django :: 4.0',
'Framework :: Django :: 4.1',
'Framework :: Django :: 4.2',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
install_requires=[
'Django>=2.2',
'ambition-django-timezone-field>=2.0.4',
'fleming>=0.6.0',
'python-dateutil',
'pytz',
],
tests_require=[
'django-nose>=1.4',
'django-dynamic-fixture',
'mock',
],
install_requires=install_requires,
tests_require=tests_require,
test_suite='run_tests.run_tests'
)
31 changes: 0 additions & 31 deletions tox.ini

This file was deleted.

0 comments on commit ddcceaa

Please sign in to comment.