Refactoring things and doing stuff WIP way.
This commit is contained in:
parent
2be2755cb6
commit
4c75991fcb
33
Makefile
33
Makefile
|
@ -1,13 +1,30 @@
|
||||||
# These are just some make targets, expressing how things
|
DOCKER_RUN = docker-compose run
|
||||||
# are supposed to be run, but feel free to change them!
|
DOCKER_BUILD = DOCKER_BUILDKIT=1 docker build
|
||||||
|
MANAGE = ${DOCKER_RUN} backend python /app/src/manage.py
|
||||||
dev-setup:
|
|
||||||
poetry run pre-commit install
|
|
||||||
poetry run python manage.py migrate
|
|
||||||
poetry run python manage.py createsuperuser
|
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
poetry run pre-commit run --all
|
poetry run pre-commit run --all
|
||||||
|
|
||||||
|
run:
|
||||||
|
docker-compose up --build
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker-compose build
|
||||||
|
|
||||||
|
makemigrations:
|
||||||
|
${MANAGE} makemigrations ${EXTRA_ARGS}
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
${MANAGE} migrate ${EXTRA_ARGS}
|
||||||
|
|
||||||
|
createsuperuser:
|
||||||
|
${MANAGE} createsuperuser
|
||||||
|
|
||||||
|
shell:
|
||||||
|
${MANAGE} shell
|
||||||
|
|
||||||
|
manage_command:
|
||||||
|
${MANAGE} ${COMMAND}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
poetry run pytest
|
${DOCKER_RUN} backend pytest src/
|
||||||
|
|
|
@ -1,233 +0,0 @@
|
||||||
# Generated by Django 2.0.6 on 2018-06-23 19:51
|
|
||||||
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="Account",
|
|
||||||
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"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"owner",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.PROTECT,
|
|
||||||
to=settings.AUTH_USER_MODEL,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"abstract": False},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Order",
|
|
||||||
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"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"description",
|
|
||||||
models.CharField(max_length=1024, verbose_name="description"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"price_currency",
|
|
||||||
djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")],
|
|
||||||
default="XYZ",
|
|
||||||
editable=False,
|
|
||||||
max_length=3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"price",
|
|
||||||
djmoney.models.fields.MoneyField(
|
|
||||||
decimal_places=2,
|
|
||||||
default=Decimal("0.0"),
|
|
||||||
max_digits=16,
|
|
||||||
verbose_name="price (excl. VAT)",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"vat_currency",
|
|
||||||
djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")],
|
|
||||||
default="XYZ",
|
|
||||||
editable=False,
|
|
||||||
max_length=3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"vat",
|
|
||||||
djmoney.models.fields.MoneyField(
|
|
||||||
decimal_places=2,
|
|
||||||
default=Decimal("0.0"),
|
|
||||||
max_digits=16,
|
|
||||||
verbose_name="VAT",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("is_paid", models.BooleanField(default=False, verbose_name="is paid")),
|
|
||||||
(
|
|
||||||
"account",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.PROTECT,
|
|
||||||
to="accounting.Account",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"user",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.PROTECT,
|
|
||||||
to=settings.AUTH_USER_MODEL,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"verbose_name": "Order", "verbose_name_plural": "Orders"},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Payment",
|
|
||||||
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"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"amount_currency",
|
|
||||||
djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")],
|
|
||||||
default="XYZ",
|
|
||||||
editable=False,
|
|
||||||
max_length=3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"amount",
|
|
||||||
djmoney.models.fields.MoneyField(
|
|
||||||
decimal_places=2, default=Decimal("0.0"), max_digits=16
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"description",
|
|
||||||
models.CharField(max_length=1024, verbose_name="description"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"stripe_charge_id",
|
|
||||||
models.CharField(blank=True, max_length=255, null=True),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"order",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.PROTECT,
|
|
||||||
to="accounting.Order",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"verbose_name": "payment", "verbose_name_plural": "payments"},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Transaction",
|
|
||||||
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"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"amount_currency",
|
|
||||||
djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")],
|
|
||||||
default="XYZ",
|
|
||||||
editable=False,
|
|
||||||
max_length=3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"amount",
|
|
||||||
djmoney.models.fields.MoneyField(
|
|
||||||
decimal_places=2,
|
|
||||||
default=Decimal("0.0"),
|
|
||||||
help_text="This will include VAT",
|
|
||||||
max_digits=16,
|
|
||||||
verbose_name="amount",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"description",
|
|
||||||
models.CharField(max_length=1024, verbose_name="description"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"account",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.PROTECT,
|
|
||||||
related_name="transactions",
|
|
||||||
to="accounting.Account",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"abstract": False},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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:
|
|
@ -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
|
|
@ -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
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -47,6 +47,17 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
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]]
|
[[package]]
|
||||||
name = "cfgv"
|
name = "cfgv"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
|
@ -71,6 +82,25 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
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]]
|
[[package]]
|
||||||
name = "defusedxml"
|
name = "defusedxml"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -87,9 +117,25 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
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]]
|
[[package]]
|
||||||
name = "django"
|
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."
|
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -106,21 +152,42 @@ bcrypt = ["bcrypt"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-allauth"
|
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."
|
description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Django = ">=1.11"
|
Django = ">=2.0"
|
||||||
|
pyjwt = {version = ">=1.7", extras = ["crypto"]}
|
||||||
python3-openid = ">=3.0.8"
|
python3-openid = ">=3.0.8"
|
||||||
requests = "*"
|
requests = "*"
|
||||||
requests-oauthlib = ">=0.3.0"
|
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]]
|
[[package]]
|
||||||
name = "django-money"
|
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."
|
description = "Adds support for using money and currency fields in django models and forms. Uses py-moneyed as the money implementation."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -132,7 +199,28 @@ py-moneyed = ">=0.8,<1.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
exchange = ["certifi"]
|
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]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
|
@ -144,7 +232,7 @@ python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "identify"
|
name = "identify"
|
||||||
version = "1.5.13"
|
version = "1.6.0"
|
||||||
description = "File identification library for Python"
|
description = "File identification library for Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -162,24 +250,22 @@ optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "importlib-metadata"
|
name = "marshmallow"
|
||||||
version = "3.4.0"
|
version = "3.10.0"
|
||||||
description = "Read metadata from Python packages"
|
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||||
category = "dev"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
|
||||||
zipp = ">=0.5"
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"]
|
||||||
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)"]
|
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]]
|
[[package]]
|
||||||
name = "more-itertools"
|
name = "more-itertools"
|
||||||
version = "8.6.0"
|
version = "8.7.0"
|
||||||
description = "More routines for operating on iterables, beyond itertools"
|
description = "More routines for operating on iterables, beyond itertools"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -208,7 +294,7 @@ signedtoken = ["cryptography", "pyjwt (>=1.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "20.8"
|
version = "20.9"
|
||||||
description = "Core utilities for Python packages"
|
description = "Core utilities for Python packages"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -225,15 +311,12 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
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]
|
[package.extras]
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pre-commit"
|
name = "pre-commit"
|
||||||
version = "2.9.3"
|
version = "2.10.1"
|
||||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -242,7 +325,6 @@ python-versions = ">=3.6.1"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cfgv = ">=2.0.0"
|
cfgv = ">=2.0.0"
|
||||||
identify = ">=1.0.0"
|
identify = ">=1.0.0"
|
||||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
nodeenv = ">=0.11.1"
|
nodeenv = ">=0.11.1"
|
||||||
pyyaml = ">=5.1"
|
pyyaml = ">=5.1"
|
||||||
toml = "*"
|
toml = "*"
|
||||||
|
@ -275,6 +357,31 @@ python-versions = "*"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["pytest (>=2.3.0)", "tox (>=1.6.0)"]
|
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]]
|
[[package]]
|
||||||
name = "pyparsing"
|
name = "pyparsing"
|
||||||
version = "2.4.7"
|
version = "2.4.7"
|
||||||
|
@ -295,7 +402,6 @@ python-versions = ">=3.5"
|
||||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||||
attrs = ">=17.4.0"
|
attrs = ">=17.4.0"
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
|
||||||
more-itertools = ">=4.0.0"
|
more-itertools = ">=4.0.0"
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
pluggy = ">=0.12,<1.0"
|
pluggy = ">=0.12,<1.0"
|
||||||
|
@ -321,6 +427,17 @@ pytest = ">=3.6"
|
||||||
docs = ["sphinx", "sphinx-rtd-theme"]
|
docs = ["sphinx", "sphinx-rtd-theme"]
|
||||||
testing = ["django", "django-configurations (>=2.0)", "six"]
|
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]]
|
[[package]]
|
||||||
name = "python3-openid"
|
name = "python3-openid"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
|
@ -338,7 +455,7 @@ postgresql = ["psycopg2"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2020.5"
|
version = "2021.1"
|
||||||
description = "World timezone definitions, modern and historical"
|
description = "World timezone definitions, modern and historical"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -409,14 +526,6 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
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]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "1.26.3"
|
version = "1.26.3"
|
||||||
|
@ -432,7 +541,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtualenv"
|
name = "virtualenv"
|
||||||
version = "20.4.0"
|
version = "20.4.2"
|
||||||
description = "Virtual Python Environment builder"
|
description = "Virtual Python Environment builder"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -442,7 +551,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||||
appdirs = ">=1.4.3,<2"
|
appdirs = ">=1.4.3,<2"
|
||||||
distlib = ">=0.3.1,<1"
|
distlib = ">=0.3.1,<1"
|
||||||
filelock = ">=3.0.0,<4"
|
filelock = ">=3.0.0,<4"
|
||||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
|
||||||
six = ">=1.9.0,<2"
|
six = ">=1.9.0,<2"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
@ -457,22 +565,10 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zipp"
|
|
||||||
version = "3.4.0"
|
|
||||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
|
|
||||||
[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"]
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.9"
|
||||||
content-hash = "29ee22cded289d14aaf1015cc00f5fd4b3e441f807c86b325e21c67c2314a274"
|
content-hash = "8a01d7c9866e867f833f218d11a56cdebbcb9c5d1d24b636fe44e6393f2652d6"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
appdirs = [
|
appdirs = [
|
||||||
|
@ -495,6 +591,45 @@ certifi = [
|
||||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
{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 = [
|
cfgv = [
|
||||||
{file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"},
|
{file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"},
|
||||||
{file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"},
|
{file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"},
|
||||||
|
@ -507,6 +642,20 @@ colorama = [
|
||||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
{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 = [
|
defusedxml = [
|
||||||
{file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"},
|
{file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"},
|
||||||
{file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"},
|
{file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"},
|
||||||
|
@ -515,36 +664,56 @@ distlib = [
|
||||||
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
|
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
|
||||||
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
|
{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 = [
|
django = [
|
||||||
{file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"},
|
{file = "Django-3.1.7-py3-none-any.whl", hash = "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"},
|
||||||
{file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"},
|
{file = "Django-3.1.7.tar.gz", hash = "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7"},
|
||||||
]
|
]
|
||||||
django-allauth = [
|
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 = [
|
django-money = [
|
||||||
{file = "django-money-1.3.tar.gz", hash = "sha256:da95f9a7174281eb2ef0f5f1584d5ee2670fc0d67707cd269816a73cae791eb3"},
|
{file = "django-money-1.3.1.tar.gz", hash = "sha256:a363ce16a23e403befdafa9895b2f538a10f9d390b160f12140094a6dfd55246"},
|
||||||
{file = "django_money-1.3-py3-none-any.whl", hash = "sha256:09952d49f998d089b21eb0f552d6dcb40d82626ab674d1caf0535bbd83a1ea01"},
|
{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 = [
|
filelock = [
|
||||||
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
|
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
|
||||||
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
|
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
|
||||||
]
|
]
|
||||||
identify = [
|
identify = [
|
||||||
{file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"},
|
{file = "identify-1.6.0-py2.py3-none-any.whl", hash = "sha256:63e8105ec44c16d96beb33a0ae5d5ed1bbefd72415ea7e9d684b2f9b4a1cabf9"},
|
||||||
{file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"},
|
{file = "identify-1.6.0.tar.gz", hash = "sha256:41b6bed56f30150b25ef4c2724688d9d202fcebc448ad243fd9df4e4856c81fc"},
|
||||||
]
|
]
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||||
]
|
]
|
||||||
importlib-metadata = [
|
marshmallow = [
|
||||||
{file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"},
|
{file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"},
|
||||||
{file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"},
|
{file = "marshmallow-3.10.0.tar.gz", hash = "sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7"},
|
||||||
]
|
]
|
||||||
more-itertools = [
|
more-itertools = [
|
||||||
{file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"},
|
{file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"},
|
||||||
{file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"},
|
{file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"},
|
||||||
]
|
]
|
||||||
nodeenv = [
|
nodeenv = [
|
||||||
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
|
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
|
||||||
|
@ -555,16 +724,16 @@ oauthlib = [
|
||||||
{file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"},
|
{file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"},
|
||||||
]
|
]
|
||||||
packaging = [
|
packaging = [
|
||||||
{file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"},
|
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
||||||
{file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"},
|
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
||||||
]
|
]
|
||||||
pluggy = [
|
pluggy = [
|
||||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||||
]
|
]
|
||||||
pre-commit = [
|
pre-commit = [
|
||||||
{file = "pre_commit-2.9.3-py2.py3-none-any.whl", hash = "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0"},
|
{file = "pre_commit-2.10.1-py2.py3-none-any.whl", hash = "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e"},
|
||||||
{file = "pre_commit-2.9.3.tar.gz", hash = "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"},
|
{file = "pre_commit-2.10.1.tar.gz", hash = "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a"},
|
||||||
]
|
]
|
||||||
psycopg2 = [
|
psycopg2 = [
|
||||||
{file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"},
|
{file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"},
|
||||||
|
@ -591,6 +760,14 @@ py-moneyed = [
|
||||||
{file = "py-moneyed-0.8.0.tar.gz", hash = "sha256:ec73795171919d537880a33c44d07fcdf0a5225e8368684fe02f0e75a6404742"},
|
{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"},
|
{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 = [
|
pyparsing = [
|
||||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||||
|
@ -603,13 +780,17 @@ pytest-django = [
|
||||||
{file = "pytest-django-3.10.0.tar.gz", hash = "sha256:4de6dbd077ed8606616958f77655fed0d5e3ee45159475671c7fa67596c6dba6"},
|
{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"},
|
{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 = [
|
python3-openid = [
|
||||||
{file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"},
|
{file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"},
|
||||||
{file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"},
|
{file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"},
|
||||||
]
|
]
|
||||||
pytz = [
|
pytz = [
|
||||||
{file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"},
|
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
||||||
{file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"},
|
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
|
||||||
]
|
]
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||||
|
@ -655,24 +836,15 @@ toml = [
|
||||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
{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 = [
|
urllib3 = [
|
||||||
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
||||||
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
||||||
]
|
]
|
||||||
virtualenv = [
|
virtualenv = [
|
||||||
{file = "virtualenv-20.4.0-py2.py3-none-any.whl", hash = "sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034"},
|
{file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"},
|
||||||
{file = "virtualenv-20.4.0.tar.gz", hash = "sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c"},
|
{file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"},
|
||||||
]
|
]
|
||||||
wcwidth = [
|
wcwidth = [
|
||||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
{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"},
|
|
||||||
]
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
"""URLs for the membersystem"""
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import include
|
|
||||||
from django.urls import path
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("", views.index),
|
|
||||||
path("users/", include("users.urls")),
|
|
||||||
path("admin/", admin.site.urls),
|
|
||||||
]
|
|
|
@ -5,16 +5,18 @@ description = ""
|
||||||
authors = ["Your Name <you@example.com>"]
|
authors = ["Your Name <you@example.com>"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7"
|
python = "^3.9"
|
||||||
Django = "^3.1"
|
Django = "^3.1"
|
||||||
django-money = "^1.3"
|
django-money = "^1.3"
|
||||||
django-allauth = "^0.40.0"
|
django-allauth = "^0.44.0"
|
||||||
psycopg2 = "^2.8.6"
|
psycopg2 = "^2.8.6"
|
||||||
|
environs = {extras = ["django"], version = "^9.3.1"}
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pre-commit = "^2.9.3"
|
pre-commit = "^2.9.3"
|
||||||
pytest = "^5.1"
|
pytest = "^5.1"
|
||||||
pytest-django = "^3.5"
|
pytest-django = "^3.5"
|
||||||
|
django-debug-toolbar = "^3.2"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry>=0.12"]
|
requires = ["poetry>=0.12"]
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Generated by Django 3.1.7 on 2021-02-27 20:06
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import djmoney.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Account',
|
||||||
|
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='oprettet')),
|
||||||
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Order',
|
||||||
|
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='oprettet')),
|
||||||
|
('description', models.CharField(max_length=1024, verbose_name='description')),
|
||||||
|
('price_currency', djmoney.models.fields.CurrencyField(choices=[('DKK', 'DKK')], default='XYZ', editable=False, max_length=3)),
|
||||||
|
('price', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=16, verbose_name='price (excl. VAT)')),
|
||||||
|
('vat_currency', djmoney.models.fields.CurrencyField(choices=[('DKK', 'DKK')], default='XYZ', editable=False, max_length=3)),
|
||||||
|
('vat', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=16, verbose_name='VAT')),
|
||||||
|
('is_paid', models.BooleanField(default=False, verbose_name='is paid')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='accounting.account')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Order',
|
||||||
|
'verbose_name_plural': 'Orders',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Transaction',
|
||||||
|
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='oprettet')),
|
||||||
|
('amount_currency', djmoney.models.fields.CurrencyField(choices=[('DKK', 'DKK')], default='XYZ', editable=False, max_length=3)),
|
||||||
|
('amount', djmoney.models.fields.MoneyField(decimal_places=2, help_text='This will include VAT', max_digits=16, verbose_name='amount')),
|
||||||
|
('description', models.CharField(max_length=1024, verbose_name='description')),
|
||||||
|
('account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='accounting.account')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Payment',
|
||||||
|
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='oprettet')),
|
||||||
|
('amount_currency', djmoney.models.fields.CurrencyField(choices=[('DKK', 'DKK')], default='XYZ', editable=False, max_length=3)),
|
||||||
|
('amount', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=16)),
|
||||||
|
('description', models.CharField(max_length=1024, verbose_name='description')),
|
||||||
|
('stripe_charge_id', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='accounting.order')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'payment',
|
||||||
|
'verbose_name_plural': 'payments',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,7 +1,6 @@
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.aggregates import Sum
|
from django.db.models.aggregates import Sum
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
@ -24,7 +23,7 @@ class Account(CreatedModifiedAbstract):
|
||||||
can decide which account to use to pay for something.
|
can decide which account to use to pay for something.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
|
owner = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def balance(self):
|
def balance(self):
|
||||||
|
@ -56,9 +55,8 @@ class Order(CreatedModifiedAbstract):
|
||||||
invoices at the moment.
|
invoices at the moment.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
|
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
account = models.ForeignKey(Account, on_delete=models.PROTECT)
|
account = models.ForeignKey(Account, on_delete=models.PROTECT)
|
||||||
is_paid = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
description = models.CharField(max_length=1024, verbose_name=_("description"))
|
description = models.CharField(max_length=1024, verbose_name=_("description"))
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# Generated by Django 3.1.7 on 2021-02-27 20:06
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.conf import settings
|
||||||
|
import django.contrib.postgres.fields.ranges
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import djmoney.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
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='oprettet')),
|
||||||
|
('name', models.CharField(max_length=64, verbose_name='navn')),
|
||||||
|
('fee_currency', djmoney.models.fields.CurrencyField(choices=[('DKK', 'DKK')], default='XYZ', editable=False, max_length=3)),
|
||||||
|
('fee', djmoney.models.fields.MoneyField(decimal_places=2, 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)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'subscription type',
|
||||||
|
'verbose_name_plural': 'subscription types',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
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='oprettet')),
|
||||||
|
('active', models.BooleanField(default=False, help_text='Automatically set by payment system.', verbose_name='aktiv')),
|
||||||
|
('duration', django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The duration this subscription is for. ')),
|
||||||
|
('subscription_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='memberships', to='membership.subscriptiontype', verbose_name='subscription type')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'subscription',
|
||||||
|
'verbose_name_plural': 'subscriptions',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
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='oprettet')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'membership',
|
||||||
|
'verbose_name_plural': 'memberships',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,4 +1,4 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.postgres.fields import DateTimeRangeField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from djmoney.models.fields import MoneyField
|
from djmoney.models.fields import MoneyField
|
||||||
|
@ -13,49 +13,18 @@ class CreatedModifiedAbstract(models.Model):
|
||||||
abstract = True
|
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):
|
class Membership(CreatedModifiedAbstract):
|
||||||
"""
|
"""
|
||||||
A user remains a member of an organization even though the subscription is
|
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
|
unpaid or renewed. This just changes the status/permissions etc. of the
|
||||||
membership, thus we need to track subscription creation, expiry, renewals
|
membership, thus we need to track subscription creation, expiry, renewals
|
||||||
etc. and ensure that the membership is modified accordingly.
|
etc. and ensure that the membership is modified accordingly.
|
||||||
|
|
||||||
This expresses some
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
organization = models.ForeignKey(Organization, on_delete=models.PROTECT)
|
user = models.OneToOneField("auth.User", 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):
|
def __str__(self):
|
||||||
|
return _(f"{self.user.get_full_name()} is a member")
|
||||||
return _("{} is a member of {}").format(
|
|
||||||
self.user.get_full_name(), self.organization.name
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("membership")
|
verbose_name = _("membership")
|
||||||
|
@ -68,18 +37,12 @@ class SubscriptionType(CreatedModifiedAbstract):
|
||||||
after subscriptions are created.
|
after subscriptions are created.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
organization = models.ForeignKey(Organization, on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
name = models.CharField(verbose_name=_("name"), max_length=64)
|
name = models.CharField(verbose_name=_("name"), max_length=64)
|
||||||
|
|
||||||
fee = MoneyField(max_digits=16, decimal_places=2)
|
fee = MoneyField(max_digits=16, decimal_places=2)
|
||||||
|
|
||||||
fee_vat = MoneyField(max_digits=16, decimal_places=2, default=0)
|
fee_vat = MoneyField(max_digits=16, decimal_places=2, default=0)
|
||||||
|
|
||||||
duration = models.PositiveSmallIntegerField(
|
|
||||||
default=1, choices=[(1, _("annual"))], verbose_name=_("duration")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("subscription type")
|
verbose_name = _("subscription type")
|
||||||
verbose_name_plural = _("subscription types")
|
verbose_name_plural = _("subscription types")
|
||||||
|
@ -88,7 +51,7 @@ class SubscriptionType(CreatedModifiedAbstract):
|
||||||
class Subscription(CreatedModifiedAbstract):
|
class Subscription(CreatedModifiedAbstract):
|
||||||
"""
|
"""
|
||||||
To not confuse other types of subscriptions, one can be a *subscribed*
|
To not confuse other types of subscriptions, one can be a *subscribed*
|
||||||
member of an organization, meaning that they are paying etc.
|
member, meaning that they are paying etc.
|
||||||
|
|
||||||
A subscription does not track payment, this is done in the accounting app.
|
A subscription does not track payment, this is done in the accounting app.
|
||||||
"""
|
"""
|
||||||
|
@ -99,7 +62,7 @@ class Subscription(CreatedModifiedAbstract):
|
||||||
verbose_name=_("subscription type"),
|
verbose_name=_("subscription type"),
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
user = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
|
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
|
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -107,16 +70,7 @@ class Subscription(CreatedModifiedAbstract):
|
||||||
help_text=_("Automatically set by payment system."),
|
help_text=_("Automatically set by payment system."),
|
||||||
)
|
)
|
||||||
|
|
||||||
starts = models.DateField()
|
duration = DateTimeRangeField(help_text=_("The duration this subscription is for. "))
|
||||||
ends = models.DateField()
|
|
||||||
|
|
||||||
renewed_subscription = models.ForeignKey(
|
|
||||||
"self",
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
verbose_name=_("renewed subscription"),
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("subscription")
|
verbose_name = _("subscription")
|
|
@ -1,26 +1,18 @@
|
||||||
"""
|
from pathlib import Path
|
||||||
Django settings for membersystem project.
|
from environs import Env
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 2.0.4.
|
env = Env()
|
||||||
|
env.read_env()
|
||||||
|
|
||||||
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
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
https://docs.djangoproject.com/en/2.0/ref/settings/
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
SECRET_KEY = env.str("SECRET_KEY", default="something-very-secret")
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
DEBUG = env.bool("DEBUG", default=False)
|
||||||
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
@ -33,11 +25,15 @@ INSTALLED_APPS = [
|
||||||
"django.contrib.messages",
|
"django.contrib.messages",
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"django.contrib.sites",
|
"django.contrib.sites",
|
||||||
"users",
|
"debug_toolbar",
|
||||||
|
"allauth",
|
||||||
|
"allauth.account",
|
||||||
"accounting",
|
"accounting",
|
||||||
"membership",
|
"membership",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DATABASES = {"default": env.dj_db_url("DATABASE_URL")}
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
@ -46,6 +42,7 @@ MIDDLEWARE = [
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = "project.urls"
|
ROOT_URLCONF = "project.urls"
|
||||||
|
@ -53,7 +50,7 @@ ROOT_URLCONF = "project.urls"
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
"DIRS": [os.path.join("project", "templates")],
|
"DIRS": [BASE_DIR / "project" / "templates"],
|
||||||
"APP_DIRS": True,
|
"APP_DIRS": True,
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"context_processors": [
|
"context_processors": [
|
||||||
|
@ -75,20 +72,6 @@ AUTHENTICATION_BACKENDS = (
|
||||||
WSGI_APPLICATION = "project.wsgi.application"
|
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 = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" # noqa
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" # noqa
|
||||||
|
@ -100,15 +83,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
AUTH_USER_MODEL = "users.User"
|
LANGUAGE_CODE = "da-dk"
|
||||||
|
|
||||||
|
TIME_ZONE = "Europe/Copenhagen"
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = "en-us"
|
|
||||||
|
|
||||||
TIME_ZONE = "UTC"
|
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
@ -116,17 +93,22 @@ USE_L10N = True
|
||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = "/static/"
|
STATIC_URL = "/static/"
|
||||||
|
|
||||||
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
|
STATICFILES_DIRS = [BASE_DIR / "project" / "static"]
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
LOGIN_REDIRECT_URL = "/"
|
||||||
|
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
|
||||||
|
# Always show DDT in development for any IP, not just 127.0.0.1 or
|
||||||
|
# settings.INTERNAL_IPS. This is useful in a docker setup where the
|
||||||
|
# requesting IP isn't static.
|
||||||
|
DEBUG_TOOLBAR_CONFIG = {
|
||||||
|
"SHOW_TOOLBAR_CALLBACK": lambda _x: DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
CURRENCIES = ("DKK",)
|
CURRENCIES = ("DKK",)
|
||||||
CURRENCY_CHOICES = [("DKK", "DKK")]
|
CURRENCY_CHOICES = [("DKK", "DKK")]
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,128 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<title>Login – {{ site.name }}</title>
|
||||||
|
|
||||||
|
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
|
||||||
|
integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
background-color: #a8f3f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 330px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .checkbox {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .form-control {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .form-control:focus {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin input[type="email"] {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin input[type="password"] {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hr-text:after {
|
||||||
|
content: attr(data-content);
|
||||||
|
padding: 0 4px;
|
||||||
|
position: relative;
|
||||||
|
top: -13px;
|
||||||
|
background-color: #a8f3f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="text-center">
|
||||||
|
|
||||||
|
<main class="form-signin">
|
||||||
|
|
||||||
|
<img class="mb-4" src="https://new.data.coop/static/img/logo_da.svg" alt=""
|
||||||
|
width="260" height="160">
|
||||||
|
<h1 class="h3 mb-3 fw-normal">{% trans "Members only" %}</h1>
|
||||||
|
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
{% for error in form.non_field_errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<label for="id_username"
|
||||||
|
class="visually-hidden">
|
||||||
|
{% trans "Username/e-mail" %}
|
||||||
|
</label>
|
||||||
|
<input type="text"
|
||||||
|
id="id_username"
|
||||||
|
name="login"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="{% trans "Username/e-mail" %}"
|
||||||
|
required
|
||||||
|
autofocus>
|
||||||
|
|
||||||
|
<label for="id_password" class="visually-hidden">
|
||||||
|
{% trans "Password" %}
|
||||||
|
</label>
|
||||||
|
<input type="password"
|
||||||
|
id="id_password"
|
||||||
|
name="password"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="{% trans "Password" %}"
|
||||||
|
required>
|
||||||
|
|
||||||
|
<button class="w-100 btn btn-lg btn-primary"
|
||||||
|
type="submit">{% trans "Sign in" %}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr class="hr-text" data-content="OR">
|
||||||
|
|
||||||
|
<a class="w-100 btn btn-lg btn-outline-success"
|
||||||
|
type="submit">{% trans "Become a member" %}</a>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,181 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<title>{% block head_title %}{% endblock %} – {{ site.name }}</title>
|
||||||
|
|
||||||
|
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
|
||||||
|
integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar
|
||||||
|
*/
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100; /* Behind the navbar */
|
||||||
|
padding: 48px 0 0; /* Height of navbar */
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.sidebar {
|
||||||
|
top: 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-sticky {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
height: calc(100vh - 48px);
|
||||||
|
padding-top: .5rem;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link .feather {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #727272;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link.active {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link:hover .feather,
|
||||||
|
.sidebar .nav-link.active .feather {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-heading {
|
||||||
|
font-size: .75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Navbar
|
||||||
|
*/
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
padding-top: .75rem;
|
||||||
|
padding-bottom: .75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: rgba(0, 0, 0, .25);
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .navbar-toggler {
|
||||||
|
top: .25rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .form-control {
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-dark {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
border-color: rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-dark:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||||
|
} </style>
|
||||||
|
|
||||||
|
<script src="{% static "js/bootstrap.bundle.min.js" %}"
|
||||||
|
integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
||||||
|
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">{{ site.name }}</a>
|
||||||
|
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button"
|
||||||
|
data-bs-toggle="collapse" data-bs-target="#sidebarMenu"
|
||||||
|
aria-controls="sidebarMenu" aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="navbar-nav px-3">
|
||||||
|
<li class="nav-item text-nowrap">
|
||||||
|
<a class="nav-link" href="{% url "account_logout" %}">
|
||||||
|
<i class="fas fa-user"></i>
|
||||||
|
Sign out</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<nav id="sidebarMenu"
|
||||||
|
class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
|
||||||
|
<div class="position-sticky pt-3">
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" aria-current="page" href="#">
|
||||||
|
{% trans "Overview" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
{% trans "Profile" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
{% trans "Membership" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
{% trans "Services" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||||
|
<span>{% trans "Admin" %}</span>
|
||||||
|
</h6>
|
||||||
|
<ul class="nav flex-column mb-2">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
<span data-feather="file-text"></span>
|
||||||
|
{% trans "Members" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,10 +1,10 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>{% block head_title %}{% endblock %} – {{ site.name }}</title>
|
<title>{% block head_title %}{% endblock %} – {{ site.name }}</title>
|
||||||
{% block extra_head %}{% endblock %}
|
{% block extra_head %}{% endblock %}
|
||||||
<link rel="stylesheet" href="{% static '/css/membersystem.css' %}" type="text/css" />
|
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type="text/css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""URLs for the membersystem"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.urls import include
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
import debug_toolbar
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", login_required(views.index)),
|
||||||
|
path('accounts/', include('allauth.urls')),
|
||||||
|
path("admin/", admin.site.urls),
|
||||||
|
path("__debug__/", include(debug_toolbar.urls)),
|
||||||
|
]
|
|
@ -1 +0,0 @@
|
||||||
from django.contrib import admin # noqa
|
|
|
@ -1,5 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class UsersConfig(AppConfig):
|
|
||||||
name = "users"
|
|
|
@ -1,19 +0,0 @@
|
||||||
from django import forms
|
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
|
|
||||||
|
|
||||||
def get_confirm_code(email):
|
|
||||||
return default_token_generator(email)[:7]
|
|
||||||
|
|
||||||
|
|
||||||
class SignupForm(UserCreationForm):
|
|
||||||
|
|
||||||
username = forms.EmailField(label=_("Email"))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.User
|
|
||||||
fields = ("username",)
|
|
|
@ -1,73 +0,0 @@
|
||||||
# Generated by Django 2.2.4 on 2019-08-31 18:44
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [("auth", "0011_update_proxy_permissions")]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="User",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.AutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
|
||||||
(
|
|
||||||
"last_login",
|
|
||||||
models.DateTimeField(
|
|
||||||
blank=True, null=True, verbose_name="last login"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("nick", models.CharField(blank=True, max_length=60, null=True)),
|
|
||||||
(
|
|
||||||
"email",
|
|
||||||
models.EmailField(
|
|
||||||
help_text="Your email address will be used for password resets and notification about your event/submissions.",
|
|
||||||
max_length=254,
|
|
||||||
unique=True,
|
|
||||||
verbose_name="E-Mail",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("is_active", models.BooleanField(default=True)),
|
|
||||||
("is_staff", models.BooleanField(default=False)),
|
|
||||||
("is_superuser", models.BooleanField(default=False)),
|
|
||||||
("token_uuid", models.UUIDField(default=uuid.uuid4, editable=False)),
|
|
||||||
(
|
|
||||||
"groups",
|
|
||||||
models.ManyToManyField(
|
|
||||||
blank=True,
|
|
||||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
|
||||||
related_name="user_set",
|
|
||||||
related_query_name="user",
|
|
||||||
to="auth.Group",
|
|
||||||
verbose_name="groups",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"user_permissions",
|
|
||||||
models.ManyToManyField(
|
|
||||||
blank=True,
|
|
||||||
help_text="Specific permissions for this user.",
|
|
||||||
related_name="user_set",
|
|
||||||
related_query_name="user",
|
|
||||||
to="auth.Permission",
|
|
||||||
verbose_name="user permissions",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={"verbose_name": "User"},
|
|
||||||
)
|
|
||||||
]
|
|
|
@ -1,60 +0,0 @@
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.contrib.auth.base_user import BaseUserManager
|
|
||||||
from django.contrib.auth.models import AbstractBaseUser
|
|
||||||
from django.contrib.auth.models import PermissionsMixin
|
|
||||||
from django.db import models
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
|
||||||
"""The user manager class."""
|
|
||||||
|
|
||||||
def create_user(self, password: str = None, **kwargs):
|
|
||||||
user = self.model(**kwargs)
|
|
||||||
user.set_password(password)
|
|
||||||
user.save()
|
|
||||||
return user
|
|
||||||
|
|
||||||
def create_superuser(self, password: str, **kwargs):
|
|
||||||
user = self.create_user(password=password, **kwargs)
|
|
||||||
user.is_staff = True
|
|
||||||
user.is_superuser = True
|
|
||||||
user.save(update_fields=["is_staff", "is_superuser"])
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
class User(PermissionsMixin, AbstractBaseUser):
|
|
||||||
|
|
||||||
EMAIL_FIELD = "email"
|
|
||||||
USERNAME_FIELD = "email"
|
|
||||||
|
|
||||||
objects = UserManager()
|
|
||||||
|
|
||||||
nick = models.CharField(max_length=60, null=True, blank=True)
|
|
||||||
email = models.EmailField(
|
|
||||||
unique=True,
|
|
||||||
verbose_name=_("E-Mail"),
|
|
||||||
help_text=_(
|
|
||||||
"Your email address will be used for password resets and notification about your event/submissions."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
is_active = models.BooleanField(default=True)
|
|
||||||
|
|
||||||
# For the Django admin...
|
|
||||||
is_staff = models.BooleanField(default=False)
|
|
||||||
is_superuser = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
# Used for confirmations and password reminders to NOT disclose email in URL
|
|
||||||
token_uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
"""Use a useful string representation."""
|
|
||||||
return self.get_display_name()
|
|
||||||
|
|
||||||
def get_display_name(self) -> str:
|
|
||||||
return self.nick if self.nick else str(_("Unnamed user"))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("User")
|
|
|
@ -1,10 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<p>{% trans "Thanks for spending some quality time with the Web site today." %}</p>
|
|
||||||
|
|
||||||
<p><a href="{% url 'admin:index' %}">{% trans 'Log in again' %}</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,39 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<p>Your username and password didn't match. Please try again.</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if next %}
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<p>Your account doesn't have access to this page. To proceed,
|
|
||||||
please login with an account that has access.</p>
|
|
||||||
{% else %}
|
|
||||||
<p>Please login to see this page.</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'users:login' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>{{ form.username.label_tag }}</td>
|
|
||||||
<td>{{ form.username }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ form.password.label_tag }}</td>
|
|
||||||
<td>{{ form.password }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<input type="submit" value="login">
|
|
||||||
<input type="hidden" name="next" value="{{ next }}">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{# Assumes you setup the password_reset view in your URLconf #}
|
|
||||||
<p><a href="{% url 'users:password_reset' %}">Lost password?</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<p>{% trans 'Your password was changed.' %}</p>
|
|
||||||
{% endblock %}
|
|
|
@ -1,52 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n static %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}<div id="content-main">
|
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}
|
|
||||||
<div>
|
|
||||||
{% if form.errors %}
|
|
||||||
<p class="errornote">
|
|
||||||
{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
<p>{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}</p>
|
|
||||||
|
|
||||||
<fieldset class="module aligned wide">
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.old_password.errors }}
|
|
||||||
{{ form.old_password.label_tag }} {{ form.old_password }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.new_password1.errors }}
|
|
||||||
{{ form.new_password1.label_tag }} {{ form.new_password1 }}
|
|
||||||
{% if form.new_password1.help_text %}
|
|
||||||
<div class="help">{{ form.new_password1.help_text|safe }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
{{ form.new_password2.errors }}
|
|
||||||
{{ form.new_password2.label_tag }} {{ form.new_password2 }}
|
|
||||||
{% if form.new_password2.help_text %}
|
|
||||||
<div class="help">{{ form.new_password2.help_text|safe }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div class="submit-row">
|
|
||||||
<input type="submit" value="{% trans 'Change my password' %}" class="default">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</form></div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
|
|
||||||
|
|
||||||
<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,34 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n static %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if validlink %}
|
|
||||||
|
|
||||||
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
|
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}
|
|
||||||
<fieldset class="module aligned">
|
|
||||||
<div class="form-row field-password1">
|
|
||||||
{{ form.new_password1.errors }}
|
|
||||||
<label for="id_new_password1">{% trans 'New password:' %}</label>
|
|
||||||
{{ form.new_password1 }}
|
|
||||||
</div>
|
|
||||||
<div class="form-row field-password2">
|
|
||||||
{{ form.new_password2.errors }}
|
|
||||||
<label for="id_new_password2">{% trans 'Confirm password:' %}</label>
|
|
||||||
{{ form.new_password2 }}
|
|
||||||
</div>
|
|
||||||
<input type="submit" value="{% trans 'Change my password' %}">
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
|
|
||||||
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,12 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<p>{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}</p>
|
|
||||||
|
|
||||||
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{% load i18n %}{% autoescape off %}
|
|
||||||
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
|
|
||||||
|
|
||||||
{% trans "Please go to the following page and choose a new password:" %}
|
|
||||||
{% block reset_link %}
|
|
||||||
{{ protocol }}://{{ domain }}{% url 'users:password_reset_confirm' uidb64=uid token=token %}
|
|
||||||
{% endblock %}
|
|
||||||
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
|
|
||||||
|
|
||||||
{% trans "Thanks for using our site!" %}
|
|
||||||
|
|
||||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
|
||||||
|
|
||||||
{% endautoescape %}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n static %}
|
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
|
||||||
{% block content_title %}<h1>{{ title }}</h1>{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Forgotten password?" %}</h1>
|
|
||||||
|
|
||||||
<p>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</p>
|
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}
|
|
||||||
{{ form.email.errors }}
|
|
||||||
<label for="id_email">{% trans 'Email address:' %}</label>
|
|
||||||
{{ form.email }}
|
|
||||||
<input type="submit" value="{% trans 'Reset my password' %}">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,21 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Sign up" %}</h1>
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'users:signup' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_p }}
|
|
||||||
|
|
||||||
<p><button type="submit">{% trans "Confirm email..." %}</button></p>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p><a href="{% url 'users:login' %}">{% trans "Already have an account? Log in..." %}</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,10 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Confirm your email" %}</h1>
|
|
||||||
|
|
||||||
<p>{% trans "You've got mail - click the link or copy paste it to this browser session and you'll be logged in." %}</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,61 +0,0 @@
|
||||||
from django.contrib.auth import views as auth_views
|
|
||||||
from django.urls import path
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
app_name = "users"
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("signup/", views.SignupView.as_view(), name="signup"),
|
|
||||||
path("signup/confirm/", views.SignupConfirmView.as_view(), name="signup_confirm"),
|
|
||||||
path(
|
|
||||||
"login/",
|
|
||||||
auth_views.LoginView.as_view(template_name="users/login.html"),
|
|
||||||
name="login",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"logout/",
|
|
||||||
auth_views.LogoutView.as_view(template_name="users/logged_out.html"),
|
|
||||||
name="logout",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"password_change/",
|
|
||||||
views.PasswordChangeView.as_view(
|
|
||||||
template_name="users/password_change_form.html"
|
|
||||||
),
|
|
||||||
name="password_change",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"password_change/done/",
|
|
||||||
auth_views.PasswordChangeDoneView.as_view(
|
|
||||||
template_name="users/password_change_done.html"
|
|
||||||
),
|
|
||||||
name="password_change_done",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"password_reset/",
|
|
||||||
views.PasswordResetView.as_view(template_name="users/password_reset_form.html"),
|
|
||||||
name="password_reset",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"password_reset/done/",
|
|
||||||
auth_views.PasswordResetDoneView.as_view(
|
|
||||||
template_name="users/password_reset_done.html"
|
|
||||||
),
|
|
||||||
name="password_reset_done",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"reset/<uidb64>/<token>/",
|
|
||||||
views.PasswordResetConfirmView.as_view(
|
|
||||||
template_name="users/password_reset_confirm.html"
|
|
||||||
),
|
|
||||||
name="password_reset_confirm",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"reset/done/",
|
|
||||||
auth_views.PasswordResetCompleteView.as_view(
|
|
||||||
template_name="users/password_reset_complete.html"
|
|
||||||
),
|
|
||||||
name="password_reset_complete",
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,59 +0,0 @@
|
||||||
from django.contrib.auth import views as auth_views
|
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.urls.base import reverse_lazy
|
|
||||||
from django.views.generic.base import RedirectView
|
|
||||||
from django.views.generic.base import TemplateView
|
|
||||||
from django.views.generic.edit import FormView
|
|
||||||
|
|
||||||
from . import forms
|
|
||||||
|
|
||||||
# from . import email
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetView(auth_views.PasswordResetView):
|
|
||||||
email_template_name = "users/password_reset_email.html"
|
|
||||||
success_url = reverse_lazy("users:password_reset_done")
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
|
||||||
success_url = reverse_lazy("users:password_reset_complete")
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordChangeView(auth_views.PasswordChangeView):
|
|
||||||
success_url = reverse_lazy("users:password_change_done")
|
|
||||||
|
|
||||||
|
|
||||||
class SignupView(FormView):
|
|
||||||
|
|
||||||
template_name = "users/signup.html"
|
|
||||||
form_class = forms.SignupForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
|
|
||||||
user = form.save(commit=False)
|
|
||||||
user.is_active = False
|
|
||||||
user.set_password(form.cleaned_data["password1"])
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
# mail = email.UserConfirm(user=user)
|
|
||||||
# mail.send_with_feedback(success_msg=_("An email was sent with a confirmation link"))
|
|
||||||
|
|
||||||
self.request.session["user_confirm_pending_id"] = user.id
|
|
||||||
|
|
||||||
return redirect("users:signup_confirm")
|
|
||||||
|
|
||||||
|
|
||||||
class SignupConfirmView(TemplateView):
|
|
||||||
|
|
||||||
template_name = "users/signup_confirm.html"
|
|
||||||
|
|
||||||
|
|
||||||
class SignupConfirmRedirectView(RedirectView):
|
|
||||||
def get_redirect_url(self):
|
|
||||||
|
|
||||||
uuid = self.kwargs["uuid"]
|
|
||||||
|
|
||||||
if self.kwargs["token"] == forms.get_confirm_code(uuid):
|
|
||||||
redirect("users:confirmed") # TODO
|
|
||||||
|
|
||||||
redirect("users:confirm_nope") # TODO
|
|
Loading…
Reference in New Issue