diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 3c550b5..21bb94f 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -11,10 +11,14 @@ jobs: strategy: matrix: python-version: [3.6, 3.7, 3.8] - django-version: ['<2', '<3', '>=3'] + django-version: ['<3', '>=3'] steps: - uses: actions/checkout@v2 + - name: Install GDAL & spatialite + run: | + sudo add-apt-repository ppa:ubuntugis/ppa && sudo apt-get update + sudo apt-get install libgdal-dev libsqlite3-mod-spatialite - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -30,8 +34,15 @@ jobs: ${{ runner.os }}- - name: Install dependencies run: | - echo "Django version set to '${{ matrix.django-version }}' in requirements.txt" + export DRF_VERSION=3.12.4 + + if [ '${{ matrix.django-version }}' == '<2' ]; then + DRF_VERSION=3.11.2 + fi + + echo "Setting versions Django -> ${{ matrix.django-version }} DRF -> $DRF_VERSION in requirements.txt" sed -i 's/Django==.*/Django${{ matrix.django-version }}/' requirements.txt + sed -i "s/djangorestframework==.*/djangorestframework==$DRF_VERSION/" requirements.txt pip install -r requirements.txt - name: Lint with flake8 run: | diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a92ab5a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.9.2 + +# Spatial packages and such +RUN apt-get update && apt-get install -y libgdal-dev libsqlite3-mod-spatialite + +# Python reqs +ADD requirements.txt . +RUN pip install -r requirements.txt + +# add all of our source + config +ADD ckc/ /src/ckc/ +ADD testproject/ /src/testproject +ADD tests/ /src/tests +ADD setup.cfg /src +WORKDIR /src diff --git a/README.md b/README.md index a9a39f3..759acf6 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,7 @@ $ twine upload dist/* ## tests ```bash -# get into a virtual env of some kind -$ pip install -r requirements.txt -$ pytest +$ docker build -t django-ckc . && docker run django-ckc pytest ``` ## what's in this @@ -60,6 +58,29 @@ class MySerializer(DefaultUserCreateMixin, ModelSerializer): model = YourModel ``` +#### `DjangoGeoPointProvider` + +Helps generate geo points in Factory Boy factories. + +```py +# factories.py +class SomeLocationFactory(DjangoModelFactory): + location = factory.Faker('geo_point', country_code='US') + + class Meta: + model = SomeLocation + +# test_whatever.py +from django.contrib.gis.geos import Point + + +class WhateverTest(TestCase): + def test_something(self): + SomeLocationFactory() # random location + SomeLocationFactory(location=Point(x=60, y=60)) # specified location +``` + + #### `./manage.py` commands | command | description| diff --git a/ckc/__init__.py b/ckc/__init__.py index e69de29..feca3ec 100644 --- a/ckc/__init__.py +++ b/ckc/__init__.py @@ -0,0 +1,2 @@ +# We import here to run the faker factory add_provider stuff... +from . import providers # noqa: F401 diff --git a/ckc/providers.py b/ckc/providers.py new file mode 100644 index 0000000..a1d274e --- /dev/null +++ b/ckc/providers.py @@ -0,0 +1,28 @@ +# Doing a try/except here so we don't force end users of the +# django-ckc module to install factory boy. +try: + import factory + from django.contrib.gis.geos import Point + from faker.providers import BaseProvider + + class DjangoGeoPointProvider(BaseProvider): + """Custom helper class giving us the 'geo_point' provider, example: + + location = factory.Faker('geo_point', country_code='US') + + (note, you must call factory.Faker.add_provider(DjangoGeoPointProvider) to add + this provider!) + """ + + def geo_point(self, **kwargs): + kwargs['coords_only'] = True + faker = factory.faker.faker.Faker() + + # local_latlng returns something like: + # ('40.72371', '-73.95097', 'Greenpoint', 'US', 'America/New_York') + coords = faker.local_latlng(**kwargs) + return Point(x=float(coords[1]), y=float(coords[0]), srid=4326) + + factory.Faker.add_provider(DjangoGeoPointProvider) +except ImportError: + pass diff --git a/requirements.txt b/requirements.txt index ffd5be4..0852b72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,14 @@ +# These requirements are for local development and testing of the module + # python packaging twine==3.1.1 # django stuff Django==3.1.8 -djangorestframework==3.11.2 +djangorestframework==3.12.4 + +# factories +factory-boy==3.2.0 # tests pytest==5.4.1 diff --git a/setup.cfg b/setup.cfg index 3d6029f..15f1f69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,7 @@ name = django-ckc author = Eric Carmichael author_email = eric@ckcollab.com description = tools, utilities, etc. we use across projects @ ckc -version = 0.0.3 +version = 0.0.4 url = https://github.com/ckcollab/django-ckc keywords = django diff --git a/testproject/settings.py b/testproject/settings.py index 6e27f59..fbcf852 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -5,9 +5,10 @@ BASE_DIR = os.path.dirname(__file__) +# NOTE: We're using Geospatial sqlite jazz DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", + "ENGINE": "django.contrib.gis.db.backends.spatialite", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -32,6 +33,7 @@ "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.staticfiles", + "django.contrib.gis", "ckc", diff --git a/testproject/testapp/factories.py b/testproject/testapp/factories.py new file mode 100644 index 0000000..ba3f335 --- /dev/null +++ b/testproject/testapp/factories.py @@ -0,0 +1,12 @@ +import factory + +from factory.django import DjangoModelFactory + +from testapp.models import Location + + +class LocationFactory(DjangoModelFactory): + geo_point = factory.Faker('geo_point', country_code='US') + + class Meta: + model = Location diff --git a/testproject/testapp/models.py b/testproject/testapp/models.py index 45d39f8..7837cca 100644 --- a/testproject/testapp/models.py +++ b/testproject/testapp/models.py @@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.contrib.gis.db.models import PointField from django.db import models from ckc.models import SoftDeletableModel @@ -17,3 +18,7 @@ class ModelWithACreator(models.Model): class ModelWithADifferentNamedCreator(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) + + +class Location(models.Model): + geo_point = PointField() diff --git a/tests/integration/test_factories.py b/tests/integration/test_factories.py new file mode 100644 index 0000000..026a706 --- /dev/null +++ b/tests/integration/test_factories.py @@ -0,0 +1,16 @@ +from django.test import TestCase + +from django.contrib.gis.geos import Point + +from testapp.factories import LocationFactory + + +class TestFactories(TestCase): + def test_location_factory_point_works(self): + location = LocationFactory() + assert location.geo_point.x + assert location.geo_point.y + + location = LocationFactory(geo_point=Point(x=50, y=50)) + assert location.geo_point.x == 50 + assert location.geo_point.y == 50