diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c0a213a --- /dev/null +++ b/.drone.yml @@ -0,0 +1,33 @@ +--- +kind: pipeline +type: docker +name: default + +steps: + - name: docker + image: plugins/docker + environment: + DJANGO_ENV: production + settings: + repo: docker.data.coop/member.data.coop + registry: docker.data.coop + username: + from_secret: DOCKER_USERNAME + password: + from_secret: DOCKER_PASSWORD + tags: + - "${DRONE_BUILD_NUMBER}" + - "vidir_refactor" + when: + branch: + - vidir_refactor + +# - name: notify +# image: plugins/matrix +# settings: +# homeserver: https://data.coop +# roomid: plKSghHbepWeUEtbHE:data.coop +# username: +# from_secret: matrix_username +# password: +# from_secret: matrix_password diff --git a/.gitignore b/.gitignore index bf52361..cb12fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ __pycache__/ *.pyc *.sw* db.sqlite3 -project/settings/local.py .pytest_cache +.idea/ +*.mo diff --git a/docker/Dockerfile b/Dockerfile similarity index 79% rename from docker/Dockerfile rename to Dockerfile index 6cd196e..962a34e 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ RUN apt-get update \ libgdk-pixbuf2.0-0 \ libffi-dev \ shared-mime-info \ + gettext \ && pip install "poetry==$POETRY_VERSION" WORKDIR /app @@ -34,10 +35,13 @@ RUN poetry export -f requirements.txt $(test "$DJANGO_ENV" != production && echo RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www COPY --chown=www:www ./ /app/ -RUN mkdir /app/static && chown www:www /app/static +RUN mkdir /app/src/static && chown www:www /app/src/static ARG BUILD ENV BUILD ${BUILD} ENTRYPOINT ["./entrypoint.sh"] -CMD ["uvicorn", "config.asgi:application", "--host", "0.0.0.0", "--port", "8080", "--workers", "3", "--lifespan", "off"] + +EXPOSE 8000 + +CMD ["uvicorn", "project.asgi:application", "--host", "0.0.0.0", "--port", "8000", "--workers", "3", "--lifespan", "off", "--app-dir", "/app/src"] diff --git a/Makefile b/Makefile index 3f22263..73cea65 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,39 @@ -# These are just some make targets, expressing how things -# are supposed to be run, but feel free to change them! - -dev-setup: - poetry run pre-commit install - poetry run python manage.py migrate - poetry run python manage.py createsuperuser +DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose +DOCKER_RUN = ${DOCKER_COMPOSE} run -e UID=`id -u` -e GID=`id -g` +MANAGE = ${DOCKER_RUN} backend python /app/src/manage.py lint: poetry run pre-commit run --all +run: + ${DOCKER_COMPOSE} up --build + +build: + ${DOCKER_COMPOSE} build + +makemessages: + ${MANAGE} makemessages -a + +makemigrations: + ${MANAGE} makemigrations ${EXTRA_ARGS} + +migrate: + ${MANAGE} migrate ${EXTRA_ARGS} + +createsuperuser: + ${MANAGE} createsuperuser + +shell: + ${MANAGE} shell + +manage_command: + ${MANAGE} ${COMMAND} + test: - poetry run pytest + ${DOCKER_RUN} backend pytest src/ + +add_dependency: + ${DOCKER_RUN} backend poetry add --lock ${DEPENDENCY} + +add_dev_dependency: + ${DOCKER_RUN} backend poetry add -D --lock ${DEPENDENCY} diff --git a/README.md b/README.md index a93d8f2..5112376 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,35 @@ # member.data.coop -To start developing: +## Development requirements -Get poetry +- Docker +- Docker compose +- pre-commit (preferred for contributions) - $ python3 -m pip install --user pipx - $ pipx install poetry +## Start local server -Run poetry to setup environment +Given that the requirements above are installed, it should be as easy as: - $ poetry install + $ make migrate -Run this make target, which installs all the requirements and sets up a development database. +This will setup the database. Next run: - $ make dev-setup + $ make run -To run the Django development server: +This will build the docker image and start the member system on http://localhost:8000. - $ poetry run python manage.py runserver +You can create a superuser by running: -Before you push your stuff, run tests: + $ make createsuperuser + +Make migrations: + + $ make makemigrations + +Make messages: + + $ make makemessages + +Running tests: $ make test diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c3156cf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3.7' + +services: + + backend: + build: + context: . + dockerfile: Dockerfile + user: $UID:$GID + command: python /app/src/manage.py runserver 0.0.0.0:8000 + tty: true + ports: + - "8000:8000" + volumes: + - ./:/app/ + links: + - redis + - postgres + env_file: + - env + + postgres: + image: postgres:13-alpine + volumes: + - postgres_data:/var/lib/postgresql/data/ + ports: + - 5432:5432 + env_file: + - env + + redis: + image: redis:latest + ports: + - "6379:6379" + +volumes: + postgres_data: diff --git a/entrypoint.sh b/entrypoint.sh index cdce425..9df57b6 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,21 +1,21 @@ #!/bin/sh - + echo "Waiting for postgres..." - + POSTGRES_PORT=${POSTGRES_PORT:-5432} POSTGRES_HOST=${POSTGRES_HOST:-localhost} while ! nc -z "$POSTGRES_HOST" "$POSTGRES_PORT"; do sleep 0.1 done - -echo "PostgreSQL started" - -# Only migrate and collectstatic if we are NOT in development -if [ -z "$DEBUG" ]; then - python manage.py migrate - python manage.py collectstatic --no-input; -fi - -exec "$@" +echo "PostgreSQL started" + +# Only migrate, collectstatic and compilemessages if we are NOT in development +if [ -z "$DEBUG" ]; then + python src/manage.py migrate; + python src/manage.py collectstatic --no-input; + python src/manage.py compilemessages; +fi + +exec "$@" diff --git a/env b/env new file mode 100644 index 0000000..19c8a09 --- /dev/null +++ b/env @@ -0,0 +1,7 @@ +SECRET_KEY=something-very-random +POSTGRES_HOST=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_PORT=5432 +DATABASE_URL=postgres://postgres:postgres@postgres:5432/postgres +DEBUG=True +DJANGO_ENV=all diff --git a/membership/admin.py b/membership/admin.py deleted file mode 100644 index 90e644e..0000000 --- a/membership/admin.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.contrib import admin - -from . import models - - -@admin.register(models.Membership) -class MembershipAdmin(admin.ModelAdmin): - pass diff --git a/membership/migrations/0001_initial.py b/membership/migrations/0001_initial.py deleted file mode 100644 index 2a32dcf..0000000 --- a/membership/migrations/0001_initial.py +++ /dev/null @@ -1,227 +0,0 @@ -# Generated by Django 2.0.6 on 2018-06-23 19:07 -from decimal import Decimal - -import django.db.models.deletion -import djmoney.models.fields -from django.conf import settings -from django.db import migrations -from django.db import models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] - - operations = [ - migrations.CreateModel( - name="Membership", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "modified", - models.DateTimeField(auto_now=True, verbose_name="modified"), - ), - ( - "created", - models.DateTimeField(auto_now_add=True, verbose_name="created"), - ), - ( - "can_vote", - models.BooleanField( - default=False, - help_text="Indicates that the user has a democratic membership of the organization.", - verbose_name="can vote", - ), - ), - ], - options={ - "verbose_name": "membership", - "verbose_name_plural": "memberships", - }, - ), - migrations.CreateModel( - name="Organization", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "modified", - models.DateTimeField(auto_now=True, verbose_name="modified"), - ), - ( - "created", - models.DateTimeField(auto_now_add=True, verbose_name="created"), - ), - ("name", models.CharField(max_length=64, verbose_name="name")), - ], - options={ - "verbose_name": "organization", - "verbose_name_plural": "organizations", - }, - ), - migrations.CreateModel( - name="Subscription", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "modified", - models.DateTimeField(auto_now=True, verbose_name="modified"), - ), - ( - "created", - models.DateTimeField(auto_now_add=True, verbose_name="created"), - ), - ( - "active", - models.BooleanField( - default=False, - help_text="Automatically set by payment system.", - verbose_name="active", - ), - ), - ("starts", models.DateField()), - ("ends", models.DateField()), - ( - "renewed_subscription", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - to="membership.Subscription", - verbose_name="renewed subscription", - ), - ), - ], - options={ - "verbose_name": "subscription", - "verbose_name_plural": "subscriptions", - }, - ), - migrations.CreateModel( - name="SubscriptionType", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "modified", - models.DateTimeField(auto_now=True, verbose_name="modified"), - ), - ( - "created", - models.DateTimeField(auto_now_add=True, verbose_name="created"), - ), - ("name", models.CharField(max_length=64, verbose_name="name")), - ( - "fee_currency", - djmoney.models.fields.CurrencyField( - choices=[("DKK", "DKK")], - default="XYZ", - editable=False, - max_length=3, - ), - ), - ( - "fee", - djmoney.models.fields.MoneyField( - decimal_places=2, default=Decimal("0.0"), max_digits=16 - ), - ), - ( - "fee_vat_currency", - djmoney.models.fields.CurrencyField( - choices=[("DKK", "DKK")], - default="XYZ", - editable=False, - max_length=3, - ), - ), - ( - "fee_vat", - djmoney.models.fields.MoneyField( - decimal_places=2, default=Decimal("0"), max_digits=16 - ), - ), - ( - "duration", - models.PositiveSmallIntegerField( - choices=[(1, "annual")], default=1, verbose_name="duration" - ), - ), - ( - "organization", - models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, - to="membership.Organization", - ), - ), - ], - options={ - "verbose_name": "subscription type", - "verbose_name_plural": "subscription types", - }, - ), - migrations.AddField( - model_name="subscription", - name="subscription_type", - field=models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, - related_name="memberships", - to="membership.SubscriptionType", - verbose_name="subscription type", - ), - ), - migrations.AddField( - model_name="subscription", - name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL - ), - ), - migrations.AddField( - model_name="membership", - name="organization", - field=models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, - to="membership.Organization", - ), - ), - migrations.AddField( - model_name="membership", - name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL - ), - ), - ] diff --git a/membership/models.py b/membership/models.py deleted file mode 100644 index ec70410..0000000 --- a/membership/models.py +++ /dev/null @@ -1,123 +0,0 @@ -from django.contrib.auth import get_user_model -from django.db import models -from django.utils.translation import gettext as _ -from djmoney.models.fields import MoneyField - - -class CreatedModifiedAbstract(models.Model): - - modified = models.DateTimeField(auto_now=True, verbose_name=_("modified")) - created = models.DateTimeField(auto_now_add=True, verbose_name=_("created")) - - class Meta: - abstract = True - - -class Organization(CreatedModifiedAbstract): - """ - This holds the data of the organization that someone is a member of. It is - possible that we'll create more advanced features here. - """ - - name = models.CharField(verbose_name=_("name"), max_length=64) - - def __str__(self): - return self.name - - class Meta: - verbose_name = _("organization") - verbose_name_plural = _("organizations") - - -class Membership(CreatedModifiedAbstract): - """ - A user remains a member of an organization even though the subscription is - unpaid or renewed. This just changes the status/permissions etc. of the - membership, thus we need to track subscription creation, expiry, renewals - etc. and ensure that the membership is modified accordingly. - - This expresses some - """ - - organization = models.ForeignKey(Organization, on_delete=models.PROTECT) - user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT) - - can_vote = models.BooleanField( - default=False, - verbose_name=_("can vote"), - help_text=_( - "Indicates that the user has a democratic membership of the " - "organization." - ), - ) - - def __str__(self): - - return _("{} is a member of {}").format( - self.user.get_full_name(), self.organization.name - ) - - class Meta: - verbose_name = _("membership") - verbose_name_plural = _("memberships") - - -class SubscriptionType(CreatedModifiedAbstract): - """ - Properties of subscriptions are stored here. Should of course not be edited - after subscriptions are created. - """ - - organization = models.ForeignKey(Organization, on_delete=models.PROTECT) - - name = models.CharField(verbose_name=_("name"), max_length=64) - - fee = MoneyField(max_digits=16, decimal_places=2) - - fee_vat = MoneyField(max_digits=16, decimal_places=2, default=0) - - duration = models.PositiveSmallIntegerField( - default=1, choices=[(1, _("annual"))], verbose_name=_("duration") - ) - - class Meta: - verbose_name = _("subscription type") - verbose_name_plural = _("subscription types") - - -class Subscription(CreatedModifiedAbstract): - """ - To not confuse other types of subscriptions, one can be a *subscribed* - member of an organization, meaning that they are paying etc. - - A subscription does not track payment, this is done in the accounting app. - """ - - subscription_type = models.ForeignKey( - SubscriptionType, - related_name="memberships", - verbose_name=_("subscription type"), - on_delete=models.PROTECT, - ) - user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT) - - active = models.BooleanField( - default=False, - verbose_name=_("active"), - help_text=_("Automatically set by payment system."), - ) - - starts = models.DateField() - ends = models.DateField() - - renewed_subscription = models.ForeignKey( - "self", - null=True, - blank=True, - verbose_name=_("renewed subscription"), - on_delete=models.PROTECT, - ) - - class Meta: - verbose_name = _("subscription") - verbose_name_plural = _("subscriptions") diff --git a/poetry.lock b/poetry.lock index 931e969..96ee153 100644 --- a/poetry.lock +++ b/poetry.lock @@ -47,6 +47,17 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "cffi" +version = "1.14.5" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "cfgv" version = "3.2.0" @@ -63,6 +74,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "colorama" version = "0.4.4" @@ -71,6 +90,25 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "cryptography" +version = "3.4.6" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + [[package]] name = "defusedxml" version = "0.6.0" @@ -87,9 +125,25 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "dj-database-url" +version = "0.5.0" +description = "Use Database URLs in your Django Application." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "dj-email-url" +version = "1.0.2" +description = "Use an URL to configure email backend settings in your Django Application." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "django" -version = "3.1.5" +version = "3.1.7" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false @@ -106,21 +160,42 @@ bcrypt = ["bcrypt"] [[package]] name = "django-allauth" -version = "0.40.0" +version = "0.44.0" description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." category = "main" optional = false python-versions = "*" [package.dependencies] -Django = ">=1.11" +Django = ">=2.0" +pyjwt = {version = ">=1.7", extras = ["crypto"]} python3-openid = ">=3.0.8" requests = "*" requests-oauthlib = ">=0.3.0" +[[package]] +name = "django-cache-url" +version = "3.2.3" +description = "Use Cache URLs in your Django application." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "django-debug-toolbar" +version = "3.2" +description = "A configurable set of panels that display various debug information about the current request/response." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Django = ">=2.2" +sqlparse = ">=0.2.0" + [[package]] name = "django-money" -version = "1.3" +version = "1.3.1" description = "Adds support for using money and currency fields in django models and forms. Uses py-moneyed as the money implementation." category = "main" optional = false @@ -132,7 +207,28 @@ py-moneyed = ">=0.8,<1.0" [package.extras] exchange = ["certifi"] -test = ["pytest (>=3.1.0)", "pytest-django", "pytest-pythonpath", "pytest-cov", "django-reversion", "mixer"] +test = ["pytest (>=3.1.0)", "pytest-django", "pytest-pythonpath", "pytest-cov", "mixer"] + +[[package]] +name = "environs" +version = "9.3.1" +description = "simplified environment variable parsing" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +dj-database-url = {version = "*", optional = true, markers = "extra == \"django\""} +dj-email-url = {version = "*", optional = true, markers = "extra == \"django\""} +django-cache-url = {version = "*", optional = true, markers = "extra == \"django\""} +marshmallow = ">=2.7.0" +python-dotenv = "*" + +[package.extras] +dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "mypy (==0.800)", "pre-commit (>=2.4,<3.0)", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +lint = ["flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "mypy (==0.800)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"] [[package]] name = "filelock" @@ -142,13 +238,21 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "h11" +version = "0.12.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "identify" -version = "1.5.13" +version = "2.0.0" description = "File identification library for Python" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.6.1" [package.extras] license = ["editdistance"] @@ -162,24 +266,22 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "importlib-metadata" -version = "3.4.0" -description = "Read metadata from Python packages" -category = "dev" +name = "marshmallow" +version = "3.10.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "main" optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" +python-versions = ">=3.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] +docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] [[package]] name = "more-itertools" -version = "8.6.0" +version = "8.7.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -208,7 +310,7 @@ signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] [[package]] name = "packaging" -version = "20.8" +version = "20.9" description = "Core utilities for Python packages" category = "dev" optional = false @@ -225,15 +327,12 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] [[package]] name = "pre-commit" -version = "2.9.3" +version = "2.10.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -242,7 +341,6 @@ python-versions = ">=3.6.1" [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" toml = "*" @@ -275,6 +373,31 @@ python-versions = "*" [package.extras] tests = ["pytest (>=2.3.0)", "tox (>=1.6.0)"] +[[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyjwt" +version = "2.0.1" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = {version = ">=3.3.1,<4.0.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.3.1,<4.0.0)"] +dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] + [[package]] name = "pyparsing" version = "2.4.7" @@ -295,7 +418,6 @@ python-versions = ">=3.5" atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=17.4.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} more-itertools = ">=4.0.0" packaging = "*" pluggy = ">=0.12,<1.0" @@ -321,6 +443,17 @@ pytest = ">=3.6" docs = ["sphinx", "sphinx-rtd-theme"] testing = ["django", "django-configurations (>=2.0)", "six"] +[[package]] +name = "python-dotenv" +version = "0.15.0" +description = "Add .env support to your django/flask apps in development and deployments" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python3-openid" version = "3.2.0" @@ -338,7 +471,7 @@ postgresql = ["psycopg2"] [[package]] name = "pytz" -version = "2020.5" +version = "2021.1" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -409,14 +542,6 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "typing-extensions" -version = "3.7.4.3" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "urllib3" version = "1.26.3" @@ -430,9 +555,24 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "uvicorn" +version = "0.13.4" +description = "The lightning-fast ASGI server." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +click = ">=7.0.0,<8.0.0" +h11 = ">=0.8" + +[package.extras] +standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] + [[package]] name = "virtualenv" -version = "20.4.0" +version = "20.4.2" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -442,7 +582,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" appdirs = ">=1.4.3,<2" distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} six = ">=1.9.0,<2" [package.extras] @@ -458,21 +597,20 @@ optional = false python-versions = "*" [[package]] -name = "zipp" -version = "3.4.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" +name = "whitenoise" +version = "5.2.0" +description = "Radically simplified static file serving for WSGI applications" +category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.5, <4" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +brotli = ["brotli"] [metadata] lock-version = "1.1" -python-versions = "^3.7" -content-hash = "29ee22cded289d14aaf1015cc00f5fd4b3e441f807c86b325e21c67c2314a274" +python-versions = "^3.9" +content-hash = "273466dbd53a484027b72a712aba600936507cd2d528c9a245464637c0c54207" [metadata.files] appdirs = [ @@ -495,6 +633,45 @@ certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] +cffi = [ + {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, + {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, + {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, + {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, + {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, + {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, + {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, + {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, + {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, + {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, + {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, + {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, + {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, + {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, + {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, + {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, + {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, + {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, + {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, +] cfgv = [ {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, @@ -503,10 +680,28 @@ chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +cryptography = [ + {file = "cryptography-3.4.6-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799"}, + {file = "cryptography-3.4.6-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7"}, + {file = "cryptography-3.4.6-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3"}, + {file = "cryptography-3.4.6-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b"}, + {file = "cryptography-3.4.6-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964"}, + {file = "cryptography-3.4.6-cp36-abi3-win32.whl", hash = "sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2"}, + {file = "cryptography-3.4.6-cp36-abi3-win_amd64.whl", hash = "sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0"}, + {file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b"}, + {file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df"}, + {file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336"}, + {file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724"}, + {file = "cryptography-3.4.6.tar.gz", hash = "sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87"}, +] defusedxml = [ {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, @@ -515,36 +710,60 @@ distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, ] +dj-database-url = [ + {file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"}, + {file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"}, +] +dj-email-url = [ + {file = "dj-email-url-1.0.2.tar.gz", hash = "sha256:838fd4ded9deba53ae757debef431e25fa7fca31d3948b3c4808ccdc84fab2b7"}, + {file = "dj_email_url-1.0.2-py2.py3-none-any.whl", hash = "sha256:15148141c6ef123636e4ca3663e95231ed94ca5ed267e91977e5a4397be8b34c"}, +] django = [ - {file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"}, - {file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"}, + {file = "Django-3.1.7-py3-none-any.whl", hash = "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"}, + {file = "Django-3.1.7.tar.gz", hash = "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7"}, ] django-allauth = [ - {file = "django-allauth-0.40.0.tar.gz", hash = "sha256:6a189fc4d3ee23596c3fd6e9f49c59b5b15618980118171a50675dd6a27cc589"}, + {file = "django-allauth-0.44.0.tar.gz", hash = "sha256:e51af457466022f52154d74c8523ac69375120fad2acce6e239635d85e610b25"}, +] +django-cache-url = [ + {file = "django-cache-url-3.2.3.tar.gz", hash = "sha256:c1d45626ae8a206267c1263aa7a3461e2e186be2e939bcbd8c660e25851ddac8"}, + {file = "django_cache_url-3.2.3-py2.py3-none-any.whl", hash = "sha256:5514ca3a2075c6b956b3d0a5c540654d32b004e76340d7bdabf6661135b5f218"}, +] +django-debug-toolbar = [ + {file = "django-debug-toolbar-3.2.tar.gz", hash = "sha256:84e2607d900dbd571df0a2acf380b47c088efb787dce9805aefeb407341961d2"}, + {file = "django_debug_toolbar-3.2-py3-none-any.whl", hash = "sha256:9e5a25d0c965f7e686f6a8ba23613ca9ca30184daa26487706d4829f5cfb697a"}, ] django-money = [ - {file = "django-money-1.3.tar.gz", hash = "sha256:da95f9a7174281eb2ef0f5f1584d5ee2670fc0d67707cd269816a73cae791eb3"}, - {file = "django_money-1.3-py3-none-any.whl", hash = "sha256:09952d49f998d089b21eb0f552d6dcb40d82626ab674d1caf0535bbd83a1ea01"}, + {file = "django-money-1.3.1.tar.gz", hash = "sha256:a363ce16a23e403befdafa9895b2f538a10f9d390b160f12140094a6dfd55246"}, + {file = "django_money-1.3.1-py3-none-any.whl", hash = "sha256:3b8fc751c8ae27cf877b8f3770ade1b63af97ee49a32ac08a6a1bc6d8d59f089"}, +] +environs = [ + {file = "environs-9.3.1-py2.py3-none-any.whl", hash = "sha256:2da44b7c30114415aa858577fa6396ee326fc76a0a60f0f15e8260ba554f19dc"}, + {file = "environs-9.3.1.tar.gz", hash = "sha256:3f6def554abb5455141b540e6e0b72fda3853404f2b0d31658aab1bf95410db3"}, ] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] +h11 = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] identify = [ - {file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"}, - {file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"}, + {file = "identify-2.0.0-py2.py3-none-any.whl", hash = "sha256:9cdd81e5d2b6e76c3006d5226316dd947bd6324fbeebb881bec489202fa09d3a"}, + {file = "identify-2.0.0.tar.gz", hash = "sha256:b99aa309329c4fea679463eb35d169f3fbe13e66e9dd6162ad1856cbeb03dcbd"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] -importlib-metadata = [ - {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, - {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, +marshmallow = [ + {file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"}, + {file = "marshmallow-3.10.0.tar.gz", hash = "sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7"}, ] more-itertools = [ - {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, - {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, + {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, + {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, ] nodeenv = [ {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, @@ -555,16 +774,16 @@ oauthlib = [ {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, ] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ - {file = "pre_commit-2.9.3-py2.py3-none-any.whl", hash = "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0"}, - {file = "pre_commit-2.9.3.tar.gz", hash = "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"}, + {file = "pre_commit-2.10.1-py2.py3-none-any.whl", hash = "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e"}, + {file = "pre_commit-2.10.1.tar.gz", hash = "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a"}, ] psycopg2 = [ {file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"}, @@ -591,6 +810,14 @@ py-moneyed = [ {file = "py-moneyed-0.8.0.tar.gz", hash = "sha256:ec73795171919d537880a33c44d07fcdf0a5225e8368684fe02f0e75a6404742"}, {file = "py_moneyed-0.8.0-py2.py3-none-any.whl", hash = "sha256:c6691b914a5e4b5b2335cf113620479a52cc82988c0e143435a7c5c7d60cd4ad"}, ] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pyjwt = [ + {file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"}, + {file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"}, +] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, @@ -603,13 +830,17 @@ pytest-django = [ {file = "pytest-django-3.10.0.tar.gz", hash = "sha256:4de6dbd077ed8606616958f77655fed0d5e3ee45159475671c7fa67596c6dba6"}, {file = "pytest_django-3.10.0-py2.py3-none-any.whl", hash = "sha256:c33e3d3da14d8409b125d825d4e74da17bb252191bf6fc3da6856e27a8b73ea4"}, ] +python-dotenv = [ + {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, + {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, +] python3-openid = [ {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, ] pytz = [ - {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, - {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -655,24 +886,23 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, -] urllib3 = [ {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, ] +uvicorn = [ + {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, + {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, +] virtualenv = [ - {file = "virtualenv-20.4.0-py2.py3-none-any.whl", hash = "sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034"}, - {file = "virtualenv-20.4.0.tar.gz", hash = "sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c"}, + {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"}, + {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] -zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, +whitenoise = [ + {file = "whitenoise-5.2.0-py2.py3-none-any.whl", hash = "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d"}, + {file = "whitenoise-5.2.0.tar.gz", hash = "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7"}, ] diff --git a/project/settings/__init__.py b/project/settings/__init__.py deleted file mode 100644 index 87c4305..0000000 --- a/project/settings/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -import warnings - -from .base import * # noqa - -try: - from .local import * # noqa -except ImportError: - warnings.warn( - "No settings.local, using a default SECRET_KEY 'hest'. You should " - "write a custom local.py with this setting." - ) - SECRET_KEY = "hest" - DEBUG = True - pass diff --git a/project/settings/base.py b/project/settings/base.py deleted file mode 100644 index fcbda74..0000000 --- a/project/settings/base.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -Django settings for membersystem project. - -Generated by 'django-admin startproject' using Django 2.0.4. - -For more information on this file, see -https://docs.djangoproject.com/en/2.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.0/ref/settings/ -""" -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", - "django.contrib.sites", - "users", - "accounting", - "membership", -] - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "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", -] - -ROOT_URLCONF = "project.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [os.path.join("project", "templates")], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - "project.context_processors.current_site", - ] - }, - } -] - -AUTHENTICATION_BACKENDS = ( - "django.contrib.auth.backends.ModelBackend", - "allauth.account.auth_backends.AuthenticationBackend", -) - -WSGI_APPLICATION = "project.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/2.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" # noqa - }, - {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, # noqa - {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, # noqa - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator" # noqa - }, -] - -AUTH_USER_MODEL = "users.User" - - -# Internationalization -# https://docs.djangoproject.com/en/2.0/topics/i18n/ - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = "UTC" - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.0/howto/static-files/ - -STATIC_URL = "/static/" - -STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] - -SITE_ID = 1 - -EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" - -CURRENCIES = ("DKK",) -CURRENCY_CHOICES = [("DKK", "DKK")] diff --git a/project/static/css/membersystem.css b/project/static/css/membersystem.css deleted file mode 100644 index cbf11d5..0000000 --- a/project/static/css/membersystem.css +++ /dev/null @@ -1,79 +0,0 @@ -/* General styles */ -html -{ - margin: 0; - padding: 0; - font-family: sans-serif; - font-size: 2.5vmin; - background: #f8f8f8; -} - -body -{ - background: #fff; - color: #000; - margin: 1em auto; - max-width: 50em; - padding: 0 1em; - box-shadow: 0 0 2.5em rgba(0, 0, 0, 20%); -} - -header, -footer -{ - background: #eee; - padding: .5em; - margin: 0 -1em; -} - -footer -{ - margin-top: 2em; -} - - -header h1 -{ - font-size: 1em; - float: left; - padding: .5em .5em; - margin: 0; -} - -header ul, -footer ul -{ - list-style-type: none; - padding: 0; - margin: 0; - text-align: right; -} - -header ul li, -footer ul li -{ - display: inline; -} - -header ul li a, -footer ul li a -{ - display: inline-block; - margin: 0; - padding: .5em .5em; -} - - -/* Forms */ -label -{ - display: block; - padding: .5em 0; -} - -button, -input, -textarea -{ - font-size: inherit; -} diff --git a/project/templates/base.html b/project/templates/base.html deleted file mode 100644 index a6aaf00..0000000 --- a/project/templates/base.html +++ /dev/null @@ -1,49 +0,0 @@ - -{% load static %} - -
-{% trans "You do not have an active membership!" %}
+ +{% trans "You can become a member by depositing the membership fee to our bank account." %}
+ +{% trans "You are a member!" %}
+ +{% trans "Period" %}: {{ current_membership.period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_membership.period.upper|date:"SHORT_DATE_FORMAT" }}
+{% trans "Type" %}: {{ current_membership.membership_type }}
+ {% endif %} + {% endblock %} diff --git a/src/membership/views.py b/src/membership/views.py new file mode 100644 index 0000000..9fbeded --- /dev/null +++ b/src/membership/views.py @@ -0,0 +1,18 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import render + +from .models import Membership + + +@login_required +def membership_overview(request): + memberships = Membership.objects.for_user(request.user) + current_membership = memberships.current() + previous_memberships = memberships.previous() + + context = dict( + current_membership=current_membership, + previous_memberships=previous_memberships, + ) + + return render(request, "membership_overview.html", context) diff --git a/project/__init__.py b/src/project/__init__.py similarity index 100% rename from project/__init__.py rename to src/project/__init__.py diff --git a/src/project/asgi.py b/src/project/asgi.py new file mode 100644 index 0000000..924a849 --- /dev/null +++ b/src/project/asgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") + +application = get_asgi_application() diff --git a/project/context_processors.py b/src/project/context_processors.py similarity index 100% rename from project/context_processors.py rename to src/project/context_processors.py diff --git a/src/project/locale/da/LC_MESSAGES/django.po b/src/project/locale/da/LC_MESSAGES/django.po new file mode 100644 index 0000000..9bb7b83 --- /dev/null +++ b/src/project/locale/da/LC_MESSAGES/django.po @@ -0,0 +1,519 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `