Compare commits
No commits in common. "main" and "master" have entirely different histories.
|
@ -1,9 +0,0 @@
|
|||
*
|
||||
.*
|
||||
*/.*
|
||||
|
||||
!src/
|
||||
!requirements/
|
||||
!entrypoint.sh
|
||||
!pyproject.toml
|
||||
!README.md
|
20
.drone.yml
20
.drone.yml
|
@ -8,20 +8,26 @@ steps:
|
|||
image: plugins/docker
|
||||
environment:
|
||||
DJANGO_ENV: production
|
||||
BUILD: "${DRONE_COMMIT_SHA}"
|
||||
settings:
|
||||
repo: docker.data.coop/membersystem
|
||||
repo: docker.data.coop/member.data.coop
|
||||
registry: docker.data.coop
|
||||
username:
|
||||
from_secret: DOCKER_USERNAME
|
||||
password:
|
||||
from_secret: DOCKER_PASSWORD
|
||||
build_args_from_env:
|
||||
- DJANGO_ENV
|
||||
- BUILD
|
||||
tags:
|
||||
- "${DRONE_BUILD_NUMBER}"
|
||||
- "latest"
|
||||
- "vidir_refactor"
|
||||
when:
|
||||
branch:
|
||||
- main
|
||||
- vidir_refactor
|
||||
|
||||
# - name: notify
|
||||
# image: plugins/matrix
|
||||
# settings:
|
||||
# homeserver: https://data.coop
|
||||
# roomid: plKSghHbepWeUEtbHE:data.coop
|
||||
# username:
|
||||
# from_secret: matrix_username
|
||||
# password:
|
||||
# from_secret: matrix_password
|
||||
|
|
|
@ -5,6 +5,3 @@ db.sqlite3
|
|||
.pytest_cache
|
||||
.idea/
|
||||
*.mo
|
||||
.env
|
||||
venv/
|
||||
.venv/
|
||||
|
|
|
@ -1,37 +1,23 @@
|
|||
default_language_version:
|
||||
python: python3.12
|
||||
exclude: ^.*\b(migrations)\b.*$
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-merge-conflict
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- id: check-added-large-files
|
||||
- id: check-json
|
||||
- id: check-symlinks
|
||||
- id: check-toml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.3.0'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
- --fix
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.15.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
- --py311-plus
|
||||
exclude: migrations/
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.16.0
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args:
|
||||
- --target-version=5.0
|
||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: flake8
|
||||
args: [--max-line-length=120, --exclude=*/migrations/*]
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
- id: check-toml
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v1.6.1
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
types: [file, python]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: stable
|
||||
hooks:
|
||||
- id: black
|
||||
language: python
|
||||
types: [file, python]
|
||||
|
|
35
Dockerfile
35
Dockerfile
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.12-slim-bullseye
|
||||
FROM python:3.9-slim-buster
|
||||
|
||||
ENV PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
|
@ -6,18 +6,10 @@ ENV PYTHONFAULTHANDLER=1 \
|
|||
PYTHONHASHSEED=random \
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100
|
||||
ARG DJANGO_ENV
|
||||
ARG BUILD
|
||||
ENV BUILD ${BUILD}
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
POETRY_VERSION=1.1.4
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN groupadd -g 1000 www && useradd -u 1000 -ms /bin/bash -g www www
|
||||
COPY --chown=www:www . .
|
||||
RUN mkdir /app/src/static \
|
||||
&& chown www:www /app/src/static \
|
||||
&& apt-get update \
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
binutils \
|
||||
libpq-dev \
|
||||
|
@ -30,8 +22,23 @@ RUN mkdir /app/src/static \
|
|||
libffi-dev \
|
||||
shared-mime-info \
|
||||
gettext \
|
||||
&& pip install . \
|
||||
&& django-admin compilemessages
|
||||
&& pip install "poetry==$POETRY_VERSION"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY poetry.lock pyproject.toml /app/
|
||||
|
||||
ARG DJANGO_ENV
|
||||
|
||||
RUN poetry export -f requirements.txt $(test "$DJANGO_ENV" != production && echo "--dev") | pip install -r /dev/stdin
|
||||
|
||||
RUN groupadd -g 1000 www
|
||||
RUN useradd -u 1000 -ms /bin/bash -g www www
|
||||
COPY --chown=www:www ./ /app/
|
||||
RUN mkdir /app/src/static && chown www:www /app/src/static
|
||||
|
||||
ARG BUILD
|
||||
ENV BUILD ${BUILD}
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
||||
|
|
59
Makefile
59
Makefile
|
@ -1,58 +1,39 @@
|
|||
DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose
|
||||
DOCKER_RUN = ${DOCKER_COMPOSE} run -u `id -u`
|
||||
DOCKER_BUILD = DOCKER_BUILDKIT=1 docker build
|
||||
DOCKER_CONTAINER_NAME = backend
|
||||
MANAGE_EXEC = python /app/src/manage.py
|
||||
MANAGE_COMMAND = ${DOCKER_RUN} ${DOCKER_CONTAINER_NAME} ${MANAGE_EXEC}
|
||||
DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose
|
||||
DOCKER_RUN = ${DOCKER_COMPOSE} run -e UID=`id -u` -e GID=`id -g`
|
||||
MANAGE = ${DOCKER_RUN} backend python /app/src/manage.py
|
||||
|
||||
init: setup_venv pre_commit_install migrate
|
||||
lint:
|
||||
poetry run pre-commit run --all
|
||||
|
||||
run:
|
||||
${DOCKER_COMPOSE} up
|
||||
${DOCKER_COMPOSE} up --build
|
||||
|
||||
setup_venv:
|
||||
rm -rf venv
|
||||
python3.11 -m venv venv;
|
||||
venv/bin/python -m pip install wheel setuptools;
|
||||
venv/bin/python -m pip install pre-commit boto3 pip-tools;
|
||||
build:
|
||||
${DOCKER_COMPOSE} build
|
||||
|
||||
pre_commit_install:
|
||||
venv/bin/pre-commit install
|
||||
|
||||
pre_commit_run_all:
|
||||
venv/bin/pre-commit run --all-files
|
||||
makemessages:
|
||||
${MANAGE} makemessages -a
|
||||
|
||||
makemigrations:
|
||||
${MANAGE_COMMAND} makemigrations ${ARGS}
|
||||
${MANAGE} makemigrations ${EXTRA_ARGS}
|
||||
|
||||
migrate:
|
||||
${MANAGE_COMMAND} migrate ${ARGS}
|
||||
${MANAGE} migrate ${EXTRA_ARGS}
|
||||
|
||||
createsuperuser:
|
||||
${MANAGE_COMMAND} createsuperuser
|
||||
${MANAGE} createsuperuser
|
||||
|
||||
shell:
|
||||
${MANAGE_COMMAND} shell
|
||||
${MANAGE} shell
|
||||
|
||||
manage_command:
|
||||
${MANAGE_COMMAND} ${ARGS}
|
||||
${MANAGE} ${COMMAND}
|
||||
|
||||
test:
|
||||
${DOCKER_RUN} backend pytest src/
|
||||
|
||||
add_dependency:
|
||||
${DOCKER_RUN} ${DOCKER_CONTAINER_NAME} poetry add --lock ${DEPENDENCY}
|
||||
${DOCKER_RUN} backend poetry add --lock ${DEPENDENCY}
|
||||
|
||||
add_dev_dependency:
|
||||
${DOCKER_RUN} ${DOCKER_CONTAINER_NAME} poetry add -D --lock ${DEPENDENCY}
|
||||
|
||||
poetry_lock:
|
||||
${DOCKER_RUN} ${DOCKER_CONTAINER_NAME} poetry lock --no-update
|
||||
|
||||
poetry_command:
|
||||
${DOCKER_RUN} ${DOCKER_CONTAINER_NAME} poetry ${COMMAND}
|
||||
|
||||
build_dev_docker_image: compile_requirements
|
||||
${DOCKER_COMPOSE} build ${DOCKER_CONTAINER_NAME}
|
||||
|
||||
compile_requirements:
|
||||
./venv/bin/pip-compile --output-file requirements/base.txt requirements/base.in
|
||||
./venv/bin/pip-compile --output-file requirements/test.txt requirements/test.in
|
||||
./venv/bin/pip-compile --output-file requirements/dev.txt requirements/dev.in
|
||||
${DOCKER_RUN} backend poetry add -D --lock ${DEPENDENCY}
|
||||
|
|
40
README.md
40
README.md
|
@ -1,22 +1,12 @@
|
|||
# member.data.coop
|
||||
|
||||
## Development
|
||||
|
||||
### Setup environment
|
||||
|
||||
Copy over the .env.example file to .env and adjust DATABASE_URL accordingly
|
||||
|
||||
$ cp .env.example .env
|
||||
|
||||
### Docker
|
||||
|
||||
#### Requirements
|
||||
## Development requirements
|
||||
|
||||
- Docker
|
||||
- Docker compose
|
||||
- pre-commit (preferred for contributions)
|
||||
|
||||
#### Setup
|
||||
## Start local server
|
||||
|
||||
Given that the requirements above are installed, it should be as easy as:
|
||||
|
||||
|
@ -43,29 +33,3 @@ Make messages:
|
|||
Running tests:
|
||||
|
||||
$ make test
|
||||
|
||||
### Non-docker
|
||||
|
||||
Create a venv
|
||||
|
||||
$ python3 -m venv venv
|
||||
|
||||
Activate the venv
|
||||
|
||||
$ source venv/bin/activate
|
||||
|
||||
Install requirements
|
||||
|
||||
$ pip install -r requirements/dev.txt
|
||||
|
||||
Run migrations
|
||||
|
||||
$ ./src/manage.py migrate
|
||||
|
||||
Create a superuser
|
||||
|
||||
$ ./src/manage.py createsuperuser
|
||||
|
||||
Run the server
|
||||
|
||||
$ ./src/manage.py runserver
|
||||
|
|
241
devenv.lock
241
devenv.lock
|
@ -1,241 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1707004164,
|
||||
"narHash": "sha256-9Hr8onWtvLk5A8vCEkaE9kxA0D7PR62povFokM1oL5Q=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "0e68853bb27981a4ffd7a7225b59ed84f7180fc7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "flake-utils",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1703887061,
|
||||
"narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1707451808,
|
||||
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-python": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1707114737,
|
||||
"narHash": "sha256-ZXqv2epXAjDjfWbYn+yy4VOmW+C7SuUBoiZkkDoSqA4=",
|
||||
"owner": "cachix",
|
||||
"repo": "nixpkgs-python",
|
||||
"rev": "f34ed02276bc08fe1c91c1bf0ef3589d68028878",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "nixpkgs-python",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1704874635,
|
||||
"narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1707347730,
|
||||
"narHash": "sha256-0etC/exQIaqC9vliKhc3eZE2Mm2wgLa0tj93ZF/egvM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6832d0d99649db3d65a0e15fa51471537b2c56a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1707297608,
|
||||
"narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "0db2e67ee49910adfa13010e7f012149660af7f0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-python": "nixpkgs-python",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
19
devenv.nix
19
devenv.nix
|
@ -1,19 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
languages.python.enable = true;
|
||||
languages.python.version = "3.12";
|
||||
|
||||
services.postgres = {
|
||||
enable = true;
|
||||
package = pkgs.postgresql_15;
|
||||
initialDatabases = [ {"name" = "postgres";} ];
|
||||
listen_addresses = "localhost";
|
||||
initialScript = "create user postgres with password 'postgres' superuser;";
|
||||
};
|
||||
|
||||
processes = {
|
||||
app.exec = "while ! pg_isready -d postgres -h localhost -U postgres 2>/dev/null; do sleep 1; done; hatch run server";
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
inputs:
|
||||
nixpkgs:
|
||||
url: github:NixOS/nixpkgs/nixpkgs-unstable
|
||||
nixpkgs-python:
|
||||
url: github:cachix/nixpkgs-python
|
|
@ -3,19 +3,21 @@ version: '3.7'
|
|||
services:
|
||||
|
||||
backend:
|
||||
image: data_coop_membersystem:dev
|
||||
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/
|
||||
depends_on:
|
||||
links:
|
||||
- redis
|
||||
- postgres
|
||||
env_file:
|
||||
- .env
|
||||
- env
|
||||
|
||||
postgres:
|
||||
image: postgres:13-alpine
|
||||
|
@ -24,7 +26,12 @@ services:
|
|||
ports:
|
||||
- 5432:5432
|
||||
env_file:
|
||||
- .env
|
||||
- env
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
|
@ -3,7 +3,5 @@ POSTGRES_HOST=postgres
|
|||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_PORT=5432
|
||||
DATABASE_URL=postgres://postgres:postgres@postgres:5432/postgres
|
||||
# Use something along the the following if you are not using docker
|
||||
# DATABASE_URL=postgres://postgres:postgres@localhost:5432/datacoop_membersystem
|
||||
DEBUG=True
|
||||
DJANGO_ENV=development
|
||||
DJANGO_ENV=all
|
|
@ -0,0 +1,908 @@
|
|||
[[package]]
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.3.1"
|
||||
description = "ASGI specs, helper code, and adapters"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest", "pytest-asyncio"]
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "20.3.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
|
||||
docs = ["furo", "sphinx", "zope.interface"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2020.12.5"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.14.5"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.2.0"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "4.0.0"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "7.1.2"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "3.4.6"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
cffi = ">=1.12"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
|
||||
docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
|
||||
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
|
||||
sdist = ["setuptools-rust (>=0.11.4)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "defusedxml"
|
||||
version = "0.6.0"
|
||||
description = "XML bomb protection for Python stdlib modules"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.1"
|
||||
description = "Distribution utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "dj-database-url"
|
||||
version = "0.5.0"
|
||||
description = "Use Database URLs in your Django Application."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "dj-email-url"
|
||||
version = "1.0.2"
|
||||
description = "Use an URL to configure email backend settings in your Django Application."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "3.1.7"
|
||||
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
asgiref = ">=3.2.10,<4"
|
||||
pytz = "*"
|
||||
sqlparse = ">=0.2.2"
|
||||
|
||||
[package.extras]
|
||||
argon2 = ["argon2-cffi (>=16.1.0)"]
|
||||
bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "django-allauth"
|
||||
version = "0.44.0"
|
||||
description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=2.0"
|
||||
pyjwt = {version = ">=1.7", extras = ["crypto"]}
|
||||
python3-openid = ">=3.0.8"
|
||||
requests = "*"
|
||||
requests-oauthlib = ">=0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "django-cache-url"
|
||||
version = "3.2.3"
|
||||
description = "Use Cache URLs in your Django application."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "django-debug-toolbar"
|
||||
version = "3.2"
|
||||
description = "A configurable set of panels that display various debug information about the current request/response."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=2.2"
|
||||
sqlparse = ">=0.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "django-money"
|
||||
version = "1.3.1"
|
||||
description = "Adds support for using money and currency fields in django models and forms. Uses py-moneyed as the money implementation."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=1.11"
|
||||
py-moneyed = ">=0.8,<1.0"
|
||||
|
||||
[package.extras]
|
||||
exchange = ["certifi"]
|
||||
test = ["pytest (>=3.1.0)", "pytest-django", "pytest-pythonpath", "pytest-cov", "mixer"]
|
||||
|
||||
[[package]]
|
||||
name = "environs"
|
||||
version = "9.3.1"
|
||||
description = "simplified environment variable parsing"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
dj-database-url = {version = "*", optional = true, markers = "extra == \"django\""}
|
||||
dj-email-url = {version = "*", optional = true, markers = "extra == \"django\""}
|
||||
django-cache-url = {version = "*", optional = true, markers = "extra == \"django\""}
|
||||
marshmallow = ">=2.7.0"
|
||||
python-dotenv = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "mypy (==0.800)", "pre-commit (>=2.4,<3.0)", "tox"]
|
||||
django = ["dj-database-url", "dj-email-url", "django-cache-url"]
|
||||
lint = ["flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "mypy (==0.800)", "pre-commit (>=2.4,<3.0)"]
|
||||
tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.0.12"
|
||||
description = "A platform independent file lock."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.12.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.0.0"
|
||||
description = "File identification library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.extras]
|
||||
license = ["editdistance"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "2.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "marshmallow"
|
||||
version = "3.10.0"
|
||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"]
|
||||
docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"]
|
||||
lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"]
|
||||
tests = ["pytest", "pytz", "simplejson"]
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "8.7.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.5.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.1.0"
|
||||
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.extras]
|
||||
rsa = ["cryptography"]
|
||||
signals = ["blinker"]
|
||||
signedtoken = ["cryptography", "pyjwt (>=1.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "20.9"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "0.13.1"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "2.10.1"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
cfgv = ">=2.0.0"
|
||||
identify = ">=1.0.0"
|
||||
nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
toml = "*"
|
||||
virtualenv = ">=20.0.8"
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2"
|
||||
version = "2.8.6"
|
||||
description = "psycopg2 - Python-PostgreSQL Database Adapter"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "py-moneyed"
|
||||
version = "0.8.0"
|
||||
description = "Provides Currency and Money classes for use in your Python code."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest (>=2.3.0)", "tox (>=1.6.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.20"
|
||||
description = "C parser in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.0.1"
|
||||
description = "JSON Web Token implementation in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
cryptography = {version = ">=3.3.1,<4.0.0", optional = true, markers = "extra == \"crypto\""}
|
||||
|
||||
[package.extras]
|
||||
crypto = ["cryptography (>=3.3.1,<4.0.0)"]
|
||||
dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
|
||||
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
|
||||
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "2.4.7"
|
||||
description = "Python parsing module"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "5.4.3"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=17.4.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
more-itertools = ">=4.0.0"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0"
|
||||
py = ">=1.5.0"
|
||||
wcwidth = "*"
|
||||
|
||||
[package.extras]
|
||||
checkqa-mypy = ["mypy (==v0.761)"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-django"
|
||||
version = "3.10.0"
|
||||
description = "A Django plugin for pytest."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "sphinx-rtd-theme"]
|
||||
testing = ["django", "django-configurations (>=2.0)", "six"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "0.15.0"
|
||||
description = "Add .env support to your django/flask apps in development and deployments"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python3-openid"
|
||||
version = "3.2.0"
|
||||
description = "OpenID support for modern servers and consumers."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
defusedxml = "*"
|
||||
|
||||
[package.extras]
|
||||
mysql = ["mysql-connector-python"]
|
||||
postgresql = ["psycopg2"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2021.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "5.4.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.25.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
chardet = ">=3.0.2,<5"
|
||||
idna = ">=2.5,<3"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "1.3.0"
|
||||
description = "OAuthlib authentication support for Requests."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
oauthlib = ">=3.0.0"
|
||||
requests = ">=2.0.0"
|
||||
|
||||
[package.extras]
|
||||
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.15.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.4.1"
|
||||
description = "A non-validating SQL parser."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.3"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.13.4"
|
||||
description = "The lightning-fast ASGI server."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=7.0.0,<8.0.0"
|
||||
h11 = ">=0.8"
|
||||
|
||||
[package.extras]
|
||||
standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.4.2"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = ">=1.4.3,<2"
|
||||
distlib = ">=0.3.1,<1"
|
||||
filelock = ">=3.0.0,<4"
|
||||
six = ">=1.9.0,<2"
|
||||
|
||||
[package.extras]
|
||||
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
|
||||
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.5"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "whitenoise"
|
||||
version = "5.2.0"
|
||||
description = "Radically simplified static file serving for WSGI applications"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "273466dbd53a484027b72a712aba600936507cd2d528c9a245464637c0c54207"
|
||||
|
||||
[metadata.files]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||
]
|
||||
asgiref = [
|
||||
{file = "asgiref-3.3.1-py3-none-any.whl", hash = "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17"},
|
||||
{file = "asgiref-3.3.1.tar.gz", hash = "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
|
||||
{file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
|
||||
{file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
|
||||
{file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
|
||||
{file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
|
||||
]
|
||||
cfgv = [
|
||||
{file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"},
|
||||
{file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"},
|
||||
]
|
||||
chardet = [
|
||||
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
|
||||
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-3.4.6-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-win32.whl", hash = "sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2"},
|
||||
{file = "cryptography-3.4.6-cp36-abi3-win_amd64.whl", hash = "sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0"},
|
||||
{file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b"},
|
||||
{file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df"},
|
||||
{file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336"},
|
||||
{file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724"},
|
||||
{file = "cryptography-3.4.6.tar.gz", hash = "sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87"},
|
||||
]
|
||||
defusedxml = [
|
||||
{file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"},
|
||||
{file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"},
|
||||
]
|
||||
distlib = [
|
||||
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
|
||||
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
|
||||
]
|
||||
dj-database-url = [
|
||||
{file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"},
|
||||
{file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"},
|
||||
]
|
||||
dj-email-url = [
|
||||
{file = "dj-email-url-1.0.2.tar.gz", hash = "sha256:838fd4ded9deba53ae757debef431e25fa7fca31d3948b3c4808ccdc84fab2b7"},
|
||||
{file = "dj_email_url-1.0.2-py2.py3-none-any.whl", hash = "sha256:15148141c6ef123636e4ca3663e95231ed94ca5ed267e91977e5a4397be8b34c"},
|
||||
]
|
||||
django = [
|
||||
{file = "Django-3.1.7-py3-none-any.whl", hash = "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"},
|
||||
{file = "Django-3.1.7.tar.gz", hash = "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7"},
|
||||
]
|
||||
django-allauth = [
|
||||
{file = "django-allauth-0.44.0.tar.gz", hash = "sha256:e51af457466022f52154d74c8523ac69375120fad2acce6e239635d85e610b25"},
|
||||
]
|
||||
django-cache-url = [
|
||||
{file = "django-cache-url-3.2.3.tar.gz", hash = "sha256:c1d45626ae8a206267c1263aa7a3461e2e186be2e939bcbd8c660e25851ddac8"},
|
||||
{file = "django_cache_url-3.2.3-py2.py3-none-any.whl", hash = "sha256:5514ca3a2075c6b956b3d0a5c540654d32b004e76340d7bdabf6661135b5f218"},
|
||||
]
|
||||
django-debug-toolbar = [
|
||||
{file = "django-debug-toolbar-3.2.tar.gz", hash = "sha256:84e2607d900dbd571df0a2acf380b47c088efb787dce9805aefeb407341961d2"},
|
||||
{file = "django_debug_toolbar-3.2-py3-none-any.whl", hash = "sha256:9e5a25d0c965f7e686f6a8ba23613ca9ca30184daa26487706d4829f5cfb697a"},
|
||||
]
|
||||
django-money = [
|
||||
{file = "django-money-1.3.1.tar.gz", hash = "sha256:a363ce16a23e403befdafa9895b2f538a10f9d390b160f12140094a6dfd55246"},
|
||||
{file = "django_money-1.3.1-py3-none-any.whl", hash = "sha256:3b8fc751c8ae27cf877b8f3770ade1b63af97ee49a32ac08a6a1bc6d8d59f089"},
|
||||
]
|
||||
environs = [
|
||||
{file = "environs-9.3.1-py2.py3-none-any.whl", hash = "sha256:2da44b7c30114415aa858577fa6396ee326fc76a0a60f0f15e8260ba554f19dc"},
|
||||
{file = "environs-9.3.1.tar.gz", hash = "sha256:3f6def554abb5455141b540e6e0b72fda3853404f2b0d31658aab1bf95410db3"},
|
||||
]
|
||||
filelock = [
|
||||
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
|
||||
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
|
||||
]
|
||||
h11 = [
|
||||
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||
]
|
||||
identify = [
|
||||
{file = "identify-2.0.0-py2.py3-none-any.whl", hash = "sha256:9cdd81e5d2b6e76c3006d5226316dd947bd6324fbeebb881bec489202fa09d3a"},
|
||||
{file = "identify-2.0.0.tar.gz", hash = "sha256:b99aa309329c4fea679463eb35d169f3fbe13e66e9dd6162ad1856cbeb03dcbd"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
]
|
||||
marshmallow = [
|
||||
{file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"},
|
||||
{file = "marshmallow-3.10.0.tar.gz", hash = "sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7"},
|
||||
]
|
||||
more-itertools = [
|
||||
{file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"},
|
||||
{file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"},
|
||||
]
|
||||
nodeenv = [
|
||||
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
|
||||
{file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"},
|
||||
]
|
||||
oauthlib = [
|
||||
{file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"},
|
||||
{file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
||||
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
]
|
||||
pre-commit = [
|
||||
{file = "pre_commit-2.10.1-py2.py3-none-any.whl", hash = "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e"},
|
||||
{file = "pre_commit-2.10.1.tar.gz", hash = "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a"},
|
||||
]
|
||||
psycopg2 = [
|
||||
{file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"},
|
||||
{file = "psycopg2-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5"},
|
||||
{file = "psycopg2-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad"},
|
||||
{file = "psycopg2-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3"},
|
||||
{file = "psycopg2-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821"},
|
||||
{file = "psycopg2-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301"},
|
||||
{file = "psycopg2-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a"},
|
||||
{file = "psycopg2-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d"},
|
||||
{file = "psycopg2-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84"},
|
||||
{file = "psycopg2-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5"},
|
||||
{file = "psycopg2-2.8.6-cp38-cp38-win32.whl", hash = "sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e"},
|
||||
{file = "psycopg2-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051"},
|
||||
{file = "psycopg2-2.8.6-cp39-cp39-win32.whl", hash = "sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3"},
|
||||
{file = "psycopg2-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7"},
|
||||
{file = "psycopg2-2.8.6.tar.gz", hash = "sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
||||
]
|
||||
py-moneyed = [
|
||||
{file = "py-moneyed-0.8.0.tar.gz", hash = "sha256:ec73795171919d537880a33c44d07fcdf0a5225e8368684fe02f0e75a6404742"},
|
||||
{file = "py_moneyed-0.8.0-py2.py3-none-any.whl", hash = "sha256:c6691b914a5e4b5b2335cf113620479a52cc82988c0e143435a7c5c7d60cd4ad"},
|
||||
]
|
||||
pycparser = [
|
||||
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||
]
|
||||
pyjwt = [
|
||||
{file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"},
|
||||
{file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
|
||||
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
|
||||
]
|
||||
pytest-django = [
|
||||
{file = "pytest-django-3.10.0.tar.gz", hash = "sha256:4de6dbd077ed8606616958f77655fed0d5e3ee45159475671c7fa67596c6dba6"},
|
||||
{file = "pytest_django-3.10.0-py2.py3-none-any.whl", hash = "sha256:c33e3d3da14d8409b125d825d4e74da17bb252191bf6fc3da6856e27a8b73ea4"},
|
||||
]
|
||||
python-dotenv = [
|
||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
||||
]
|
||||
python3-openid = [
|
||||
{file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"},
|
||||
{file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"},
|
||||
]
|
||||
pytz = [
|
||||
{file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
|
||||
{file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
|
||||
]
|
||||
pyyaml = [
|
||||
{file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
|
||||
{file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
|
||||
{file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
|
||||
{file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
|
||||
{file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
|
||||
{file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
|
||||
{file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
|
||||
{file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
|
||||
{file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
|
||||
{file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
|
||||
{file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
|
||||
{file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
|
||||
{file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
|
||||
{file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
|
||||
{file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
|
||||
{file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
|
||||
{file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
|
||||
{file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
|
||||
{file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
|
||||
{file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
|
||||
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||
]
|
||||
requests-oauthlib = [
|
||||
{file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
|
||||
{file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
|
||||
{file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
||||
]
|
||||
sqlparse = [
|
||||
{file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"},
|
||||
{file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
|
||||
{file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
|
||||
]
|
||||
uvicorn = [
|
||||
{file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"},
|
||||
{file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"},
|
||||
]
|
||||
virtualenv = [
|
||||
{file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"},
|
||||
{file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"},
|
||||
]
|
||||
wcwidth = [
|
||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||
]
|
||||
whitenoise = [
|
||||
{file = "whitenoise-5.2.0-py2.py3-none-any.whl", hash = "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d"},
|
||||
{file = "whitenoise-5.2.0.tar.gz", hash = "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7"},
|
||||
]
|
147
pyproject.toml
147
pyproject.toml
|
@ -1,130 +1,25 @@
|
|||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
[tool.poetry]
|
||||
name = "membersystem"
|
||||
description = ''
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
keywords = []
|
||||
authors = [
|
||||
{ name = "Víðir Valberg Guðmundsson", email = "valberg@orn.li" },
|
||||
]
|
||||
dependencies = [
|
||||
"Django==5.0.1",
|
||||
"django-money==3.4.1",
|
||||
"django-allauth==0.60.0",
|
||||
"psycopg[binary]==3.1.16",
|
||||
"environs[django]==10.0.0",
|
||||
"uvicorn==0.25.0",
|
||||
"whitenoise==6.6.0",
|
||||
"django-zen-queries==2.1.0",
|
||||
"django-registries==0.0.3",
|
||||
"django-view-decorator==0.0.4",
|
||||
"django-oauth-toolkit==2.3.0",
|
||||
]
|
||||
version = "0.0.1"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src"]
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
Django = "^3.1"
|
||||
django-money = "^1.3"
|
||||
django-allauth = "^0.44.0"
|
||||
psycopg2 = "^2.8.6"
|
||||
environs = {extras = ["django"], version = "^9.3.1"}
|
||||
uvicorn = "^0.13.4"
|
||||
whitenoise = "^5.2.0"
|
||||
|
||||
[tool.hatch.envs.default]
|
||||
dependencies = [
|
||||
"coverage[toml]==7.3.0",
|
||||
"pytest==7.2.2",
|
||||
"pytest-cov",
|
||||
"pytest-django==4.5.2",
|
||||
"mypy==1.1.1",
|
||||
"django-stubs==1.16.0",
|
||||
"pip-tools==7.3.0",
|
||||
"django-debug-toolbar==4.2.0",
|
||||
"django-browser-reload==1.7.0",
|
||||
"model-bakery==1.17.0",
|
||||
]
|
||||
[tool.poetry.dev-dependencies]
|
||||
pre-commit = "^2.9.3"
|
||||
pytest = "^5.1"
|
||||
pytest-django = "^3.5"
|
||||
django-debug-toolbar = "^3.2"
|
||||
|
||||
[[tool.hatch.envs.tests.matrix]]
|
||||
python = ["3.12"]
|
||||
django = ["4.2", "5.0"]
|
||||
|
||||
[tool.hatch.envs.tests.overrides]
|
||||
matrix.django.dependencies = [
|
||||
{ value = "django~={matrix:django}" },
|
||||
]
|
||||
matrix.python.dependencies = [
|
||||
{ value = "typing_extensions==4.5.0", if = ["3.10"]},
|
||||
]
|
||||
|
||||
[tool.hatch.envs.default.scripts]
|
||||
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src --cov=tests --cov=append {args}"
|
||||
no-cov = "cov --no-cov {args}"
|
||||
typecheck = "mypy --config-file=pyproject.toml ."
|
||||
requirements = "pip-compile --output-file requirements/base.txt pyproject.toml"
|
||||
server = "./src/manage.py runserver 0.0.0.0:8000"
|
||||
migrate = "./src/manage.py migrate"
|
||||
makemigrations = "./src/manage.py makemigrations"
|
||||
createsuperuser = "./src/manage.py createsuperuser"
|
||||
shell = "./src/manage.py shell"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
DJANGO_SETTINGS_MODULE="tests.settings"
|
||||
addopts = "--reuse-db"
|
||||
norecursedirs = "build dist docs .eggs/* *.egg-info htmlcov .git"
|
||||
python_files = "test*.py"
|
||||
testpaths = "tests"
|
||||
pythonpath = ". tests"
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
parallel = true
|
||||
|
||||
[tool.coverage.report]
|
||||
exclude_lines = [
|
||||
"no cov",
|
||||
"if __name__ == .__main__.:",
|
||||
"if TYPE_CHECKING:",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
mypy_path = "src/"
|
||||
exclude = [
|
||||
"venv/",
|
||||
"dist/",
|
||||
"docs/",
|
||||
]
|
||||
namespace_packages = false
|
||||
show_error_codes = true
|
||||
strict = true
|
||||
warn_unreachable = true
|
||||
follow_imports = "normal"
|
||||
#plugins = ["mypy_django_plugin.main"]
|
||||
|
||||
[tool.django-stubs]
|
||||
#django_settings_module = "tests.settings"
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "tests.*"
|
||||
allow_untyped_defs = true
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
extend-exclude = [
|
||||
".git",
|
||||
"__pycache__",
|
||||
]
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ALL"]
|
||||
ignore = [
|
||||
"G004", # Logging statement uses f-string
|
||||
"ANN101", # Missing type annotation for `self` in method
|
||||
"ANN102", # Missing type annotation for `cls` in classmethod
|
||||
"EM101", # Exception must not use a string literal, assign to variable first
|
||||
"EM102", # Exception must not use a f-string literal, assign to variable first
|
||||
"COM812", # missing-trailing-comma (https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules)
|
||||
"ISC001", # single-line-implicit-string-concatenation (https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules)
|
||||
]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
force-single-line = true
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Django==5.0.1
|
||||
django-money==3.4.1
|
||||
django-allauth==0.60.0
|
||||
psycopg[binary]==3.1.16
|
||||
environs[django]==10.0.0
|
||||
uvicorn==0.25.0
|
||||
whitenoise==6.6.0
|
||||
django-zen-queries==2.1.0
|
|
@ -1,101 +0,0 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/base.txt pyproject.toml
|
||||
#
|
||||
asgiref==3.7.2
|
||||
# via django
|
||||
babel==2.14.0
|
||||
# via py-moneyed
|
||||
certifi==2023.11.17
|
||||
# via requests
|
||||
cffi==1.16.0
|
||||
# via cryptography
|
||||
charset-normalizer==3.3.2
|
||||
# via requests
|
||||
click==8.1.7
|
||||
# via uvicorn
|
||||
cryptography==41.0.7
|
||||
# via pyjwt
|
||||
defusedxml==0.7.1
|
||||
# via python3-openid
|
||||
dj-database-url==2.1.0
|
||||
# via environs
|
||||
dj-email-url==1.0.6
|
||||
# via environs
|
||||
django==5.0.1
|
||||
# via
|
||||
# dj-database-url
|
||||
# django-allauth
|
||||
# django-money
|
||||
# django-registries
|
||||
# django-view-decorator
|
||||
# django-zen-queries
|
||||
# membersystem (pyproject.toml)
|
||||
django-allauth==0.60.0
|
||||
# via membersystem (pyproject.toml)
|
||||
django-cache-url==3.4.5
|
||||
# via environs
|
||||
django-money==3.4.1
|
||||
# via membersystem (pyproject.toml)
|
||||
django-registries==0.0.3
|
||||
# via membersystem (pyproject.toml)
|
||||
django-view-decorator==0.0.4
|
||||
# via membersystem (pyproject.toml)
|
||||
django-zen-queries==2.1.0
|
||||
# via membersystem (pyproject.toml)
|
||||
environs[django]==10.0.0
|
||||
# via
|
||||
# environs
|
||||
# membersystem (pyproject.toml)
|
||||
h11==0.14.0
|
||||
# via uvicorn
|
||||
idna==3.6
|
||||
# via requests
|
||||
marshmallow==3.20.1
|
||||
# via environs
|
||||
oauthlib==3.2.2
|
||||
# via requests-oauthlib
|
||||
packaging==23.2
|
||||
# via marshmallow
|
||||
psycopg[binary]==3.1.16
|
||||
# via
|
||||
# membersystem (pyproject.toml)
|
||||
# psycopg
|
||||
psycopg-binary==3.1.16
|
||||
# via psycopg
|
||||
py-moneyed==3.0
|
||||
# via django-money
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via
|
||||
# django-allauth
|
||||
# pyjwt
|
||||
python-dotenv==1.0.0
|
||||
# via environs
|
||||
python3-openid==3.2.0
|
||||
# via django-allauth
|
||||
requests==2.31.0
|
||||
# via
|
||||
# django-allauth
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via django-allauth
|
||||
sqlparse==0.4.4
|
||||
# via django
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# dj-database-url
|
||||
# psycopg
|
||||
# py-moneyed
|
||||
urllib3==2.1.0
|
||||
# via requests
|
||||
uvicorn==0.25.0
|
||||
# via membersystem (pyproject.toml)
|
||||
whitenoise==6.6.0
|
||||
# via membersystem (pyproject.toml)
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
|
@ -1,8 +0,0 @@
|
|||
-r test.txt
|
||||
|
||||
django-browser-reload==1.12.1
|
||||
django-debug-toolbar==4.2.0
|
||||
django-extensions==3.2.3
|
||||
django-stubs==4.2.7
|
||||
ipython==8.19.0
|
||||
mypy==1.8.0
|
|
@ -1,213 +0,0 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/dev.txt requirements/dev.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django
|
||||
# django-browser-reload
|
||||
asttokens==2.4.1
|
||||
# via stack-data
|
||||
babel==2.14.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# py-moneyed
|
||||
certifi==2023.11.17
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
cffi==1.16.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# cryptography
|
||||
charset-normalizer==3.3.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# uvicorn
|
||||
coverage==7.4.0
|
||||
# via -r requirements/test.txt
|
||||
cryptography==41.0.7
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# pyjwt
|
||||
decorator==5.1.1
|
||||
# via ipython
|
||||
defusedxml==0.7.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# python3-openid
|
||||
dj-database-url==2.1.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
dj-email-url==1.0.6
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
django==5.0.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# dj-database-url
|
||||
# django-allauth
|
||||
# django-browser-reload
|
||||
# django-debug-toolbar
|
||||
# django-extensions
|
||||
# django-money
|
||||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# django-zen-queries
|
||||
django-allauth==0.60.0
|
||||
# via -r requirements/test.txt
|
||||
django-browser-reload==1.12.1
|
||||
# via -r requirements/dev.in
|
||||
django-cache-url==3.4.5
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
django-debug-toolbar==4.2.0
|
||||
# via -r requirements/dev.in
|
||||
django-extensions==3.2.3
|
||||
# via -r requirements/dev.in
|
||||
django-money==3.4.1
|
||||
# via -r requirements/test.txt
|
||||
django-stubs==4.2.7
|
||||
# via -r requirements/dev.in
|
||||
django-stubs-ext==4.2.7
|
||||
# via django-stubs
|
||||
django-zen-queries==2.1.0
|
||||
# via -r requirements/test.txt
|
||||
environs[django]==10.0.0
|
||||
# via -r requirements/test.txt
|
||||
executing==2.0.1
|
||||
# via stack-data
|
||||
h11==0.14.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# uvicorn
|
||||
idna==3.6
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
ipython==8.19.0
|
||||
# via -r requirements/dev.in
|
||||
jedi==0.19.1
|
||||
# via ipython
|
||||
lxml==5.0.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# unittest-xml-reporting
|
||||
marshmallow==3.20.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
matplotlib-inline==0.1.6
|
||||
# via ipython
|
||||
mypy==1.8.0
|
||||
# via -r requirements/dev.in
|
||||
mypy-extensions==1.0.0
|
||||
# via mypy
|
||||
oauthlib==3.2.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests-oauthlib
|
||||
packaging==23.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# marshmallow
|
||||
parso==0.8.3
|
||||
# via jedi
|
||||
pexpect==4.9.0
|
||||
# via ipython
|
||||
prompt-toolkit==3.0.43
|
||||
# via ipython
|
||||
psycopg[binary]==3.1.16
|
||||
# via -r requirements/test.txt
|
||||
psycopg-binary==3.1.16
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# psycopg
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
py-moneyed==3.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-money
|
||||
pycparser==2.21
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# cffi
|
||||
pygments==2.17.2
|
||||
# via ipython
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
python-dotenv==1.0.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
python3-openid==3.2.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
requests==2.31.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
six==1.16.0
|
||||
# via asttokens
|
||||
sqlparse==0.4.4
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django
|
||||
# django-debug-toolbar
|
||||
stack-data==0.6.3
|
||||
# via ipython
|
||||
tblib==3.0.0
|
||||
# via -r requirements/test.txt
|
||||
traitlets==5.14.1
|
||||
# via
|
||||
# ipython
|
||||
# matplotlib-inline
|
||||
types-pytz==2023.3.1.1
|
||||
# via django-stubs
|
||||
types-pyyaml==6.0.12.12
|
||||
# via django-stubs
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# dj-database-url
|
||||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# mypy
|
||||
# psycopg
|
||||
# py-moneyed
|
||||
unittest-xml-reporting==3.2.0
|
||||
# via -r requirements/test.txt
|
||||
urllib3==2.1.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
uvicorn==0.25.0
|
||||
# via -r requirements/test.txt
|
||||
wcwidth==0.2.13
|
||||
# via prompt-toolkit
|
||||
whitenoise==6.6.0
|
||||
# via -r requirements/test.txt
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
|
@ -1,5 +0,0 @@
|
|||
-r base.txt
|
||||
|
||||
coverage==7.4.0
|
||||
tblib==3.0.0
|
||||
unittest-xml-reporting==3.2.0
|
|
@ -1,149 +0,0 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/test.txt requirements/test.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django
|
||||
babel==2.14.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# py-moneyed
|
||||
certifi==2023.11.17
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
cffi==1.16.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# cryptography
|
||||
charset-normalizer==3.3.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# uvicorn
|
||||
coverage==7.4.0
|
||||
# via -r requirements/test.in
|
||||
cryptography==41.0.7
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# pyjwt
|
||||
defusedxml==0.7.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# python3-openid
|
||||
dj-database-url==2.1.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
dj-email-url==1.0.6
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
django==5.0.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# dj-database-url
|
||||
# django-allauth
|
||||
# django-money
|
||||
# django-zen-queries
|
||||
django-allauth==0.60.0
|
||||
# via -r requirements/base.txt
|
||||
django-cache-url==3.4.5
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
django-money==3.4.1
|
||||
# via -r requirements/base.txt
|
||||
django-zen-queries==2.1.0
|
||||
# via -r requirements/base.txt
|
||||
environs[django]==10.0.0
|
||||
# via -r requirements/base.txt
|
||||
h11==0.14.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# uvicorn
|
||||
idna==3.6
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
lxml==5.0.1
|
||||
# via unittest-xml-reporting
|
||||
marshmallow==3.20.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
oauthlib==3.2.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests-oauthlib
|
||||
packaging==23.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# marshmallow
|
||||
psycopg[binary]==3.1.16
|
||||
# via -r requirements/base.txt
|
||||
psycopg-binary==3.1.16
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# psycopg
|
||||
py-moneyed==3.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-money
|
||||
pycparser==2.21
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# cffi
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
python-dotenv==1.0.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
python3-openid==3.2.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
requests==2.31.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
sqlparse==0.4.4
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django
|
||||
tblib==3.0.0
|
||||
# via -r requirements/test.in
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# dj-database-url
|
||||
# psycopg
|
||||
# py-moneyed
|
||||
unittest-xml-reporting==3.2.0
|
||||
# via -r requirements/test.in
|
||||
urllib3==2.1.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
uvicorn==0.25.0
|
||||
# via -r requirements/base.txt
|
||||
whitenoise==6.6.0
|
||||
# via -r requirements/base.txt
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
|
@ -6,21 +6,26 @@ from . import models
|
|||
|
||||
@admin.register(models.Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
|
||||
list_display = ("who", "description", "created", "is_paid")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
def who(self, instance):
|
||||
return instance.user.get_full_name()
|
||||
|
||||
who.short_description = _("Customer")
|
||||
|
||||
|
||||
@admin.register(models.Payment)
|
||||
class PaymentAdmin(admin.ModelAdmin):
|
||||
|
||||
list_display = ("who", "description", "order_id", "created")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
def who(self, instance):
|
||||
return instance.order.user.get_full_name()
|
||||
|
||||
@admin.display(description=_("Order ID"))
|
||||
who.short_description = _("Customer")
|
||||
|
||||
def order_id(self, instance):
|
||||
return instance.order.id
|
||||
|
||||
order_id.short_description = _("Order ID")
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.db import models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
@ -98,7 +99,9 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"vat",
|
||||
djmoney.models.fields.MoneyField(decimal_places=2, max_digits=16, verbose_name="VAT"),
|
||||
djmoney.models.fields.MoneyField(
|
||||
decimal_places=2, max_digits=16, verbose_name="VAT"
|
||||
),
|
||||
),
|
||||
("is_paid", models.BooleanField(default=False, verbose_name="is paid")),
|
||||
(
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
# Generated by Django 5.0.1 on 2024-01-14 11:14
|
||||
|
||||
import djmoney.models.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("accounting", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="order",
|
||||
name="price_currency",
|
||||
field=djmoney.models.fields.CurrencyField(
|
||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="order",
|
||||
name="vat_currency",
|
||||
field=djmoney.models.fields.CurrencyField(
|
||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="payment",
|
||||
name="amount_currency",
|
||||
field=djmoney.models.fields.CurrencyField(
|
||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transaction",
|
||||
name="amount_currency",
|
||||
field=djmoney.models.fields.CurrencyField(
|
||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
||||
),
|
||||
),
|
||||
]
|
|
@ -9,6 +9,7 @@ from djmoney.models.fields import MoneyField
|
|||
|
||||
|
||||
class CreatedModifiedAbstract(models.Model):
|
||||
|
||||
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
|
||||
|
@ -17,7 +18,8 @@ class CreatedModifiedAbstract(models.Model):
|
|||
|
||||
|
||||
class Account(CreatedModifiedAbstract):
|
||||
"""This is the model where we can give access to several users, such that they
|
||||
"""
|
||||
This is the model where we can give access to several users, such that they
|
||||
can decide which account to use to pay for something.
|
||||
"""
|
||||
|
||||
|
@ -29,14 +31,13 @@ class Account(CreatedModifiedAbstract):
|
|||
|
||||
|
||||
class Transaction(CreatedModifiedAbstract):
|
||||
"""Tracks in and outgoing events of an account. When an order is received, an
|
||||
"""
|
||||
Tracks in and outgoing events of an account. When an order is received, an
|
||||
amount is subtracted, when a payment is received, an amount is added.
|
||||
"""
|
||||
|
||||
account = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="transactions",
|
||||
Account, on_delete=models.PROTECT, related_name="transactions"
|
||||
)
|
||||
amount = MoneyField(
|
||||
verbose_name=_("amount"),
|
||||
|
@ -48,7 +49,8 @@ class Transaction(CreatedModifiedAbstract):
|
|||
|
||||
|
||||
class Order(CreatedModifiedAbstract):
|
||||
"""Scoped out: Contents of invoices will have to be tracked either here or in
|
||||
"""
|
||||
Scoped out: Contents of invoices will have to be tracked either here or in
|
||||
a separate Invoice model. This is undecided because we are not generating
|
||||
invoices at the moment.
|
||||
"""
|
||||
|
@ -59,9 +61,7 @@ class Order(CreatedModifiedAbstract):
|
|||
description = models.CharField(max_length=1024, verbose_name=_("description"))
|
||||
|
||||
price = MoneyField(
|
||||
verbose_name=_("price (excl. VAT)"),
|
||||
max_digits=16,
|
||||
decimal_places=2,
|
||||
verbose_name=_("price (excl. VAT)"), max_digits=16, decimal_places=2
|
||||
)
|
||||
vat = MoneyField(verbose_name=_("VAT"), max_digits=16, decimal_places=2)
|
||||
|
||||
|
@ -88,11 +88,12 @@ class Order(CreatedModifiedAbstract):
|
|||
verbose_name = pgettext_lazy("accounting term", "Order")
|
||||
verbose_name_plural = pgettext_lazy("accounting term", "Orders")
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Order ID {self.display_id}"
|
||||
def __str__(self):
|
||||
return "Order ID {id}".format(id=self.display_id)
|
||||
|
||||
|
||||
class Payment(CreatedModifiedAbstract):
|
||||
|
||||
amount = MoneyField(max_digits=16, decimal_places=2)
|
||||
order = models.ForeignKey(Order, on_delete=models.PROTECT)
|
||||
|
||||
|
@ -113,8 +114,8 @@ class Payment(CreatedModifiedAbstract):
|
|||
description=order.description,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Payment ID {self.display_id}"
|
||||
def __str__(self):
|
||||
return "Payment ID {id}".format(id=self.display_id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("payment")
|
||||
|
|
|
@ -8,8 +8,8 @@ from . import models
|
|||
# do stuff
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_balance() -> None:
|
||||
@pytest.mark.django_db
|
||||
def test_balance():
|
||||
user = User.objects.create_user("test", "lala@adas.com", "1234")
|
||||
account = models.Account.objects.create(owner=user)
|
||||
assert account.balance == 0
|
||||
|
|
|
@ -10,6 +10,6 @@ if __name__ == "__main__":
|
|||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?",
|
||||
"forget to activate a virtual environment?"
|
||||
)
|
||||
execute_from_command_line(sys.argv)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""Membership application.
|
||||
"""
|
||||
Membership application
|
||||
======================
|
||||
|
||||
This application's domain relate to organizational structures and
|
||||
|
|
|
@ -2,7 +2,6 @@ from django.contrib import admin
|
|||
|
||||
from .models import Membership
|
||||
from .models import MembershipType
|
||||
from .models import SubscriptionPeriod
|
||||
|
||||
|
||||
@admin.register(Membership)
|
||||
|
@ -13,8 +12,3 @@ class MembershipAdmin(admin.ModelAdmin):
|
|||
@admin.register(MembershipType)
|
||||
class MembershipTypeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(SubscriptionPeriod)
|
||||
class SubscriptionPeriodAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
class MembershipConfig(AppConfig):
|
||||
name = "membership"
|
||||
|
||||
def ready(self) -> None:
|
||||
from .permissions import persist_permissions
|
||||
|
||||
post_migrate.connect(persist_permissions, sender=self)
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.db import models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
# Generated by Django 4.1 on 2023-01-02 21:05
|
||||
|
||||
import django.contrib.postgres.constraints
|
||||
import django.contrib.postgres.fields.ranges
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("membership", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="SubscriptionPeriod",
|
||||
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"),
|
||||
),
|
||||
(
|
||||
"period",
|
||||
django.contrib.postgres.fields.ranges.DateRangeField(verbose_name="period"),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="membership",
|
||||
name="period",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="membership",
|
||||
name="created",
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name="created"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="membershiptype",
|
||||
name="created",
|
||||
field=models.DateTimeField(auto_now_add=True, verbose_name="created"),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="subscriptionperiod",
|
||||
constraint=django.contrib.postgres.constraints.ExclusionConstraint(
|
||||
expressions=[("period", "&&")], name="exclude_overlapping_periods"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 4.1 on 2023-01-02 21:05
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("membership", "0002_subscriptionperiod_remove_membership_period_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="membership",
|
||||
name="period",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
to="membership.subscriptionperiod",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,22 +0,0 @@
|
|||
# Generated by Django 4.1 on 2023-01-02 21:06
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("membership", "0003_membership_period"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="membership",
|
||||
name="period",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
to="membership.subscriptionperiod",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 4.1.5 on 2023-09-16 14:09
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
("membership", "0004_alter_membership_period"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Member",
|
||||
fields=[],
|
||||
options={
|
||||
"proxy": True,
|
||||
"indexes": [],
|
||||
"constraints": [],
|
||||
},
|
||||
bases=("auth.user",),
|
||||
),
|
||||
]
|
|
@ -1,78 +1,43 @@
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.constraints import ExclusionConstraint
|
||||
from django.contrib.postgres.fields import DateRangeField
|
||||
from django.contrib.postgres.fields import RangeOperators
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib.postgres.fields import DateTimeRangeField
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from utils.mixins import CreatedModifiedAbstract
|
||||
|
||||
|
||||
class Member(User):
|
||||
class QuerySet(models.QuerySet):
|
||||
def annotate_membership(self):
|
||||
from .selectors import get_current_subscription_period
|
||||
class CreatedModifiedAbstract(models.Model):
|
||||
|
||||
current_subscription_period = get_current_subscription_period()
|
||||
|
||||
if not current_subscription_period:
|
||||
raise ValueError("No current subscription period found")
|
||||
|
||||
return self.annotate(
|
||||
active_membership=models.Exists(
|
||||
Membership.objects.filter(
|
||||
user=models.OuterRef("pk"),
|
||||
period=current_subscription_period.id,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
objects = QuerySet.as_manager()
|
||||
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
class SubscriptionPeriod(CreatedModifiedAbstract):
|
||||
"""Denotes a period for which members should pay their membership fee for."""
|
||||
|
||||
period = DateRangeField(verbose_name=_("period"))
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
ExclusionConstraint(
|
||||
name="exclude_overlapping_periods",
|
||||
expressions=[
|
||||
("period", RangeOperators.OVERLAPS),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.period.lower} - {self.period.upper or _('next general assembly')}"
|
||||
abstract = True
|
||||
|
||||
|
||||
class Membership(CreatedModifiedAbstract):
|
||||
"""Tracks that a user has membership of a given type for a given period."""
|
||||
"""
|
||||
Tracks that a user has membership of a given type for a given period.
|
||||
"""
|
||||
|
||||
class QuerySet(models.QuerySet):
|
||||
def for_member(self, member: Member):
|
||||
return self.filter(user=member)
|
||||
def for_user(self, user):
|
||||
return self.filter(user=user)
|
||||
|
||||
def _current(self):
|
||||
return self.filter(period__period__contains=timezone.now())
|
||||
return self.filter(period__contains=timezone.now())
|
||||
|
||||
def current(self) -> "Membership | None":
|
||||
def current(self) -> Optional["Membership"]:
|
||||
try:
|
||||
return self._current().get()
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
def previous(self) -> list["Membership"]:
|
||||
def previous(self):
|
||||
# A naïve way to get previous by just excluding the current. This
|
||||
# means that there must be some protection against "future"
|
||||
# memberships.
|
||||
return list(self.all().difference(self._current()))
|
||||
return self.all().difference(self._current())
|
||||
|
||||
objects = QuerySet.as_manager()
|
||||
|
||||
|
@ -89,17 +54,15 @@ class Membership(CreatedModifiedAbstract):
|
|||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
period = models.ForeignKey(
|
||||
"membership.SubscriptionPeriod",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
period = DateTimeRangeField(help_text=_("The duration this subscription is for. "))
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return f"{self.user} - {self.period}"
|
||||
|
||||
|
||||
class MembershipType(CreatedModifiedAbstract):
|
||||
"""Models membership types. Currently only a name, but will in the future
|
||||
"""
|
||||
Models membership types. Currently only a name, but will in the future
|
||||
possibly contain more information like fees.
|
||||
"""
|
||||
|
||||
|
@ -109,5 +72,5 @@ class MembershipType(CreatedModifiedAbstract):
|
|||
|
||||
name = models.CharField(verbose_name=_("name"), max_length=64)
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from django.contrib.auth.models import Permission as DjangoPermission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
PERMISSIONS = []
|
||||
|
||||
|
||||
def persist_permissions(sender, **kwargs) -> None:
|
||||
for permission in PERMISSIONS:
|
||||
permission.persist_permission()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Permission:
|
||||
name: str
|
||||
codename: str
|
||||
app_label: str
|
||||
model: str
|
||||
|
||||
def __post_init__(self, *args, **kwargs):
|
||||
PERMISSIONS.append(self)
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
return f"{self.app_label}.{self.codename}"
|
||||
|
||||
def persist_permission(self) -> None:
|
||||
content_type, _ = ContentType.objects.get_or_create(
|
||||
app_label=self.app_label,
|
||||
model=self.model,
|
||||
)
|
||||
DjangoPermission.objects.get_or_create(
|
||||
content_type=content_type,
|
||||
codename=self.codename,
|
||||
defaults={"name": self.name},
|
||||
)
|
||||
|
||||
|
||||
ADMINISTRATE_MEMBERS = Permission(
|
||||
name=_("Can administrate members"),
|
||||
codename="administrate_members",
|
||||
app_label="membership",
|
||||
model="membership",
|
||||
)
|
|
@ -1,60 +0,0 @@
|
|||
import contextlib
|
||||
|
||||
from django.db.models import Exists
|
||||
from django.db.models import OuterRef
|
||||
from django.utils import timezone
|
||||
|
||||
from membership.models import Member
|
||||
from membership.models import Membership
|
||||
from membership.models import SubscriptionPeriod
|
||||
|
||||
|
||||
def get_subscription_periods(member: Member | None = None) -> list[SubscriptionPeriod]:
|
||||
subscription_periods = SubscriptionPeriod.objects.prefetch_related(
|
||||
"membership_set",
|
||||
"membership_set__user",
|
||||
).all()
|
||||
|
||||
if member:
|
||||
subscription_periods = subscription_periods.annotate(
|
||||
membership_exists=Exists(
|
||||
Membership.objects.filter(
|
||||
user=member,
|
||||
period=OuterRef("pk"),
|
||||
),
|
||||
),
|
||||
).filter(membership_exists=True)
|
||||
|
||||
return list(subscription_periods)
|
||||
|
||||
|
||||
def get_current_subscription_period() -> SubscriptionPeriod | None:
|
||||
with contextlib.suppress(SubscriptionPeriod.DoesNotExist):
|
||||
return SubscriptionPeriod.objects.prefetch_related(
|
||||
"membership_set",
|
||||
"membership_set__user",
|
||||
).get(period__contains=timezone.now())
|
||||
|
||||
|
||||
def get_memberships(
|
||||
*,
|
||||
member: Member | None = None,
|
||||
period: SubscriptionPeriod | None = None,
|
||||
) -> Membership.QuerySet:
|
||||
memberships = Membership.objects.select_related("membership_type").all()
|
||||
|
||||
if member:
|
||||
memberships = memberships.for_member(member=member)
|
||||
|
||||
if period:
|
||||
memberships = memberships.filter(period=period)
|
||||
|
||||
return memberships
|
||||
|
||||
|
||||
def get_members():
|
||||
return Member.objects.all().annotate_membership().order_by("username")
|
||||
|
||||
|
||||
def get_member(*, member_id: int) -> Member:
|
||||
return get_members().get(id=member_id)
|
|
@ -1,45 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}
|
||||
{% trans "Member detail" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
<h1>
|
||||
{{ member.username }}
|
||||
</h1>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>{% trans "Membership" %}</h3>
|
||||
|
||||
{% if subscription_periods %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Start" %}</th>
|
||||
<th>{% trans "End" %}</th>
|
||||
<th>{% trans "Has membership" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for period in subscription_periods %}
|
||||
<tr {% if not period.period.upper %}class="table-active"{% endif %}>
|
||||
<td>{{ period.period.lower }}</td>
|
||||
<td>{{ period.period.upper }}</td>
|
||||
<td>{{ period.membership_exists }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
{% trans "No memberships" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,63 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}
|
||||
{% trans "Membership" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
<h2>Membership settings</h2>
|
||||
{% if not current_membership %}
|
||||
<p>{% trans "You do not have an active membership!" %}</p>
|
||||
|
||||
<p>{% trans "You can become a member by depositing the membership fee to our bank account." %}</p>
|
||||
|
||||
<ul>
|
||||
<li>Reg. 8401 (Merkur)</li>
|
||||
<li>Kontonr. 1016866</li>
|
||||
<li>Tekst på overførslen: Your email</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>{% trans "You are a member!" %}</p>
|
||||
|
||||
{% trans "next general assembly" as next_general_assembly %}
|
||||
<p>{% trans "Period" %}: {{ current_period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_period.upper|date:"SHORT_DATE_FORMAT"|default:next_general_assembly }}</p>
|
||||
<p>{% trans "Type" %}: {{ current_membership.membership_type }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="content-view">
|
||||
<h2>Profile settings</h2>
|
||||
<form>
|
||||
<div>
|
||||
<label for="username">
|
||||
Username
|
||||
</label>
|
||||
<input id="username" type="text" value="{{user}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="first_name">
|
||||
First name
|
||||
</label>
|
||||
<input id="first_name" type="text" value="{{user.first_name}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="last_name">
|
||||
Last name
|
||||
</label>
|
||||
<input id="last_name" type="text" value="{{user.last_name}}" />
|
||||
</div>
|
||||
|
||||
<button>Update Profile</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="view-list">
|
||||
<h2>Email settings</h2>
|
||||
<button>Update Email</button>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if not current_membership %}
|
||||
<p>{% trans "You do not have an active membership!" %}</p>
|
||||
|
||||
<p>{% trans "You can become a member by depositing the membership fee to our bank account." %}</p>
|
||||
|
||||
<ul>
|
||||
<li>Reg. 8401 (Merkur)</li>
|
||||
<li>Kontonr. 1016866</li>
|
||||
<li>Tekst på overførslen: Your email</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>{% trans "You are a member!" %}</p>
|
||||
|
||||
<p>{% trans "Period" %}: {{ current_membership.period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_membership.period.upper|date:"SHORT_DATE_FORMAT" }}</p>
|
||||
<p>{% trans "Type" %}: {{ current_membership.membership_type }}</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,99 +1,18 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django_view_decorator import namespaced_decorator_factory
|
||||
from utils.view_utils import RowAction
|
||||
from utils.view_utils import render
|
||||
from utils.view_utils import render_list
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render
|
||||
|
||||
from .permissions import ADMINISTRATE_MEMBERS
|
||||
from .selectors import get_member
|
||||
from .selectors import get_members
|
||||
from .selectors import get_memberships
|
||||
from .selectors import get_subscription_periods
|
||||
|
||||
member_view = namespaced_decorator_factory(namespace="member", base_path="membership")
|
||||
from .models import Membership
|
||||
|
||||
|
||||
@member_view(
|
||||
paths="",
|
||||
name="membership-overview",
|
||||
login_required=True,
|
||||
)
|
||||
@login_required
|
||||
def membership_overview(request):
|
||||
memberships = get_memberships(member=request.user)
|
||||
memberships = Membership.objects.for_user(request.user)
|
||||
current_membership = memberships.current()
|
||||
previous_memberships = memberships.previous()
|
||||
|
||||
current_period = current_membership.period.period if current_membership else None
|
||||
|
||||
context = {
|
||||
"current_membership": current_membership,
|
||||
"current_period": current_period,
|
||||
"previous_memberships": previous_memberships,
|
||||
}
|
||||
|
||||
return render(
|
||||
request=request,
|
||||
template_name="membership/membership_overview.html",
|
||||
context=context,
|
||||
context = dict(
|
||||
current_membership=current_membership,
|
||||
previous_memberships=previous_memberships,
|
||||
)
|
||||
|
||||
|
||||
admin_members_view = namespaced_decorator_factory(
|
||||
namespace="admin-members",
|
||||
base_path="admin",
|
||||
)
|
||||
|
||||
|
||||
@admin_members_view(
|
||||
paths="members/",
|
||||
name="list",
|
||||
login_required=True,
|
||||
permissions=[ADMINISTRATE_MEMBERS.path],
|
||||
)
|
||||
def members_admin(request):
|
||||
users = get_members()
|
||||
|
||||
return render_list(
|
||||
entity_name="member",
|
||||
entity_name_plural="members",
|
||||
request=request,
|
||||
paginate_by=20,
|
||||
objects=users,
|
||||
columns=[
|
||||
("username", _("Username")),
|
||||
("first_name", _("First name")),
|
||||
("last_name", _("Last name")),
|
||||
("email", _("Email")),
|
||||
("active_membership", _("Active membership")),
|
||||
],
|
||||
row_actions=[
|
||||
RowAction(
|
||||
label=_("View"),
|
||||
url_name="admin-members:detail",
|
||||
url_kwargs={"member_id": "id"},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@admin_members_view(
|
||||
paths="<int:member_id>/",
|
||||
name="detail",
|
||||
login_required=True,
|
||||
permissions=[ADMINISTRATE_MEMBERS.path],
|
||||
)
|
||||
def members_admin_detail(request, member_id):
|
||||
member = get_member(member_id=member_id)
|
||||
subscription_periods = get_subscription_periods(member=member)
|
||||
|
||||
context = {
|
||||
"member": member,
|
||||
"subscription_periods": subscription_periods,
|
||||
"base_path": "admin-members:list",
|
||||
}
|
||||
|
||||
return render(
|
||||
request=request,
|
||||
template_name="membership/members_admin_detail.html",
|
||||
context=context,
|
||||
)
|
||||
return render(request, "membership_overview.html", context)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
"""Context processors for the membersystem app."""
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
|
||||
|
||||
def current_site(request):
|
||||
"""Include the current site in the context."""
|
||||
return {"site": get_current_site(request)}
|
|
@ -16,18 +16,13 @@ SECRET_KEY = env.str("SECRET_KEY", default="something-very-secret")
|
|||
DEBUG = env.bool("DEBUG", default=False)
|
||||
|
||||
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
|
||||
CSRF_TRUSTED_ORIGINS = env.list(
|
||||
"CSRF_TRUSTED_ORIGINS",
|
||||
default=["http://localhost:8000"],
|
||||
)
|
||||
|
||||
ADMINS = [tuple(x.split(":")) for x in env.list("DJANGO_ADMINS", default=[])]
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
# Application definition
|
||||
|
||||
DJANGO_APPS = [
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
|
@ -35,26 +30,14 @@ DJANGO_APPS = [
|
|||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"django.contrib.sites",
|
||||
]
|
||||
|
||||
THIRD_PARTY_APPS = [
|
||||
"debug_toolbar",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
"django_view_decorator",
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
"utils",
|
||||
"accounting",
|
||||
"membership",
|
||||
]
|
||||
|
||||
INSTALLED_APPS = [
|
||||
*DJANGO_APPS,
|
||||
*THIRD_PARTY_APPS,
|
||||
*LOCAL_APPS,
|
||||
]
|
||||
|
||||
DATABASES = {"default": env.dj_db_url("DATABASE_URL")}
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -66,7 +49,7 @@ MIDDLEWARE = [
|
|||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "project.urls"
|
||||
|
@ -82,12 +65,10 @@ TEMPLATES = [
|
|||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
"builtins": [
|
||||
"django.templatetags.i18n",
|
||||
],
|
||||
"project.context_processors.current_site",
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
|
@ -98,7 +79,13 @@ AUTHENTICATION_BACKENDS = (
|
|||
WSGI_APPLICATION = "project.wsgi.application"
|
||||
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = []
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" # noqa
|
||||
},
|
||||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, # noqa
|
||||
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, # noqa
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = "da-dk"
|
||||
|
||||
|
@ -106,6 +93,7 @@ TIME_ZONE = "Europe/Copenhagen"
|
|||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
@ -118,8 +106,7 @@ SITE_ID = 1
|
|||
LOGIN_REDIRECT_URL = "/"
|
||||
|
||||
EMAIL_BACKEND = env.str(
|
||||
"EMAIL_BACKEND",
|
||||
default="django.core.mail.backends.console.EmailBackend",
|
||||
"EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
|
||||
)
|
||||
DEFAULT_FROM_EMAIL = env.str("DEFAULT_FROM_EMAIL", default="")
|
||||
# Parse email URLs, e.g. "smtp://"
|
||||
|
@ -153,36 +140,3 @@ ACCOUNT_AUTHENTICATION_METHOD = "email"
|
|||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
|
||||
ACCOUNT_USERNAME_REQUIRED = False
|
||||
|
||||
# Logging
|
||||
# We want to log everything to stdout in docker
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"handlers": ["console"],
|
||||
"level": "DEBUG",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if DEBUG:
|
||||
INSTALLED_APPS += ["debug_toolbar", "django_browser_reload"]
|
||||
MIDDLEWARE += [
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
"django_browser_reload.middleware.BrowserReloadMiddleware",
|
||||
]
|
||||
# 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,
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,67 +0,0 @@
|
|||
html.dark body {
|
||||
--splash: #5b47e0;
|
||||
background: var(--dark-dark);
|
||||
color: var(--medium-dust)
|
||||
}
|
||||
|
||||
html.dark h1,
|
||||
html.dark h2,
|
||||
html.dark h3,
|
||||
html.dark h4,
|
||||
html.dark h5,
|
||||
html.dark h6,
|
||||
html.dark footer,
|
||||
html.dark nav ol li a {
|
||||
color: var(--medium-dust);
|
||||
}
|
||||
|
||||
html.dark nav ol li a:not(.current):hover {
|
||||
border-color: var(--medium-dust);
|
||||
}
|
||||
|
||||
html.dark header,
|
||||
html.dark main aside,
|
||||
html.dark nav {
|
||||
background: #1d1d1d;
|
||||
}
|
||||
|
||||
html.dark nav {
|
||||
border: none;
|
||||
}
|
||||
|
||||
html.dark hr {
|
||||
border-color: var(--twilight);
|
||||
}
|
||||
|
||||
html.dark main aside div,
|
||||
html.dark article div.content-view {
|
||||
background: var(--dark-twilight);
|
||||
}
|
||||
|
||||
html.dark article table tbody {
|
||||
background: var(--dark-twilight);
|
||||
}
|
||||
|
||||
html.dark article table tbody tr:nth-child(2n+1) {
|
||||
background: var(--dark);
|
||||
}
|
||||
|
||||
html.dark article table tbody tr:nth-child(2n+1) td {
|
||||
border-top: 1px solid var(--dark-dark);
|
||||
border-bottom: 1px solid var(--dark-dark);
|
||||
}
|
||||
|
||||
html.dark article table tbody tr:last-child td {
|
||||
border-bottom: var(--half-space) solid var(--twilight);
|
||||
}
|
||||
|
||||
html.dark form>div>input[type="text"],
|
||||
html.dark form>div>input[type="password"],
|
||||
html.dark input[type="email"] {
|
||||
border: 2px solid var(--twilight);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
background: var(--dark-dark);
|
||||
width: 100%;
|
||||
color: var(--light-dust);
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,559 +0,0 @@
|
|||
/* Reset */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#root,
|
||||
#__next {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/* Variables */
|
||||
:root {
|
||||
/* Colors */
|
||||
--light: #ffffff;
|
||||
--light-dust: #fefef9;
|
||||
--dust: #f4f1ef;
|
||||
--medium-dust: #dadada;
|
||||
--dark-dust: #bfbfbf;
|
||||
--fade: #878787;
|
||||
--twilight: #4a4a4a;
|
||||
--dark-twilight: #2f2f2f;
|
||||
--dark: #2a2a2a;
|
||||
--dark-dark: #121212;
|
||||
--light-custard: #eee7d5;
|
||||
--custard: #f0dcac;
|
||||
--dark-custard: #d4c7a9;
|
||||
--water: #a8f3f4;
|
||||
--splash: #4b3aba;
|
||||
|
||||
/* Sizes */
|
||||
--space: 12px;
|
||||
--double-space: calc(var(--space) * 2);
|
||||
--half-space: calc(var(--space) / 2);
|
||||
--quarter-space: calc(var(--space) / 4);
|
||||
--outer-space: var(--double-space);
|
||||
}
|
||||
|
||||
@media (min-width: 1380px) {
|
||||
:root {
|
||||
--outer-space: 15%;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 600;
|
||||
color: var(--twilight);
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: var(--splash);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: var(--double-space) 0;
|
||||
height: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--dark-custard);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--custard);
|
||||
font-family: Inter;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
padding: var(--double-space) var(--outer-space);
|
||||
background: var(--light);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header>h1 {
|
||||
font-size: 1.44em;
|
||||
}
|
||||
|
||||
#switch-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 var(--space);
|
||||
top: -2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#switch-icon #layer1 path {
|
||||
fill: var(--twilight);
|
||||
}
|
||||
|
||||
header>div>a#logout {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
background: var(--twilight);
|
||||
text-decoration: none;
|
||||
color: var(--dust);
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
header>div>a#logout:hover {
|
||||
background: var(--splash);
|
||||
color: var(--light);
|
||||
}
|
||||
|
||||
aside {
|
||||
padding: 0 var(--outer-space) var(--double-space) var(--outer-space);
|
||||
background: var(--light);
|
||||
}
|
||||
|
||||
aside>div {
|
||||
background: var(--dust);
|
||||
padding: var(--double-space);
|
||||
border-radius: var(--quarter-space);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
aside>div>h2 {
|
||||
font-size: 1.22em;
|
||||
margin: 0 0 6px 0;
|
||||
}
|
||||
|
||||
aside>div>figure {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid var(--dark-dust);
|
||||
float: left;
|
||||
margin-right: var(--double-space);
|
||||
}
|
||||
|
||||
aside>div>dl {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
aside>div>dl>dt {
|
||||
float: left;
|
||||
clear: left;
|
||||
margin: 0 var(--double-space) 0 0;
|
||||
width: 180px;
|
||||
font-weight: 600;
|
||||
color: var(--twilight);
|
||||
}
|
||||
|
||||
nav {
|
||||
display: block;
|
||||
border-bottom: 1px solid var(--dark-custard);
|
||||
background: var(--light);
|
||||
}
|
||||
|
||||
nav ol {
|
||||
margin: 0 calc(var(--outer-space));
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
nav ol li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
nav>ol>li>a {
|
||||
display: block;
|
||||
padding: var(--half-space) var(--half-space) var(--quarter-space);
|
||||
margin: 0 var(--space);
|
||||
border-bottom: var(--half-space) solid transparent;
|
||||
text-decoration: none;
|
||||
color: var(--dark);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
nav>ol>li:first-child>a {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
nav ol li a:hover {
|
||||
border-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
nav ol li a.current {
|
||||
font-weight: bold;
|
||||
border-color: var(--splash);
|
||||
color: var(--splash);
|
||||
}
|
||||
|
||||
article {
|
||||
padding: var(--double-space) var(--outer-space);
|
||||
}
|
||||
|
||||
article div.content-view {
|
||||
background: var(--dust);
|
||||
padding: var(--double-space);
|
||||
margin-bottom: var(--space);
|
||||
}
|
||||
|
||||
div.content-view>h2 {
|
||||
margin: 0 0 var(--space) 0;
|
||||
}
|
||||
|
||||
div.services {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--double-space);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.services>div,
|
||||
div.infobox {
|
||||
background: var(--light);
|
||||
padding: var(--double-space);
|
||||
border-radius: 6px;
|
||||
flex: 240px;
|
||||
max-width: 420px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
div.infobox button {
|
||||
margin-top: var(--double-space);
|
||||
}
|
||||
|
||||
div.services>div>div.description {
|
||||
margin-bottom: var(--double-space);
|
||||
}
|
||||
|
||||
div.services>div>div.description>p {
|
||||
margin-top: var(--half-space);
|
||||
}
|
||||
|
||||
div.services>div>a,
|
||||
a.button,
|
||||
button {
|
||||
display: block;
|
||||
color: var(--light);
|
||||
background: var(--splash);
|
||||
padding: var(--space) var(--double-space);
|
||||
border-radius: var(--quarter-space);
|
||||
opacity: 0.85;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
button.small {
|
||||
font-size: 0.78em;
|
||||
padding: var(--half-space) var(--space);
|
||||
}
|
||||
|
||||
div.services>div>a:hover,
|
||||
a.button:hover,
|
||||
button:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.6;
|
||||
background: var(--twilight);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: var(--twilight);
|
||||
}
|
||||
|
||||
article table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
margin: var(--space) 0;
|
||||
}
|
||||
|
||||
article table thead th {
|
||||
background: var(--twilight);
|
||||
color: var(--medium-dust);
|
||||
}
|
||||
|
||||
article table thead th a {
|
||||
color: var(--light);
|
||||
}
|
||||
|
||||
article table thead th:first-child {
|
||||
border-radius: var(--half-space) 0 0 0;
|
||||
}
|
||||
|
||||
article table thead th:last-child {
|
||||
border-radius: 0 var(--half-space) 0 0;
|
||||
}
|
||||
|
||||
article table tbody {
|
||||
background: var(--light-dust);
|
||||
}
|
||||
|
||||
article table tbody tr:nth-child(odd) {
|
||||
background: var(--light-custard);
|
||||
}
|
||||
|
||||
article table tbody tr:nth-child(odd) td {
|
||||
border-top: 1px solid var(--dark-custard);
|
||||
border-bottom: 1px solid var(--dark-custard);
|
||||
}
|
||||
|
||||
article table tbody tr:last-child td {
|
||||
border-bottom: var(--half-space) solid var(--twilight);
|
||||
}
|
||||
|
||||
article table thead th,
|
||||
article table tbody td {
|
||||
padding: var(--space);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
article table#user_email_table tbody tr td:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form>div {
|
||||
margin: 0 0 var(--double-space);
|
||||
}
|
||||
|
||||
form>div>label {
|
||||
display: block;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
form>div>input[type="text"],
|
||||
form>div>input[type="password"],
|
||||
input[type="email"] {
|
||||
border: 2px solid var(--twilight);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
background: var(--light-dust);
|
||||
width: 100%;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
form div.buttonHolder button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#email-add-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#email-add-overlay .content-view {
|
||||
width: 600px;
|
||||
padding: var(--double-space);
|
||||
}
|
||||
|
||||
#email-add-overlay .content-view p {
|
||||
margin: var(--double-space) 0;
|
||||
}
|
||||
|
||||
#login {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#loginbox {
|
||||
border-radius: var(--space);
|
||||
border: 6px solid white;
|
||||
width: 800px;
|
||||
height: 500px;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
#loginbox>div {
|
||||
padding: var(--double-space);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#loginbox label {
|
||||
color: var(--twilight);
|
||||
}
|
||||
|
||||
#loginbox>div.login {
|
||||
background: var(--light-dust);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#loginbox>div.signup {
|
||||
background: var(--water);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#loginbox>div:first-child {
|
||||
border-radius: var(--half-space) 0 0 var(--half-space);
|
||||
}
|
||||
|
||||
#loginbox>div:last-child {
|
||||
border-radius: 0 var(--half-space) var(--half-space) 0;
|
||||
}
|
||||
|
||||
#loginbox>div:last-child>* {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#loginbox div.new_here {
|
||||
margin-top: var(--double-space);
|
||||
}
|
||||
|
||||
#loginbox div.new_here h2 {
|
||||
margin: var(--double-space) 0;
|
||||
}
|
||||
|
||||
#loginbox button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#loginbox img {
|
||||
padding: 0 var(--double-space);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin: var(--space) var(--outer-space);
|
||||
padding: var(--space);
|
||||
border-radius: var(--quarter-space);
|
||||
background: var(--dark);
|
||||
color: var(--dust);
|
||||
font-size: 0.78em;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
span.time_remaining {
|
||||
color: var(--fade);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
list-style: none;
|
||||
padding: var(--half-space) 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination>li {
|
||||
margin: 0 var(--half-space);
|
||||
}
|
||||
|
||||
.pagination>li:first-child {
|
||||
margin-right: var(--double-space);
|
||||
}
|
||||
|
||||
.pagination>li:last-child {
|
||||
margin-left: var(--double-space);
|
||||
}
|
||||
|
||||
.pagination .page-item {
|
||||
border: 1px solid var(--fade);
|
||||
padding: var(--quarter-space) var(--half-space);
|
||||
border-radius: var(--half-space);
|
||||
background: var(--light-dust);
|
||||
font-size: 0.78em;
|
||||
}
|
||||
|
||||
.pagination .page-link {
|
||||
padding: var(--half-space);
|
||||
color: var(--twilight);
|
||||
}
|
||||
|
||||
.pagination .page-item.active {
|
||||
background: var(--twilight);
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
color: var(--light-dust);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pagination .page-item.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pagination .page-item.disabled .page-link {
|
||||
cursor: default;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,200 +0,0 @@
|
|||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url("Inter-Thin.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Thin.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-ThinItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-ExtraLight.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url("Inter-Light.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Light.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-LightItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Regular.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Italic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("Inter-Medium.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Medium.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-MediumItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-SemiBold.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Bold.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-BoldItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-ExtraBold.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url("Inter-Black.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-Black.woff?v=3.19") format("woff");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"),
|
||||
url("Inter-BlackItalic.woff?v=3.19") format("woff");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------
|
||||
Variable font.
|
||||
Usage:
|
||||
|
||||
html { font-family: 'Inter', sans-serif; }
|
||||
@supports (font-variation-settings: normal) {
|
||||
html { font-family: 'Inter var', sans-serif; }
|
||||
}
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Inter var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-named-instance: 'Regular';
|
||||
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-named-instance: 'Italic';
|
||||
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
[EXPERIMENTAL] Multi-axis, single variable font.
|
||||
|
||||
Slant axis is not yet widely supported (as of February 2019) and thus this
|
||||
multi-axis single variable font is opt-in rather than the default.
|
||||
|
||||
When using this, you will probably need to set font-variation-settings
|
||||
explicitly, e.g.
|
||||
|
||||
* { font-variation-settings: "slnt" 0deg }
|
||||
.italic { font-variation-settings: "slnt" 10deg }
|
||||
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Inter var experimental';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: oblique 0deg 10deg;
|
||||
src: url("Inter.var.woff2?v=3.19") format("woff2");
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -2,95 +2,114 @@
|
|||
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}{% trans "Email Addresses" %}{% endblock %}
|
||||
{% block head_title %}{% trans "E-mail Addresses" %}{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="content-view">
|
||||
<h2>{% trans "Email Addresses" %}</h2>
|
||||
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
|
||||
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
{% if user.emailaddress_set.all %}
|
||||
<form action="{% url 'account_email' %}" class="email_list" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="blockLabels">
|
||||
<div class="buttonHolder">
|
||||
<button class="small" name="action_add_open" style="float:right">Add Email …</button>
|
||||
<button class="small" disabled type="submit" id="action_primary" name="action_primary">Make Primary</button>
|
||||
<button class="small" type="submit" name="action_send">Re-send Verification</button>
|
||||
<button class="small" type="submit" name="action_remove">Remove</button>
|
||||
</div>
|
||||
<table class="table" id="user_email_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans "Address" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
<th>{% trans "Primary" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for emailaddress in user.emailaddress_set.all %}
|
||||
<tr>
|
||||
<label for="email_radio_{{ forloop.counter }}"
|
||||
class="{% if emailaddress.primary %}primary_email{% endif %}">
|
||||
<td>
|
||||
<input
|
||||
id="email_radio_{{ forloop.counter }}"
|
||||
type="radio"
|
||||
name="email"
|
||||
value="{{ emailaddress.email }}"
|
||||
{% if emailaddress.primary or user.emailaddress_set.count == 1 %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
class="{% if emailaddress.primary %}primary_email{% endif %}"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{ emailaddress.email }}
|
||||
</td>
|
||||
<td>
|
||||
{% if emailaddress.verified %}
|
||||
<span class="label label-success">{% trans "Verified" %}</span>
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "Unverified" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if emailaddress.primary %}
|
||||
<span class="label label-primary">{% trans "Primary" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</label>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>{% trans "E-mail Addresses" %}</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>
|
||||
<strong>{% trans 'Warning:' %}</strong>
|
||||
{% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="email-add-overlay">
|
||||
<div class="content-view">
|
||||
<h3>{% trans "Add E-mail" %}</h3>
|
||||
<div class="panel-body">
|
||||
<form method="post" action="{% url 'account_email' %}"
|
||||
class="add_email">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button name="action_add" style="float:right" type="submit">
|
||||
{% trans "Add E-mail" %}
|
||||
</button>
|
||||
<button id="overlay-close-button" class="secondary">Cancel</button>
|
||||
</form>
|
||||
{% if user.emailaddress_set.all %}
|
||||
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
|
||||
<form action="{% url 'account_email' %}" class="email_list"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="blockLabels">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans "Address" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
<th>{% trans "Primary" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for emailaddress in user.emailaddress_set.all %}
|
||||
<tr class="ctrlHolder">
|
||||
<label for="email_radio_{{ forloop.counter }}"
|
||||
class="{% if emailaddress.primary %}primary_email{% endif %}">
|
||||
<td>
|
||||
<input
|
||||
id="email_radio_{{ forloop.counter }}"
|
||||
type="radio"
|
||||
name="email"
|
||||
value="{{ emailaddress.email }}"
|
||||
{% if emailaddress.primary or user.emailaddress_set.count == 1 %}
|
||||
checked="checked"
|
||||
{% endif %}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{{ emailaddress.email }}
|
||||
</td>
|
||||
<td>
|
||||
{% if emailaddress.verified %}
|
||||
<span class="label label-success">Verified</span>
|
||||
{% else %}
|
||||
<span class="label label-danger">Unverified</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if emailaddress.primary %}
|
||||
<span class="label label-primary">Primary</span>{% endif %}
|
||||
</td>
|
||||
</label>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="buttonHolder">
|
||||
<button class="btn btn-success" type="submit"
|
||||
name="action_primary">Make Primary
|
||||
</button>
|
||||
<button class="btn btn-primary" type="submit"
|
||||
name="action_send">Re-send Verification
|
||||
</button>
|
||||
<button class="btn btn-danger" type="submit"
|
||||
name="action_remove">Remove
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>
|
||||
<strong>{% trans 'Warning:' %}</strong>
|
||||
{% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4>{% trans "Add E-mail" %}</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form method="post" action="{% url 'account_email' %}"
|
||||
class="add_email">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button name="action_add" class="btn btn-success" type="submit">
|
||||
{% trans "Add E-mail" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -105,33 +124,6 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
let addEmail = document.getElementsByName('action_add_open')[0];
|
||||
|
||||
addEmail.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
let overlay = document.getElementById('email-add-overlay')
|
||||
overlay.style.display = 'flex'
|
||||
|
||||
window.addEventListener('keydown', function(e) {
|
||||
if (e.key == 'Escape') {
|
||||
overlay.style.display = 'none'
|
||||
}
|
||||
})
|
||||
|
||||
document.getElementById('overlay-close-button').addEventListener('click', function() {
|
||||
overlay.style.display = 'none'
|
||||
})
|
||||
})
|
||||
|
||||
let radio_actions = document.getElementsByName('email');
|
||||
if (radio_actions.length) {
|
||||
for (radio of radio_actions) {
|
||||
radio.addEventListener("change", function (e) {
|
||||
document.getElementById('action_primary').disabled = e.target.classList.contains('primary_email')
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
|
|
@ -4,61 +4,53 @@
|
|||
|
||||
{% block non_login_content %}
|
||||
|
||||
<div id="loginbox">
|
||||
<div class="login">
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<h2>Login</h2>
|
||||
{% 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 %}
|
||||
<div>
|
||||
<label for="id_username"
|
||||
class="visually-hidden">
|
||||
{% trans "Email" %}
|
||||
</label>
|
||||
<input type="text"
|
||||
id="id_username"
|
||||
name="login"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "Email" %}"
|
||||
required
|
||||
autofocus>
|
||||
</div>
|
||||
<div>
|
||||
<label for="id_password" class="visually-hidden">
|
||||
{% trans "Password" %}
|
||||
</label>
|
||||
<input type="password"
|
||||
id="id_password"
|
||||
name="password"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "Password" %}"
|
||||
required>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">{% trans "Login" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<a href="{% url "account_reset_password" %}"
|
||||
class="w-100 btn btn-lg btn-outline-info">
|
||||
{% trans "Forgot password?" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signup">
|
||||
<img src="https://data.coop/static/img/logo_da.svg" alt="data.coop logo">
|
||||
<div class="new_here">
|
||||
<h2>{% trans "Are you new here?" %}</h2>
|
||||
<a class="button" href="{% url "account_signup" %}">{% trans "Become a member" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
|
||||
<label for="id_username"
|
||||
class="visually-hidden">
|
||||
{% trans "E-mail" %}
|
||||
</label>
|
||||
<input type="text"
|
||||
id="id_username"
|
||||
name="login"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "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 mb-lg-2"
|
||||
placeholder="{% trans "Password" %}"
|
||||
required>
|
||||
|
||||
<button class="w-100 mb-lg-2 btn btn-lg btn-primary"
|
||||
type="submit">{% trans "Sign in" %}</button>
|
||||
</form>
|
||||
<a href="{% url "account_reset_password" %}"
|
||||
class="w-100 btn btn-lg btn-outline-info">
|
||||
{% trans "Forgot password?" %}
|
||||
</a>
|
||||
|
||||
<hr class="hr-text" data-content="{% trans "Or"|upper %}">
|
||||
|
||||
<a class="w-100 btn btn-lg btn-outline-success"
|
||||
type="submit"
|
||||
href="{% url "account_signup" %}">
|
||||
{% trans "Become a member" %}
|
||||
</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -5,18 +5,17 @@
|
|||
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-view">
|
||||
<h2>{% trans "Sign Out" %}</h2>
|
||||
<h1>{% trans "Sign Out" %}</h1>
|
||||
|
||||
<p>{% trans 'Are you sure you want to sign out?' %}</p>
|
||||
|
||||
<form method="post" action="{% url 'account_logout' %}">
|
||||
{% csrf_token %}
|
||||
{% if redirect_field_value %}
|
||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
|
||||
{% endif %}
|
||||
<button type="submit">{% trans 'Sign Out' %}</button>
|
||||
{% csrf_token %}
|
||||
{% if redirect_field_value %}
|
||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
|
||||
{% endif %}
|
||||
<button type="submit">{% trans 'Sign Out' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -7,13 +7,69 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<title>Login – {{ site.name }}</title>
|
||||
<link rel="stylesheet" href="{% static "/fonts/inter.css" %}">
|
||||
<link rel="stylesheet" href="{% static "/css/style.css" %}">
|
||||
|
||||
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
|
||||
integrity="sha384-8A6VqJCgKATsNvbzGwXr1Y1kRA+dQzm5znASKSOLMVRJ3s2FgO1h9J1HrtVp9pBF"
|
||||
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;
|
||||
}
|
||||
|
||||
.hr-text:after {
|
||||
content: attr(data-content);
|
||||
padding: 0 4px;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
background-color: #a8f3f4;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<main id="login">
|
||||
{% block non_login_content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
<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">
|
||||
|
||||
{% block non_login_content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,119 +3,210 @@
|
|||
{% 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 rel="stylesheet" href="{% static "fonts/inter.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/style.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/dark-style.css" %}">
|
||||
<script>
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
<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>
|
||||
|
||||
if (savedTheme === "dark" || (savedTheme == null && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||||
document.querySelector('html').classList.add('dark');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1> data.coop membersystem </h1>
|
||||
<div>
|
||||
<a id="theme-switcher" title="Switch theme">
|
||||
<svg id="switch-icon"
|
||||
width="20.00033mm"
|
||||
height="20.000334mm"
|
||||
viewBox="0 0 20.00033 20.000334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g id="layer1">
|
||||
<path id="path1"
|
||||
style="fill-opacity:1;stroke:none;stroke-width:0.697782;stroke-linecap:round"
|
||||
d="M 9.999906,20.000334 A 10,10 0 0 0 20.000329,9.999911 10,10 0 0 0 9.999906,0 10,10 0 0 0 0,9.999911 10,10 0 0 0 9.999906,20.000334 Z m 0,-2.00039 V 1.99988 a 8,8 0 0 1 8.000023,8.000031 8,8 0 0 1 -8.000023,8.000033 z" />
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
<a id="logout" href="{% url "account_logout" %}">Log out</a>
|
||||
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
|
||||
integrity="sha384-8A6VqJCgKATsNvbzGwXr1Y1kRA+dQzm5znASKSOLMVRJ3s2FgO1h9J1HrtVp9pBF"
|
||||
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-161MkQeTxmFupfLvH3jbFeSkoKSweR+66Tfp/ot/TuqbbOq0qssz8c91dG8I8y19"
|
||||
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_path "index" "active" %}"
|
||||
href="{% url "index" %}">
|
||||
{% trans "Dashboard" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<span>{% trans "Profile" %}</span>
|
||||
</h6>
|
||||
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
{% trans "Details" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% active_path "account_email" "active" %}"
|
||||
aria-current="page" href="{% url "account_email" %}">
|
||||
{% trans "Emails" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<span>{% trans "Membership" %}</span>
|
||||
</h6>
|
||||
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% active_path "membership-overview" "active" %}"
|
||||
aria-current="page" href="{% url "membership-overview" %}">
|
||||
{% trans "Overview" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<span>{% trans "Services" %}</span>
|
||||
</h6>
|
||||
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% active_path "services-overview" "active" %}"
|
||||
href="{% url "services-overview" %}">
|
||||
{% trans "Overview" %}
|
||||
</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>
|
||||
</header>
|
||||
<main>
|
||||
<aside>
|
||||
<div>
|
||||
<figure></figure>
|
||||
<h2>{{ user }}</h2>
|
||||
</nav>
|
||||
|
||||
{% if current_membership %}
|
||||
<dl>
|
||||
<dt>Membership</dt>
|
||||
<dd>
|
||||
Active
|
||||
</dd>
|
||||
|
||||
<dt>Period</dt>
|
||||
<dd>
|
||||
Until {{ current_period.upper }} <span class="time_remaining">({{ current_period.upper|timeuntil }})</span>
|
||||
</dd>
|
||||
|
||||
<dt>Membership type</dt>
|
||||
<dd>Normal member</dd>
|
||||
</dl>
|
||||
{% else %}
|
||||
Your membership status will be displayed here in the future.
|
||||
{% endif %}
|
||||
</div>
|
||||
</aside>
|
||||
<nav>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="/" class="{% active_path "index" "current" %}">
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% comment %}
|
||||
<li>
|
||||
<a href="/services" class="{% active_path "services" "current" %}">
|
||||
Services
|
||||
</a>
|
||||
</li>
|
||||
{% endcomment %}
|
||||
|
||||
<li>
|
||||
<a href="{% url "account_email" %}" class="{% active_path "account_email" "current" %}">
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if perms.membership.administrate_memberships %}
|
||||
<li>
|
||||
<a href="{% url "admin-members:list" %}" class="{% active_path "admin-members:list" "current" %}">
|
||||
Admin
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ol>
|
||||
</nav>
|
||||
<article>
|
||||
{% block content %}{% endblock %}
|
||||
</article>
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
<footer>
|
||||
data.coop membersystem version 0.0.1
|
||||
</footer>
|
||||
<script>
|
||||
const themeSwitcher = document.getElementById('theme-switcher');
|
||||
themeSwitcher.addEventListener('click', function() {
|
||||
themeSwitcher.classList.toggle('active')
|
||||
let isDark = document.querySelector('html').classList.toggle('dark');
|
||||
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,28 +1 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block head_title %}
|
||||
{% trans "Home" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-view">
|
||||
<h2>Welcome {{ user }}!</h2>
|
||||
<p>
|
||||
This is the new member area.
|
||||
</p>
|
||||
<p>
|
||||
It is very much under construction.
|
||||
</p>
|
||||
|
||||
{% comment %}
|
||||
<hr>
|
||||
<br>
|
||||
<div class="infobox">
|
||||
<p>
|
||||
To get started we need you to verify your email!
|
||||
</p>
|
||||
<button>Verify email</button>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,62 +1,10 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
Coming soon!
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<div class="content-view">
|
||||
<h2>Services you subscribe to</h2>
|
||||
<div class="services">
|
||||
<div>
|
||||
<div class="description">
|
||||
<h3>Passit</h3>
|
||||
<p>Passit is a service that blabla</p>
|
||||
<a href="#">Read more …</a>
|
||||
</div>
|
||||
<a>Unsubscribe</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-view">
|
||||
<h2>Available services</h2>
|
||||
<div class="services">
|
||||
<div>
|
||||
<div class="description">
|
||||
<h3>Forgejo</h3>
|
||||
<p>Forgejo is a service that blabla</p>
|
||||
<a href="#">Read more …</a>
|
||||
</div>
|
||||
<a>Subscribe</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="description">
|
||||
<h3>Mastodon</h3>
|
||||
<p>Mastodon is a service where you can write things to people around the world.</p>
|
||||
<a href="#">Read more …</a>
|
||||
</div>
|
||||
<a>Subscribe</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="description">
|
||||
<h3>Matrix</h3>
|
||||
<p>Matrix is a service that blabla</p>
|
||||
<a href="#">Read more …</a>
|
||||
</div>
|
||||
<a>Subscribe</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="description">
|
||||
<h3>NextCloud</h3>
|
||||
<p>NextCloud is a service that blabla</p>
|
||||
<a href="#">Read more …</a>
|
||||
</div>
|
||||
<a>Subscribe</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
<p>
|
||||
Services and signup to these will be
|
||||
</p>
|
||||
<p>
|
||||
This is yet to be implemented.
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
"""URLs for the membersystem."""
|
||||
from django.conf import settings
|
||||
"""URLs for the membersystem"""
|
||||
import debug_toolbar
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import include
|
||||
from django.urls import path
|
||||
from django_view_decorator import include_view_urls
|
||||
from membership.views import membership_overview
|
||||
|
||||
from .views import index
|
||||
from .views import services_overview
|
||||
|
||||
urlpatterns = [
|
||||
path("", include_view_urls(extra_modules=["project.views"])),
|
||||
path("", login_required(index), name="index"),
|
||||
path("services/", login_required(services_overview), name="services-overview"),
|
||||
path("membership/", membership_overview, name="membership-overview"),
|
||||
path("accounts/", include("allauth.urls")),
|
||||
path("_admin/", admin.site.urls),
|
||||
path("admin/", admin.site.urls),
|
||||
path("__debug__/", include(debug_toolbar.urls)),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns = [
|
||||
path("__debug__/", include("debug_toolbar.urls")),
|
||||
path("__reload__/", include("django_browser_reload.urls")),
|
||||
*urlpatterns,
|
||||
]
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
from django_view_decorator import view
|
||||
from utils.view_utils import render
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
@view(
|
||||
paths="",
|
||||
name="index",
|
||||
login_required=True,
|
||||
)
|
||||
def index(request):
|
||||
return render(request, "index.html")
|
||||
|
||||
|
||||
@view(
|
||||
paths="services/",
|
||||
name="services",
|
||||
login_required=True,
|
||||
)
|
||||
def services_overview(request):
|
||||
return render(request, "services_overview.html")
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue