Compare commits
152 Commits
v1.0.2
...
9f0a22d3d1
Author | SHA1 | Date | |
---|---|---|---|
9f0a22d3d1
|
|||
f60691846b
|
|||
8cf9dfb9b9
|
|||
c3ab61bd04
|
|||
8f895dc4d7 | |||
1187577728 | |||
8a58af3b31 | |||
0c23625147 | |||
21219b9c62 | |||
5ab8beecef | |||
1ca5133026 | |||
93bc6bb245 | |||
952c4383e7 | |||
15dd2b8f0c
|
|||
c540b6334c
|
|||
0b93968b9e | |||
97375ef6c0
|
|||
36cfcd533f
|
|||
21dbc53615
|
|||
e6f10ebdac
|
|||
47968844ce
|
|||
a435460e29
|
|||
b7c4360108
|
|||
8d8c417c50
|
|||
2b189af25b
|
|||
5a07c8a94f
|
|||
6cc1857eb6
|
|||
601534d610
|
|||
c271593839
|
|||
f351794aa0
|
|||
2793fee58c | |||
7a715df121
|
|||
9308878054
|
|||
b5ccf5b800
|
|||
5e63254439 | |||
da96506218
|
|||
b4714b896a | |||
cdb2647a4d
|
|||
cc12e3ec63
|
|||
be168c5ada
|
|||
b46ae6f856
|
|||
ec0bcbf015
|
|||
81303b8ef8 | |||
910b98fefc
|
|||
5a7a219ba8
|
|||
116451603c | |||
b2437ef9b5
|
|||
d8c9618772
|
|||
c825dee95a
|
|||
73d27e820b
|
|||
40e1b42078
|
|||
72806f0ace
|
|||
b244e01231
|
|||
76d1784aea
|
|||
56c5fa4057
|
|||
b5ef937a03
|
|||
e95a8b6e18
|
|||
635adf1360
|
|||
d5a9bf175f
|
|||
b597a6ac5b
|
|||
a704b92c3d | |||
53090b1a21 | |||
c49af0b83a | |||
5a05997d9d
|
|||
c109cd3ddd
|
|||
84304971d7
|
|||
b8b781f9a2 | |||
002128eed2 | |||
8d71783c42 | |||
a6f23df7d5
|
|||
d9c97628e2
|
|||
893534955d
|
|||
dfbf9972c2
|
|||
b5f3b3ffc1
|
|||
3aad4e7398
|
|||
b4a1b513cc
|
|||
c0c64f225c | |||
9d8f47115c
|
|||
f4156f1b94
|
|||
e60994e065
|
|||
801f711994
|
|||
e4568b410f
|
|||
c8f7986d5a | |||
d3a9c442a5
|
|||
016ab5a9c9
|
|||
7866ab7ec0
|
|||
f570ff3cd5
|
|||
6b2638c271
|
|||
5cb4183e9f
|
|||
3a20555663
|
|||
95be0042e9
|
|||
48880e7fd3
|
|||
e0030771e4
|
|||
d47799e6ee
|
|||
eae091625a
|
|||
aceb77ffb9
|
|||
338c94ed05
|
|||
290848f904 | |||
72dca54bbf | |||
117d9da3ba | |||
37efebe85b | |||
3af2ec71b6 | |||
0b4a95525b | |||
af664e481f | |||
0171f16311 | |||
296b94d237 | |||
4942553335 | |||
c1efb87180 | |||
72eead8595 | |||
ade7e583e5 | |||
4a8a101822 | |||
dd2cfa6327 | |||
2adf84b7fc | |||
2f54e64ea2 | |||
8434c0062c | |||
6d976f32bf | |||
b9d49d53f2 | |||
23243e09bb | |||
2682e9a610 | |||
5635598bbc | |||
b58a0c43cd | |||
e1f647bd02 | |||
39fd3a2471 | |||
1072e227b8 | |||
cbf7e6fe6c | |||
950922d041 | |||
78fe070cd3 | |||
51d5733578 | |||
7bd895c1df | |||
e5e94c52f2 | |||
051591cb7a | |||
0e7390b669 | |||
fe4363b83d | |||
6e80016b38 | |||
08e50ffc22 | |||
9cb65277f3 | |||
224a0fdd8c | |||
6dc7604e90 | |||
cb7f3c9f18 | |||
f910feca9e | |||
91f784872c | |||
b655135a42 | |||
58aa4983e3 | |||
6cc3cf4174 | |||
2097e67321 | |||
d773303d18 | |||
3cabcf40e7 | |||
bf29efda0a | |||
ceccba0d71 | |||
3eced33082 | |||
acb3fb4a91 | |||
420a24ebac |
@ -10,7 +10,6 @@ DJANGO_SECRET_KEY=CHANGE_ME
|
||||
DJANGO_SETTINGS_MODULE=note_kfet.settings
|
||||
CONTACT_EMAIL=tresorerie.bde@localhost
|
||||
NOTE_URL=localhost
|
||||
DOMAIN=localhost
|
||||
|
||||
# Config for mails. Only used in production
|
||||
NOTE_MAIL=notekfet@localhost
|
||||
|
5
.gitignore
vendored
@ -47,3 +47,8 @@ backups/
|
||||
env/
|
||||
venv/
|
||||
db.sqlite3
|
||||
|
||||
# ansibles customs host
|
||||
ansible/host_vars/*.yaml
|
||||
!ansible/host_vars/bde*
|
||||
ansible/hosts
|
||||
|
@ -1,6 +1,7 @@
|
||||
stages:
|
||||
- test
|
||||
- quality-assurance
|
||||
- docs
|
||||
|
||||
# Also fetch submodules
|
||||
variables:
|
||||
@ -38,6 +39,21 @@ py38-django22:
|
||||
python3-bs4 python3-setuptools tox texlive-xetex
|
||||
script: tox -e py38-django22
|
||||
|
||||
# Debian Bullseye
|
||||
py39-django22:
|
||||
stage: test
|
||||
image: debian:bullseye
|
||||
before_script:
|
||||
- >
|
||||
apt-get update &&
|
||||
apt-get install --no-install-recommends -y
|
||||
python3-django python3-django-crispy-forms
|
||||
python3-django-extensions python3-django-filters python3-django-polymorphic
|
||||
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
|
||||
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
|
||||
python3-bs4 python3-setuptools tox texlive-xetex
|
||||
script: tox -e py39-django22
|
||||
|
||||
linters:
|
||||
stage: quality-assurance
|
||||
image: debian:buster-backports
|
||||
@ -47,3 +63,17 @@ linters:
|
||||
|
||||
# Be nice to new contributors, but please use `tox`
|
||||
allow_failure: true
|
||||
|
||||
# Compile documentation
|
||||
documentation:
|
||||
stage: docs
|
||||
image: sphinxdoc/sphinx
|
||||
before_script:
|
||||
- pip install sphinx-rtd-theme
|
||||
- cd docs
|
||||
script:
|
||||
- make dirhtml
|
||||
artifacts:
|
||||
paths:
|
||||
- docs/_build
|
||||
expire_in: 1 day
|
||||
|
33
README.md
@ -69,13 +69,31 @@ accessible depuis l'ensemble de votre réseau, pratique pour tester le rendu
|
||||
de la note sur un téléphone !
|
||||
|
||||
## Installation d'une instance de production
|
||||
Pour déployer facilement la note il est possible d'utiliser le playbook Ansible (sinon vous pouvez toujours le faire a la main, voir plus bas).
|
||||
### Avec ansible
|
||||
Il vous faudra un serveur sous debian ou ubuntu connecté à internet et que vous souhaiterez accéder à cette instance de la note sur `note.nomdedomaine.tld`.
|
||||
|
||||
0. Installer Ansible sur votre machine personnelle.
|
||||
|
||||
0. (bis) cloner le dépot sur votre machine personelle.
|
||||
|
||||
1. Copier le fichier `ansible/host_example`
|
||||
``` bash
|
||||
$ cp ansible/hosts_example ansible/hosts
|
||||
```
|
||||
et ajouter sous [dev] et/ou [prod] les serveurs sur lesquels vous souhaitez installer la note.
|
||||
2. Créer un fichier `ansible/host_vars/<note.nomdedomaine.tld.yaml>` sur le modèle des fichiers existants dans `ansible/hosts` et compléter les variables nécessaires.
|
||||
|
||||
3. lancer `ansible/base.yaml -l <nomdedomaine.tld.yaml>`
|
||||
4. Aller vous faire un café, ca peux durer un moment.
|
||||
|
||||
### Installation manuelle
|
||||
|
||||
**En production on souhaite absolument utiliser les modules Python packagées dans le gestionnaire de paquet.**
|
||||
Cela permet de mettre à jour facilement les dépendances critiques telles que Django.
|
||||
|
||||
L'installation d'une instance de production néccessite **une installation de Debian Buster ou d'Ubuntu 20.04**.
|
||||
|
||||
Pour aller vite vous pouvez lancer le Playbook Ansible fournit dans ce dépôt en l'adaptant.
|
||||
Sinon vous pouvez suivre les étapes décrites ci-dessous.
|
||||
|
||||
0. Sous Debian Buster, **activer Debian Backports.** En effet Django 2.2 LTS n'est que disponible dans les backports.
|
||||
@ -261,20 +279,25 @@ Le cahier des charges initial est disponible [sur le Wiki Crans](https://wiki.cr
|
||||
La documentation des classes et fonctions est directement dans le code et est explorable à partir de la partie documentation de l'interface d'administration de Django.
|
||||
**Commentez votre code !**
|
||||
|
||||
La documentation plus haut niveau sur le développement est disponible sur [le Wiki associé au dépôt Git](https://gitlab.crans.org/bde/nk20/-/wikis/home).
|
||||
La documentation plus haut niveau sur le développement et sur l'utilisation
|
||||
est disponible sur <https://note.crans.org/doc> et également dans le dossier `docs`.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Regénérer les fichiers de traduction
|
||||
|
||||
Pour regénérer les traductions vous pouvez vous placer à la racine du projet et lancer le script `makemessages`. Il faut penser à ignorer les dossiers ne contenant pas notre code, dont le virtualenv.
|
||||
Pour regénérer les traductions vous pouvez vous placer à la racine du projet et lancer le script `makemessages`.
|
||||
Il faut penser à ignorer les dossiers ne contenant pas notre code, dont le virtualenv.
|
||||
De plus, il faut aussi extraire les variables des fichiers JavaScript.
|
||||
|
||||
```bash
|
||||
django-admin makemessages -i env
|
||||
python3 manage.py makemessages -i env
|
||||
python3 manage.py makemessages -i env -e js -d djangojs
|
||||
```
|
||||
|
||||
Une fois les fichiers édités, vous pouvez compiler les nouvelles traductions avec
|
||||
|
||||
```bash
|
||||
django-admin compilemessages
|
||||
python3 manage.py compilemessages
|
||||
python3 manage.py compilejsmessages
|
||||
```
|
||||
|
@ -7,7 +7,7 @@
|
||||
prompt: "Password of the database (leave it blank to skip database init)"
|
||||
private: yes
|
||||
vars:
|
||||
mirror: deb.debian.org
|
||||
mirror: mirror.crans.org
|
||||
roles:
|
||||
- 1-apt-basic
|
||||
- 2-nk20
|
||||
@ -16,3 +16,4 @@
|
||||
- 5-nginx
|
||||
- 6-psql
|
||||
- 7-postinstall
|
||||
- 8-docs
|
||||
|
@ -3,3 +3,4 @@ note:
|
||||
server_name: note-beta.crans.org
|
||||
git_branch: beta
|
||||
cron_enabled: false
|
||||
email: notekfet2020@lists.crans.org
|
||||
|
@ -3,3 +3,4 @@ note:
|
||||
server_name: note-dev.crans.org
|
||||
git_branch: beta
|
||||
cron_enabled: false
|
||||
email: notekfet2020@lists.crans.org
|
@ -3,3 +3,4 @@ note:
|
||||
server_name: note.crans.org
|
||||
git_branch: master
|
||||
cron_enabled: true
|
||||
email: notekfet2020@lists.crans.org
|
||||
|
@ -1,5 +1,5 @@
|
||||
[dev]
|
||||
bde3-virt.adh.crans.org
|
||||
bde-note-dev.adh.crans.org
|
||||
bde-nk20-beta.adh.crans.org
|
||||
|
||||
[prod]
|
@ -3,11 +3,12 @@
|
||||
apt_repository:
|
||||
repo: deb http://{{ mirror }}/debian buster-backports main
|
||||
state: present
|
||||
when: ansible_facts['distribution'] == "Debian"
|
||||
|
||||
- name: Install note_kfet APT dependencies
|
||||
apt:
|
||||
update_cache: true
|
||||
default_release: buster-backports
|
||||
default_release: "{{ 'buster-backports' if ansible_facts['distribution'] == 'Debian' }}"
|
||||
install_recommends: false
|
||||
name:
|
||||
# Common tools
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
- name: Use default env vars (should be updated!)
|
||||
template:
|
||||
src: "env_example"
|
||||
src: "env.j2"
|
||||
dest: "/var/www/note_kfet/.env"
|
||||
mode: 0644
|
||||
force: false
|
||||
@ -36,3 +36,13 @@
|
||||
dest: /etc/cron.d/note
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Set default directory to /var/www/note_kfet
|
||||
lineinfile:
|
||||
path: /etc/skel/.bashrc
|
||||
line: 'cd /var/www/note_kfet'
|
||||
|
||||
- name: Automatically source Python virtual environment
|
||||
lineinfile:
|
||||
path: /etc/skel/.bashrc
|
||||
line: 'source /var/www/note_kfet/env/bin/activate'
|
||||
|
23
ansible/roles/2-nk20/templates/env.j2
Normal file
@ -0,0 +1,23 @@
|
||||
DJANGO_APP_STAGE=prod
|
||||
# Only used in dev mode, change to "postgresql" if you want to use PostgreSQL in dev
|
||||
DJANGO_DEV_STORE_METHOD=sqlite
|
||||
DJANGO_DB_HOST=localhost
|
||||
DJANGO_DB_NAME=note_db
|
||||
DJANGO_DB_USER=note
|
||||
DJANGO_DB_PASSWORD={{ DB_PASSWORD }}
|
||||
DJANGO_DB_PORT=
|
||||
DJANGO_SECRET_KEY=CHANGE_ME
|
||||
DJANGO_SETTINGS_MODULE=note_kfet.settings
|
||||
CONTACT_EMAIL=tresorerie.bde@localhost
|
||||
NOTE_URL= {{note.server_name}}
|
||||
|
||||
# Config for mails. Only used in production
|
||||
NOTE_MAIL=notekfet@localhost
|
||||
EMAIL_HOST=smtp.localhost
|
||||
EMAIL_PORT=25
|
||||
EMAIL_USER=notekfet@localhost
|
||||
EMAIL_PASSWORD=CHANGE_ME
|
||||
|
||||
# Wiki configuration
|
||||
WIKI_USER=NoteKfet2020
|
||||
WIKI_PASSWORD=
|
@ -9,6 +9,11 @@
|
||||
retries: 3
|
||||
until: pkg_result is succeeded
|
||||
|
||||
- name: Check if certificate already exists.
|
||||
stat:
|
||||
path: /etc/letsencrypt/live/{{note.server_name}}/cert.pem
|
||||
register: letsencrypt_cert
|
||||
|
||||
- name: Create /etc/letsencrypt/conf.d
|
||||
file:
|
||||
path: /etc/letsencrypt/conf.d
|
||||
@ -19,3 +24,17 @@
|
||||
src: "letsencrypt/conf.d/nk20.ini.j2"
|
||||
dest: "/etc/letsencrypt/conf.d/nk20.ini"
|
||||
mode: 0644
|
||||
|
||||
- name: Stop services to allow certbot to generate a cert.
|
||||
service:
|
||||
name: nginx
|
||||
state: stopped
|
||||
|
||||
- name: Generate new certificate if one doesn't exist.
|
||||
shell: "certbot certonly --non-interactive --agree-tos --config /etc/letsencrypt/conf.d/nk20.ini -d {{note.server_name}}"
|
||||
when: letsencrypt_cert.stat.exists == False
|
||||
|
||||
- name: Restart services to allow certbot to generate a cert.
|
||||
service:
|
||||
name: nginx
|
||||
state: started
|
||||
|
@ -10,7 +10,7 @@ rsa-key-size = 4096
|
||||
# server = https://acme-staging.api.letsencrypt.org/directory
|
||||
|
||||
# Uncomment and update to register with the specified e-mail address
|
||||
email = notekfet2020@lists.crans.org
|
||||
email = {{ note.email }}
|
||||
|
||||
# Uncomment to use a text interface instead of ncurses
|
||||
text = True
|
||||
|
@ -1,5 +1,5 @@
|
||||
# the upstream component nginx needs to connect to
|
||||
upstream note{
|
||||
upstream note {
|
||||
server unix:///var/www/note_kfet/note_kfet.sock; # file socket
|
||||
}
|
||||
|
||||
@ -50,6 +50,10 @@ server {
|
||||
alias /var/www/note_kfet/static; # your Django project's static files - amend as required
|
||||
}
|
||||
|
||||
location /doc {
|
||||
alias /var/www/documentation; # The documentation of the project
|
||||
}
|
||||
|
||||
# Finally, send all non-media requests to the Django server.
|
||||
location / {
|
||||
uwsgi_pass note;
|
||||
|
@ -11,14 +11,14 @@
|
||||
until: pkg_result is succeeded
|
||||
|
||||
- name: Create role note
|
||||
when: "DB_PASSWORD|bool" # If the password is not defined, skip the installation
|
||||
when: DB_PASSWORD|length > 0 # If the password is not defined, skip the installation
|
||||
postgresql_user:
|
||||
name: note
|
||||
password: "{{ DB_PASSWORD }}"
|
||||
become_user: postgres
|
||||
|
||||
- name: Create NK20 database
|
||||
when: "DB_PASSWORD|bool"
|
||||
when: DB_PASSWORD|length >0
|
||||
postgresql_db:
|
||||
name: note_db
|
||||
owner: note
|
||||
|
@ -1,4 +1,10 @@
|
||||
---
|
||||
- name: Collect static files
|
||||
command: /var/www/note_kfet/env/bin/python manage.py collectstatic --noinput
|
||||
args:
|
||||
chdir: /var/www/note_kfet
|
||||
become_user: www-data
|
||||
|
||||
- name: Migrate Django database
|
||||
command: /var/www/note_kfet/env/bin/python manage.py migrate
|
||||
args:
|
||||
@ -11,14 +17,14 @@
|
||||
chdir: /var/www/note_kfet
|
||||
become_user: www-data
|
||||
|
||||
- name: Compile JavaScript messages
|
||||
command: /var/www/note_kfet/env/bin/python manage.py compilejsmessages
|
||||
args:
|
||||
chdir: /var/www/note_kfet
|
||||
become_user: www-data
|
||||
|
||||
- name: Install initial fixtures
|
||||
command: /var/www/note_kfet/env/bin/python manage.py loaddata initial
|
||||
args:
|
||||
chdir: /var/www/note_kfet
|
||||
become_user: postgres
|
||||
|
||||
- name: Collect static files
|
||||
command: /var/www/note_kfet/env/bin/python manage.py collectstatic --noinput
|
||||
args:
|
||||
chdir: /var/www/note_kfet
|
||||
become_user: www-data
|
||||
|
20
ansible/roles/8-docs/tasks/main.yml
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Install Sphinx and RTD theme
|
||||
pip:
|
||||
requirements: /var/www/note_kfet/docs/requirements.txt
|
||||
virtualenv: /var/www/note_kfet/env
|
||||
virtualenv_command: /usr/bin/python3 -m venv
|
||||
virtualenv_site_packages: true
|
||||
become_user: www-data
|
||||
|
||||
- name: Create documentation directory with good permissions
|
||||
file:
|
||||
path: /var/www/documentation
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: u=rwx,g=rwxs,o=rx
|
||||
|
||||
- name: Build HTML documentation
|
||||
command: /var/www/note_kfet/env/bin/sphinx-build -b dirhtml /var/www/note_kfet/docs/ /var/www/documentation/
|
||||
become_user: www-data
|
@ -15,10 +15,10 @@ class ActivityTypeViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `ActivityType` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/activity/type/
|
||||
"""
|
||||
queryset = ActivityType.objects.all()
|
||||
queryset = ActivityType.objects.order_by('id')
|
||||
serializer_class = ActivityTypeSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['name', 'can_invite', ]
|
||||
filterset_fields = ['name', 'manage_entries', 'can_invite', 'guest_entry_fee', ]
|
||||
|
||||
|
||||
class ActivityViewSet(ReadProtectedModelViewSet):
|
||||
@ -27,10 +27,16 @@ class ActivityViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Activity` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/activity/activity/
|
||||
"""
|
||||
queryset = Activity.objects.all()
|
||||
queryset = Activity.objects.order_by('id')
|
||||
serializer_class = ActivitySerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['name', 'description', 'activity_type', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'description', 'activity_type', 'location', 'creater', 'organizer', 'attendees_club',
|
||||
'date_start', 'date_end', 'valid', 'open', ]
|
||||
search_fields = ['$name', '$description', '$location', '$creater__last_name', '$creater__first_name',
|
||||
'$creater__email', '$creater__note__alias__name', '$creater__note__alias__normalized_name',
|
||||
'$organizer__name', '$organizer__email', '$organizer__note__alias__name',
|
||||
'$organizer__note__alias__normalized_name', '$attendees_club__name', '$attendees_club__email',
|
||||
'$attendees_club__note__alias__name', '$attendees_club__note__alias__normalized_name', ]
|
||||
|
||||
|
||||
class GuestViewSet(ReadProtectedModelViewSet):
|
||||
@ -39,10 +45,13 @@ class GuestViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Guest` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/activity/guest/
|
||||
"""
|
||||
queryset = Guest.objects.all()
|
||||
queryset = Guest.objects.order_by('id')
|
||||
serializer_class = GuestSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ['$last_name', '$first_name', '$inviter__alias__name', '$inviter__alias__normalized_name', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['activity', 'activity__name', 'last_name', 'first_name', 'inviter', 'inviter__alias__name',
|
||||
'inviter__alias__normalized_name', ]
|
||||
search_fields = ['$activity__name', '$last_name', '$first_name', '$inviter__user__email', '$inviter__alias__name',
|
||||
'$inviter__alias__normalized_name', ]
|
||||
|
||||
|
||||
class EntryViewSet(ReadProtectedModelViewSet):
|
||||
@ -51,7 +60,9 @@ class EntryViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Entry` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/activity/entry/
|
||||
"""
|
||||
queryset = Entry.objects.all()
|
||||
queryset = Entry.objects.order_by('id')
|
||||
serializer_class = EntrySerializer
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ['$last_name', '$first_name', '$inviter__alias__name', '$inviter__alias__normalized_name', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
||||
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
||||
'$guest__last_name', '$guest__first_name', ]
|
||||
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
headers: {"X-CSRFTOKEN": CSRF_TOKEN}
|
||||
})
|
||||
.done(function() {
|
||||
addMsg('Invité supprimé','success');
|
||||
addMsg('{% trans "Guest deleted" %}', 'success');
|
||||
$("#guests_table").load(location.pathname + " #guests_table");
|
||||
})
|
||||
.fail(function(xhr, textStatus, error) {
|
||||
|
@ -86,10 +86,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
}).done(function () {
|
||||
if (target.hasClass("table-info"))
|
||||
addMsg(
|
||||
"Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.",
|
||||
"{% trans "Entry done, but caution: the user is not a Kfet member." %}",
|
||||
"warning", 10000);
|
||||
else
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
addMsg("Entry made!", "success", 4000);
|
||||
reloadTable(true);
|
||||
}).fail(function (xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
@ -121,10 +121,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
}).done(function () {
|
||||
if (target.hasClass("table-info"))
|
||||
addMsg(
|
||||
"Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.",
|
||||
"{% trans "Entry done, but caution: the user is not a Kfet member." %}",
|
||||
"warning", 10000);
|
||||
else
|
||||
addMsg("Entrée effectuée !", "success", 4000);
|
||||
addMsg("{% trans "Entry done!" %}", "success", 4000);
|
||||
reloadTable(true);
|
||||
}).fail(function (xhr) {
|
||||
errMsg(xhr.responseJSON, 4000);
|
||||
|
@ -3,13 +3,16 @@
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from api.tests import TestAPI
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from activity.models import Activity, ActivityType, Guest, Entry
|
||||
from member.models import Club
|
||||
|
||||
from ..api.views import ActivityTypeViewSet, ActivityViewSet, EntryViewSet, GuestViewSet
|
||||
from ..models import Activity, ActivityType, Guest, Entry
|
||||
|
||||
|
||||
class TestActivities(TestCase):
|
||||
"""
|
||||
@ -173,3 +176,58 @@ class TestActivities(TestCase):
|
||||
"""
|
||||
response = self.client.get(reverse("activity:calendar_ics"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TestActivityAPI(TestAPI):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
|
||||
self.activity = Activity.objects.create(
|
||||
name="Activity",
|
||||
description="This is a test activity\non two very very long lines\nbecause this is very important.",
|
||||
location="Earth",
|
||||
activity_type=ActivityType.objects.get(name="Pot"),
|
||||
creater=self.user,
|
||||
organizer=Club.objects.get(name="Kfet"),
|
||||
attendees_club=Club.objects.get(name="Kfet"),
|
||||
date_start=timezone.now(),
|
||||
date_end=timezone.now() + timedelta(days=2),
|
||||
valid=True,
|
||||
)
|
||||
|
||||
self.guest = Guest.objects.create(
|
||||
activity=self.activity,
|
||||
inviter=self.user.note,
|
||||
last_name="GUEST",
|
||||
first_name="Guest",
|
||||
)
|
||||
|
||||
self.entry = Entry.objects.create(
|
||||
activity=self.activity,
|
||||
note=self.user.note,
|
||||
guest=self.guest,
|
||||
)
|
||||
|
||||
def test_activity_api(self):
|
||||
"""
|
||||
Load Activity API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ActivityViewSet, "/api/activity/activity/")
|
||||
|
||||
def test_activity_type_api(self):
|
||||
"""
|
||||
Load ActivityType API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ActivityTypeViewSet, "/api/activity/type/")
|
||||
|
||||
def test_entry_api(self):
|
||||
"""
|
||||
Load Entry API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(EntryViewSet, "/api/activity/entry/")
|
||||
|
||||
def test_guest_api(self):
|
||||
"""
|
||||
Load Guest API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(GuestViewSet, "/api/activity/guest/")
|
||||
|
@ -4,10 +4,14 @@
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from django.utils import timezone
|
||||
from rest_framework import serializers
|
||||
from member.api.serializers import ProfileSerializer, MembershipSerializer
|
||||
from note.api.serializers import NoteSerializer
|
||||
from note.models import Alias
|
||||
|
||||
|
||||
class UserSerializer(ModelSerializer):
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Users.
|
||||
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
||||
@ -22,7 +26,7 @@ class UserSerializer(ModelSerializer):
|
||||
)
|
||||
|
||||
|
||||
class ContentTypeSerializer(ModelSerializer):
|
||||
class ContentTypeSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Users.
|
||||
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
||||
@ -31,3 +35,42 @@ class ContentTypeSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = ContentType
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class OAuthSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Informations that are transmitted by OAuth.
|
||||
For now, this includes user, profile and valid memberships.
|
||||
This should be better managed later.
|
||||
"""
|
||||
normalized_name = serializers.SerializerMethodField()
|
||||
|
||||
profile = ProfileSerializer()
|
||||
|
||||
note = NoteSerializer()
|
||||
|
||||
memberships = serializers.SerializerMethodField()
|
||||
|
||||
def get_normalized_name(self, obj):
|
||||
return Alias.normalize(obj.username)
|
||||
|
||||
def get_memberships(self, obj):
|
||||
return serializers.ListSerializer(child=MembershipSerializer()).to_representation(
|
||||
obj.memberships.filter(date_start__lte=timezone.now(), date_end__gte=timezone.now()))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = (
|
||||
'id',
|
||||
'username',
|
||||
'normalized_name',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'is_superuser',
|
||||
'is_active',
|
||||
'is_staff',
|
||||
'profile',
|
||||
'note',
|
||||
'memberships',
|
||||
)
|
||||
|
240
apps/api/tests.py
Normal file
@ -0,0 +1,240 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import json
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
from urllib.parse import quote_plus
|
||||
from warnings import warn
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models.fields.files import ImageFieldFile
|
||||
from django.test import TestCase
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from member.models import Membership, Club
|
||||
from note.models import NoteClub, NoteUser, Alias, Note
|
||||
from permission.models import PermissionMask, Permission, Role
|
||||
from phonenumbers import PhoneNumber
|
||||
from rest_framework.filters import SearchFilter, OrderingFilter
|
||||
|
||||
from .viewsets import ContentTypeViewSet, UserViewSet
|
||||
|
||||
|
||||
class TestAPI(TestCase):
|
||||
"""
|
||||
Load API pages and check that filters are working.
|
||||
"""
|
||||
fixtures = ('initial', )
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = User.objects.create_superuser(
|
||||
username="adminapi",
|
||||
password="adminapi",
|
||||
email="adminapi@example.com",
|
||||
last_name="Admin",
|
||||
first_name="Admin",
|
||||
)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
sess = self.client.session
|
||||
sess["permission_mask"] = 42
|
||||
sess.save()
|
||||
|
||||
def check_viewset(self, viewset, url):
|
||||
"""
|
||||
This function should be called inside a unit test.
|
||||
This loads the viewset and for each filter entry, it checks that the filter is running good.
|
||||
"""
|
||||
resp = self.client.get(url + "?format=json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
model = viewset.serializer_class.Meta.model
|
||||
|
||||
if not model.objects.exists(): # pragma: no cover
|
||||
warn(f"Warning: unable to test API filters for the model {model._meta.verbose_name} "
|
||||
"since there is no instance of it.")
|
||||
return
|
||||
|
||||
if hasattr(viewset, "filter_backends"):
|
||||
backends = viewset.filter_backends
|
||||
obj = model.objects.last()
|
||||
|
||||
if DjangoFilterBackend in backends:
|
||||
# Specific search
|
||||
for field in viewset.filterset_fields:
|
||||
obj = self.fix_note_object(obj, field)
|
||||
|
||||
value = self.get_value(obj, field)
|
||||
if value is None: # pragma: no cover
|
||||
warn(f"Warning: the filter {field} for the model {model._meta.verbose_name} "
|
||||
"has not been tested.")
|
||||
continue
|
||||
resp = self.client.get(url + f"?format=json&{field}={quote_plus(str(value))}")
|
||||
self.assertEqual(resp.status_code, 200, f"The filter {field} for the model "
|
||||
f"{model._meta.verbose_name} does not work. "
|
||||
f"Given parameter: {value}")
|
||||
content = json.loads(resp.content)
|
||||
self.assertGreater(content["count"], 0, f"The filter {field} for the model "
|
||||
f"{model._meta.verbose_name} does not work. "
|
||||
f"Given parameter: {value}")
|
||||
|
||||
if OrderingFilter in backends:
|
||||
# Ensure that ordering is working well
|
||||
for field in viewset.ordering_fields:
|
||||
resp = self.client.get(url + f"?ordering={field}")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
resp = self.client.get(url + f"?ordering=-{field}")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
if SearchFilter in backends:
|
||||
# Basic search
|
||||
for field in viewset.search_fields:
|
||||
obj = self.fix_note_object(obj, field)
|
||||
|
||||
if field[0] == '$' or field[0] == '=':
|
||||
field = field[1:]
|
||||
value = self.get_value(obj, field)
|
||||
if value is None: # pragma: no cover
|
||||
warn(f"Warning: the filter {field} for the model {model._meta.verbose_name} "
|
||||
"has not been tested.")
|
||||
continue
|
||||
resp = self.client.get(url + f"?format=json&search={quote_plus(str(value))}")
|
||||
self.assertEqual(resp.status_code, 200, f"The filter {field} for the model "
|
||||
f"{model._meta.verbose_name} does not work. "
|
||||
f"Given parameter: {value}")
|
||||
content = json.loads(resp.content)
|
||||
self.assertGreater(content["count"], 0, f"The filter {field} for the model "
|
||||
f"{model._meta.verbose_name} does not work. "
|
||||
f"Given parameter: {value}")
|
||||
|
||||
self.check_permissions(url, obj)
|
||||
|
||||
def check_permissions(self, url, obj):
|
||||
"""
|
||||
Check that permissions are working
|
||||
"""
|
||||
# Drop rights
|
||||
self.user.is_superuser = False
|
||||
self.user.save()
|
||||
sess = self.client.session
|
||||
sess["permission_mask"] = 0
|
||||
sess.save()
|
||||
|
||||
# Delete user permissions
|
||||
for m in Membership.objects.filter(user=self.user).all():
|
||||
m.roles.clear()
|
||||
m.save()
|
||||
|
||||
# Create a new role, which will have the checking permission
|
||||
role = Role.objects.get_or_create(name="β-tester")[0]
|
||||
role.permissions.clear()
|
||||
role.save()
|
||||
membership = Membership.objects.get_or_create(user=self.user, club=Club.objects.get(name="BDE"))[0]
|
||||
membership.roles.set([role])
|
||||
membership.save()
|
||||
|
||||
# Ensure that the access to the object is forbidden without permission
|
||||
resp = self.client.get(url + f"{obj.pk}/")
|
||||
self.assertEqual(resp.status_code, 404, f"Mysterious access to {url}{obj.pk}/ for {obj}")
|
||||
|
||||
obj.refresh_from_db()
|
||||
|
||||
# There are problems with polymorphism
|
||||
if isinstance(obj, Note) and hasattr(obj, "note_ptr"):
|
||||
obj = obj.note_ptr
|
||||
|
||||
mask = PermissionMask.objects.get(rank=0)
|
||||
|
||||
for field in obj._meta.fields:
|
||||
# Build permission query
|
||||
value = self.get_value(obj, field.name)
|
||||
if isinstance(value, date) or isinstance(value, datetime):
|
||||
value = value.isoformat()
|
||||
elif isinstance(value, ImageFieldFile):
|
||||
value = value.name
|
||||
elif isinstance(value, Decimal):
|
||||
value = str(value)
|
||||
query = json.dumps({field.name: value})
|
||||
|
||||
# Create sample permission
|
||||
permission = Permission.objects.get_or_create(
|
||||
model=ContentType.objects.get_for_model(obj._meta.model),
|
||||
query=query,
|
||||
mask=mask,
|
||||
type="view",
|
||||
permanent=False,
|
||||
description=f"Can view {obj._meta.verbose_name}",
|
||||
)[0]
|
||||
role.permissions.set([permission])
|
||||
role.save()
|
||||
|
||||
# Check that the access is possible
|
||||
resp = self.client.get(url + f"{obj.pk}/")
|
||||
self.assertEqual(resp.status_code, 200, f"Permission {permission.query} is not working "
|
||||
f"for the model {obj._meta.verbose_name}")
|
||||
|
||||
# Restore rights
|
||||
self.user.is_superuser = True
|
||||
self.user.save()
|
||||
sess = self.client.session
|
||||
sess["permission_mask"] = 42
|
||||
sess.save()
|
||||
|
||||
@staticmethod
|
||||
def get_value(obj, key: str):
|
||||
"""
|
||||
Resolve the queryset filter to get the Python value of an object.
|
||||
"""
|
||||
if hasattr(obj, "all"):
|
||||
# obj is a RelatedManager
|
||||
obj = obj.last()
|
||||
|
||||
if obj is None: # pragma: no cover
|
||||
return None
|
||||
|
||||
if '__' not in key:
|
||||
obj = getattr(obj, key)
|
||||
if hasattr(obj, "pk"):
|
||||
return obj.pk
|
||||
elif hasattr(obj, "all"):
|
||||
if not obj.exists(): # pragma: no cover
|
||||
return None
|
||||
return obj.last().pk
|
||||
elif isinstance(obj, bool):
|
||||
return int(obj)
|
||||
elif isinstance(obj, datetime):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, PhoneNumber):
|
||||
return obj.raw_input
|
||||
return obj
|
||||
|
||||
key, remaining = key.split('__', 1)
|
||||
return TestAPI.get_value(getattr(obj, key), remaining)
|
||||
|
||||
@staticmethod
|
||||
def fix_note_object(obj, field):
|
||||
"""
|
||||
When querying an object that has a noteclub or a noteuser field,
|
||||
ensure that the object has a good value.
|
||||
"""
|
||||
if isinstance(obj, Alias):
|
||||
if "noteuser" in field:
|
||||
return NoteUser.objects.last().alias.last()
|
||||
elif "noteclub" in field:
|
||||
return NoteClub.objects.last().alias.last()
|
||||
elif isinstance(obj, Note):
|
||||
if "noteuser" in field:
|
||||
return NoteUser.objects.last()
|
||||
elif "noteclub" in field:
|
||||
return NoteClub.objects.last()
|
||||
return obj
|
||||
|
||||
|
||||
class TestBasicAPI(TestAPI):
|
||||
def test_user_api(self):
|
||||
"""
|
||||
Load the user page.
|
||||
"""
|
||||
self.check_viewset(ContentTypeViewSet, "/api/models/")
|
||||
self.check_viewset(UserViewSet, "/api/user/")
|
@ -5,6 +5,7 @@ from django.conf import settings
|
||||
from django.conf.urls import url, include
|
||||
from rest_framework import routers
|
||||
|
||||
from .views import UserInformationView
|
||||
from .viewsets import ContentTypeViewSet, UserViewSet
|
||||
|
||||
# Routers provide an easy way of automatically determining the URL conf.
|
||||
@ -47,5 +48,6 @@ app_name = 'api'
|
||||
# Additionally, we include login URLs for the browsable API.
|
||||
urlpatterns = [
|
||||
url('^', include(router.urls)),
|
||||
url('^me/', UserInformationView.as_view()),
|
||||
url('^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
]
|
||||
|
20
apps/api/views.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.generics import RetrieveAPIView
|
||||
|
||||
from .serializers import OAuthSerializer
|
||||
|
||||
|
||||
class UserInformationView(RetrieveAPIView):
|
||||
"""
|
||||
These fields are give to OAuth authenticators.
|
||||
"""
|
||||
serializer_class = OAuthSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return User.objects.filter(pk=self.request.user.pk)
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
@ -6,6 +6,7 @@ from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
|
||||
from permission.backends import PermissionBackend
|
||||
from note_kfet.middlewares import get_current_session
|
||||
@ -48,12 +49,13 @@ class UserViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/users/
|
||||
then render it on /api/user/
|
||||
"""
|
||||
queryset = User.objects.all()
|
||||
queryset = User.objects
|
||||
serializer_class = UserSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ]
|
||||
filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active',
|
||||
'note__alias__name', 'note__alias__normalized_name', ]
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
@ -106,7 +108,10 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/users/
|
||||
then render it on /api/models/
|
||||
"""
|
||||
queryset = ContentType.objects.all()
|
||||
queryset = ContentType.objects.order_by('id')
|
||||
serializer_class = ContentTypeSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['id', 'app_label', 'model', ]
|
||||
search_fields = ['$app_label', '$model', ]
|
||||
|
@ -15,7 +15,7 @@ class ChangelogViewSet(ReadOnlyProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Changelog` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/logs/
|
||||
"""
|
||||
queryset = Changelog.objects.all()
|
||||
queryset = Changelog.objects.order_by('id')
|
||||
serializer_class = ChangelogSerializer
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter]
|
||||
filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip', ]
|
||||
|
@ -1,7 +1,8 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from rest_framework.filters import SearchFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
|
||||
from .serializers import ProfileSerializer, ClubSerializer, MembershipSerializer
|
||||
@ -14,8 +15,15 @@ class ProfileViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Profile` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/members/profile/
|
||||
"""
|
||||
queryset = Profile.objects.all()
|
||||
queryset = Profile.objects.order_by('id')
|
||||
serializer_class = ProfileSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['user', 'user__first_name', 'user__last_name', 'user__username', 'user__email',
|
||||
'user__note__alias__name', 'user__note__alias__normalized_name', 'phone_number', "section",
|
||||
'department', 'promotion', 'address', 'paid', 'ml_events_registration', 'ml_sport_registration',
|
||||
'ml_art_registration', 'report_frequency', 'email_confirmed', 'registration_valid', ]
|
||||
search_fields = ['$user__first_name', '$user__last_name', '$user__username', '$user__email',
|
||||
'$user__note__alias__name', '$user__note__alias__normalized_name', ]
|
||||
|
||||
|
||||
class ClubViewSet(ReadProtectedModelViewSet):
|
||||
@ -24,10 +32,13 @@ class ClubViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Club` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/members/club/
|
||||
"""
|
||||
queryset = Club.objects.all()
|
||||
queryset = Club.objects.order_by('id')
|
||||
serializer_class = ClubSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ['$name', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'email', 'note__alias__name', 'note__alias__normalized_name', 'parent_club',
|
||||
'parent_club__name', 'require_memberships', 'membership_fee_paid', 'membership_fee_unpaid',
|
||||
'membership_duration', 'membership_start', 'membership_end', ]
|
||||
search_fields = ['$name', '$email', '$note__alias__name', '$note__alias__normalized_name', ]
|
||||
|
||||
|
||||
class MembershipViewSet(ReadProtectedModelViewSet):
|
||||
@ -36,5 +47,14 @@ class MembershipViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Membership` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/members/membership/
|
||||
"""
|
||||
queryset = Membership.objects.all()
|
||||
queryset = Membership.objects.order_by('id')
|
||||
serializer_class = MembershipSerializer
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name', 'club__note__alias__normalized_name',
|
||||
'user__username', 'user__last_name', 'user__first_name', 'user__email',
|
||||
'user__note__alias__name', 'user__note__alias__normalized_name',
|
||||
'date_start', 'date_end', 'fee', 'roles', ]
|
||||
ordering_fields = ['id', 'date_start', 'date_end', ]
|
||||
search_fields = ['$club__name', '$club__email', '$club__note__alias__name', '$club__note__alias__normalized_name',
|
||||
'$user__username', '$user__last_name', '$user__first_name', '$user__email',
|
||||
'$user__note__alias__name', '$user__note__alias__normalized_name', '$roles__name', ]
|
||||
|
17
apps/member/auth.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from cas_server.auth import DjangoAuthUser # pragma: no cover
|
||||
from note.models import Alias
|
||||
|
||||
|
||||
class CustomAuthUser(DjangoAuthUser): # pragma: no cover
|
||||
"""
|
||||
Override Django Auth User model to define a custom Matrix username.
|
||||
"""
|
||||
|
||||
def attributs(self):
|
||||
d = super().attributs()
|
||||
if self.user:
|
||||
d["normalized_name"] = Alias.normalize(self.user.username)
|
||||
return d
|
@ -0,0 +1,50 @@
|
||||
import sys
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def give_note_account_permissions(apps, schema_editor):
|
||||
"""
|
||||
Automatically manage the membership of the Note account.
|
||||
"""
|
||||
User = apps.get_model("auth", "user")
|
||||
Membership = apps.get_model("member", "membership")
|
||||
Role = apps.get_model("permission", "role")
|
||||
|
||||
note = User.objects.filter(username="note")
|
||||
if not note.exists():
|
||||
# We are in a test environment, don't log error message
|
||||
if len(sys.argv) > 1 and sys.argv[1] == 'test':
|
||||
return
|
||||
print("Warning: Note account was not found. The note account was not imported.")
|
||||
print("Make sure you have imported the NK15 database. The new import script handles correctly the permissions.")
|
||||
print("This migration will be ignored, you can re-run it if you forgot the note account or ignore it if you "
|
||||
"don't want this account.")
|
||||
return
|
||||
|
||||
note = note.get()
|
||||
|
||||
# Set for the two clubs a large expiration date and the correct role.
|
||||
for m in Membership.objects.filter(user_id=note.id).all():
|
||||
m.date_end = "3142-12-12"
|
||||
m.roles.set(Role.objects.filter(name="PC Kfet").all())
|
||||
m.save()
|
||||
# By default, the note account is only authorized to be logged from localhost.
|
||||
note.password = "ipbased$127.0.0.1"
|
||||
note.is_active = True
|
||||
note.save()
|
||||
# Ensure that the note of the account is disabled
|
||||
note.note.inactivity_reason = 'forced'
|
||||
note.note.is_active = False
|
||||
note.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('member', '0005_remove_null_tag_on_charfields'),
|
||||
('permission', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(give_note_account_permissions),
|
||||
]
|
23
apps/member/migrations/0007_auto_20210313_1235.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.19 on 2021-03-13 11:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('member', '0006_create_note_account_bde_membership'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='membership',
|
||||
name='roles',
|
||||
field=models.ManyToManyField(related_name='memberships', to='permission.Role', verbose_name='roles'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='promotion',
|
||||
field=models.PositiveSmallIntegerField(default=2021, help_text='Year of entry to the school (None if not ENS student)', null=True, verbose_name='promotion'),
|
||||
),
|
||||
]
|
@ -313,6 +313,7 @@ class Membership(models.Model):
|
||||
|
||||
roles = models.ManyToManyField(
|
||||
"permission.Role",
|
||||
related_name="memberships",
|
||||
verbose_name=_("roles"),
|
||||
)
|
||||
|
||||
|
@ -14,7 +14,7 @@ function create_alias (e) {
|
||||
}).done(function () {
|
||||
// Reload table
|
||||
$('#alias_table').load(location.pathname + ' #alias_table')
|
||||
addMsg('Alias ajouté', 'success')
|
||||
addMsg(gettext('Alias successfully added'), 'success')
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
})
|
||||
@ -22,7 +22,7 @@ function create_alias (e) {
|
||||
|
||||
/**
|
||||
* On click of "delete", delete the alias
|
||||
* @param Integer button_id Alias id to remove
|
||||
* @param button_id:Integer Alias id to remove
|
||||
*/
|
||||
function delete_button (button_id) {
|
||||
$.ajax({
|
||||
@ -30,7 +30,7 @@ function delete_button (button_id) {
|
||||
method: 'DELETE',
|
||||
headers: { 'X-CSRFTOKEN': CSRF_TOKEN }
|
||||
}).done(function () {
|
||||
addMsg('Alias supprimé', 'success')
|
||||
addMsg(gettext('Alias successfully deleted'), 'success')
|
||||
$('#alias_table').load(location.pathname + ' #alias_table')
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
|
@ -43,8 +43,24 @@ class UserTable(tables.Table):
|
||||
|
||||
section = tables.Column(accessor='profile__section')
|
||||
|
||||
# Override the column to let replace the URL
|
||||
email = tables.EmailColumn(linkify=lambda record: "mailto:{}".format(record.email))
|
||||
|
||||
balance = tables.Column(accessor='note__balance', verbose_name=_("Balance"))
|
||||
|
||||
def render_email(self, record, value):
|
||||
# Replace the email by a dash if the user can't see the profile detail
|
||||
# Replace also the URL
|
||||
if not PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile):
|
||||
value = "—"
|
||||
record.email = value
|
||||
return value
|
||||
|
||||
def render_section(self, record, value):
|
||||
return value \
|
||||
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile) \
|
||||
else "—"
|
||||
|
||||
def render_balance(self, record, value):
|
||||
return pretty_money(value)\
|
||||
if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", record.note) else "—"
|
||||
|
@ -48,7 +48,7 @@
|
||||
<dd class="col-xl-6">
|
||||
<a class="badge badge-secondary" href="{% url 'member:club_alias' club.pk %}">
|
||||
<i class="fa fa-edit"></i>
|
||||
{% trans 'Manage aliases' %} ({{ club.note.alias_set.all|length }})
|
||||
{% trans 'Manage aliases' %} ({{ club.note.alias.all|length }})
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
|
@ -21,29 +21,31 @@
|
||||
<dd class="col-xl-6">
|
||||
<a class="badge badge-secondary" href="{% url 'member:user_alias' user_object.pk %}">
|
||||
<i class="fa fa-edit"></i>
|
||||
{% trans 'Manage aliases' %} ({{ user_object.note.alias_set.all|length }})
|
||||
{% trans 'Manage aliases' %} ({{ user_object.note.alias.all|length }})
|
||||
</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
|
||||
{% if "member.view_profile"|has_perm:user_object.profile %}
|
||||
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
|
||||
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
|
||||
</dd>
|
||||
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
|
||||
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
|
||||
|
||||
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
|
||||
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
|
||||
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
|
||||
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
|
||||
|
||||
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
|
||||
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% load i18n perms %}
|
||||
|
||||
{% block content %}
|
||||
{% if "member.change_profile_registration_valid"|has_perm:user %}
|
||||
{% if can_manage_registrations %}
|
||||
<a class="btn btn-block btn-secondary mb-3" href="{% url 'registration:future_user_list' %}">
|
||||
<i class="fa fa-user-plus"></i> {% trans "Registrations" %}
|
||||
</a>
|
||||
|
@ -5,17 +5,20 @@ import hashlib
|
||||
import os
|
||||
from datetime import date, timedelta
|
||||
|
||||
from api.tests import TestAPI
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from member.models import Club, Membership, Profile
|
||||
from note.models import Alias, NoteSpecial
|
||||
from permission.models import Role
|
||||
from treasury.models import SogeCredit
|
||||
|
||||
from ..api.views import ClubViewSet, MembershipViewSet, ProfileViewSet
|
||||
from ..models import Club, Membership, Profile
|
||||
|
||||
"""
|
||||
Create some users and clubs and test that all pages are rendering properly
|
||||
and that memberships are working.
|
||||
@ -403,3 +406,46 @@ class TestMemberships(TestCase):
|
||||
self.user.password = "custom_nk15$1$" + salt + "|" + hashed
|
||||
self.user.save()
|
||||
self.assertTrue(self.user.check_password(password))
|
||||
|
||||
|
||||
class TestMemberAPI(TestAPI):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
|
||||
self.user.profile.registration_valid = True
|
||||
self.user.profile.email_confirmed = True
|
||||
self.user.profile.phone_number = "0600000000"
|
||||
self.user.profile.section = "1A0"
|
||||
self.user.profile.department = "A0"
|
||||
self.user.profile.address = "Earth"
|
||||
self.user.profile.save()
|
||||
|
||||
self.club = Club.objects.create(
|
||||
name="totoclub",
|
||||
parent_club=Club.objects.get(name="BDE"),
|
||||
membership_start=date(year=1970, month=1, day=1),
|
||||
membership_end=date(year=2040, month=1, day=1),
|
||||
membership_duration=365 * 10,
|
||||
)
|
||||
self.bde_membership = Membership.objects.create(user=self.user, club=Club.objects.get(name="BDE"))
|
||||
self.membership = Membership.objects.create(user=self.user, club=self.club)
|
||||
self.membership.roles.add(Role.objects.get(name="Bureau de club"))
|
||||
self.membership.save()
|
||||
|
||||
def test_club_api(self):
|
||||
"""
|
||||
Load Club API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ClubViewSet, "/api/members/club/")
|
||||
|
||||
def test_profile_api(self):
|
||||
"""
|
||||
Load Profile API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ProfileViewSet, "/api/members/profile/")
|
||||
|
||||
def test_membership_api(self):
|
||||
"""
|
||||
Load Membership API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(MembershipViewSet, "/api/members/membership/")
|
||||
|
@ -70,10 +70,11 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||
form.fields['email'].required = True
|
||||
form.fields['email'].help_text = _("This address must be valid.")
|
||||
|
||||
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
|
||||
data=self.request.POST if self.request.POST else None)
|
||||
if not self.object.profile.report_frequency:
|
||||
del context['profile_form'].fields["last_report"]
|
||||
if PermissionBackend.check_perm(self.request.user, "member.change_profile", context['user_object'].profile):
|
||||
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
|
||||
data=self.request.POST if self.request.POST else None)
|
||||
if not self.object.profile.report_frequency:
|
||||
del context['profile_form'].fields["last_report"]
|
||||
|
||||
return context
|
||||
|
||||
@ -234,6 +235,13 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||
|
||||
return qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
pre_registered_users = User.objects.filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))\
|
||||
.filter(profile__registration_valid=False)
|
||||
context["can_manage_registrations"] = pre_registered_users.exists()
|
||||
return context
|
||||
|
||||
|
||||
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
@ -247,8 +255,8 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
note = context['object'].note
|
||||
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
|
||||
.filter_queryset(self.request.user, Alias, "view")).all())
|
||||
context["aliases"] = AliasTable(
|
||||
note.alias.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
|
||||
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
|
||||
note=context["object"].note,
|
||||
name="",
|
||||
@ -450,8 +458,8 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
note = context['object'].note
|
||||
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
|
||||
.filter_queryset(self.request.user, Alias, "view")).all())
|
||||
context["aliases"] = AliasTable(note.alias.filter(
|
||||
PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
|
||||
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
|
||||
note=context["object"].note,
|
||||
name="",
|
||||
@ -617,9 +625,6 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
# Retrieve form data
|
||||
credit_type = form.cleaned_data["credit_type"]
|
||||
credit_amount = form.cleaned_data["credit_amount"]
|
||||
last_name = form.cleaned_data["last_name"]
|
||||
first_name = form.cleaned_data["first_name"]
|
||||
bank = form.cleaned_data["bank"]
|
||||
soge = form.cleaned_data["soge"] and not user.profile.soge and (club.name == "BDE" or club.name == "Kfet")
|
||||
|
||||
if not credit_type:
|
||||
@ -650,8 +655,8 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
if club.name != "Kfet" and club.parent_club and not Membership.objects.filter(
|
||||
user=form.instance.user,
|
||||
club=club.parent_club,
|
||||
date_start__gte=club.parent_club.membership_start,
|
||||
date_end__lte=club.parent_club.membership_end,
|
||||
date_start__lte=timezone.now(),
|
||||
date_end__gte=club.parent_club.membership_end,
|
||||
).exists():
|
||||
form.add_error('user', _('User is not a member of the parent club') + ' ' + club.parent_club.name)
|
||||
error = True
|
||||
@ -666,15 +671,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
.format(form.instance.club.membership_end))
|
||||
error = True
|
||||
|
||||
if credit_amount:
|
||||
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
|
||||
if not last_name:
|
||||
form.add_error('last_name', _("This field is required."))
|
||||
if not first_name:
|
||||
form.add_error('first_name', _("This field is required."))
|
||||
if not bank and credit_type.special_type == "Chèque":
|
||||
form.add_error('bank', _("This field is required."))
|
||||
return self.form_invalid(form)
|
||||
if credit_amount and not SpecialTransaction.validate_payment_form(form):
|
||||
# Check that special information for payment are filled
|
||||
error = True
|
||||
|
||||
return not error
|
||||
|
||||
@ -736,6 +735,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
# When we renew the BDE membership, we update the profile section
|
||||
# that should happens at least once a year.
|
||||
user.profile.section = user.profile.section_generated
|
||||
user.profile._force_save = True
|
||||
user.profile.save()
|
||||
|
||||
# Credit note before the membership is created.
|
||||
|
@ -15,29 +15,37 @@ from permission.backends import PermissionBackend
|
||||
|
||||
from .serializers import NotePolymorphicSerializer, AliasSerializer, ConsumerSerializer,\
|
||||
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
|
||||
from ..models.notes import Note, Alias
|
||||
from ..models.notes import Note, Alias, NoteUser, NoteClub, NoteSpecial
|
||||
from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
|
||||
|
||||
|
||||
class NotePolymorphicViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `Note` objects (with polymorhism), serialize it to JSON with the given serializer,
|
||||
The djangorestframework plugin will get all `Note` objects (with polymorhism),
|
||||
serialize it to JSON with the given serializer,
|
||||
then render it on /api/note/note/
|
||||
"""
|
||||
queryset = Note.objects.all()
|
||||
queryset = Note.objects.order_by('id')
|
||||
serializer_class = NotePolymorphicSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||
filterset_fields = ['polymorphic_ctype', 'is_active', ]
|
||||
search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model', ]
|
||||
ordering_fields = ['alias__name', 'alias__normalized_name']
|
||||
filterset_fields = ['alias__name', 'polymorphic_ctype', 'is_active', 'balance', 'last_negative', 'created_at', ]
|
||||
search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model',
|
||||
'$noteuser__user__last_name', '$noteuser__user__first_name', '$noteuser__user__email',
|
||||
'$noteuser__user__email', '$noteclub__club__email', ]
|
||||
ordering_fields = ['alias__name', 'alias__normalized_name', 'balance', 'created_at', ]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Parse query and apply filters.
|
||||
:return: The filtered set of requested notes
|
||||
"""
|
||||
queryset = super().get_queryset().distinct()
|
||||
user = self.request.user
|
||||
get_current_session().setdefault("permission_mask", 42)
|
||||
queryset = self.queryset.filter(PermissionBackend.filter_queryset(user, Note, "view")
|
||||
| PermissionBackend.filter_queryset(user, NoteUser, "view")
|
||||
| PermissionBackend.filter_queryset(user, NoteClub, "view")
|
||||
| PermissionBackend.filter_queryset(user, NoteSpecial, "view")).distinct()
|
||||
|
||||
alias = self.request.query_params.get("alias", ".*")
|
||||
queryset = queryset.filter(
|
||||
@ -55,18 +63,18 @@ class AliasViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Alias` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/aliases/
|
||||
"""
|
||||
queryset = Alias.objects.all()
|
||||
queryset = Alias.objects
|
||||
serializer_class = AliasSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
||||
filterset_fields = ['note']
|
||||
ordering_fields = ['name', 'normalized_name']
|
||||
filterset_fields = ['note', 'note__noteuser__user', 'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
||||
ordering_fields = ['name', 'normalized_name', ]
|
||||
|
||||
def get_serializer_class(self):
|
||||
serializer_class = self.serializer_class
|
||||
if self.request.method in ['PUT', 'PATCH']:
|
||||
# alias owner cannot be change once establish
|
||||
setattr(serializer_class.Meta, 'read_only_fields', ('note',))
|
||||
serializer_class.Meta.read_only_fields = ('note',)
|
||||
return serializer_class
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
@ -74,7 +82,7 @@ class AliasViewSet(ReadProtectedModelViewSet):
|
||||
try:
|
||||
self.perform_destroy(instance)
|
||||
except ValidationError as e:
|
||||
return Response({e.code: e.message}, status.HTTP_400_BAD_REQUEST)
|
||||
return Response({e.code: str(e)}, status.HTTP_400_BAD_REQUEST)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
def get_queryset(self):
|
||||
@ -106,12 +114,12 @@ class AliasViewSet(ReadProtectedModelViewSet):
|
||||
|
||||
|
||||
class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
|
||||
queryset = Alias.objects.all()
|
||||
queryset = Alias.objects
|
||||
serializer_class = ConsumerSerializer
|
||||
filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
|
||||
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
||||
filterset_fields = ['note']
|
||||
ordering_fields = ['name', 'normalized_name']
|
||||
filterset_fields = ['note', 'note__noteuser__user', 'note__noteclub__club', 'note__polymorphic_ctype__model', ]
|
||||
ordering_fields = ['name', 'normalized_name', ]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
@ -157,10 +165,11 @@ class TemplateCategoryViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `TemplateCategory` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/note/transaction/category/
|
||||
"""
|
||||
queryset = TemplateCategory.objects.order_by("name").all()
|
||||
queryset = TemplateCategory.objects.order_by('name')
|
||||
serializer_class = TemplateCategorySerializer
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ['$name', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'templates', 'templates__name']
|
||||
search_fields = ['$name', '$templates__name', ]
|
||||
|
||||
|
||||
class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
||||
@ -169,11 +178,12 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
||||
The djangorestframework plugin will get all `TransactionTemplate` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/note/transaction/template/
|
||||
"""
|
||||
queryset = TransactionTemplate.objects.order_by("name").all()
|
||||
queryset = TransactionTemplate.objects.order_by('name')
|
||||
serializer_class = TransactionTemplateSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
filterset_fields = ['name', 'amount', 'display', 'category', ]
|
||||
search_fields = ['$name', ]
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||
filterset_fields = ['name', 'amount', 'display', 'category', 'category__name', ]
|
||||
search_fields = ['$name', '$category__name', ]
|
||||
ordering_fields = ['amount', ]
|
||||
|
||||
|
||||
class TransactionViewSet(ReadProtectedModelViewSet):
|
||||
@ -182,13 +192,17 @@ class TransactionViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Transaction` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/note/transaction/transaction/
|
||||
"""
|
||||
queryset = Transaction.objects.order_by("-created_at").all()
|
||||
queryset = Transaction.objects.order_by('-created_at')
|
||||
serializer_class = TransactionPolymorphicSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
|
||||
filterset_fields = ["source", "source_alias", "destination", "destination_alias", "quantity",
|
||||
"polymorphic_ctype", "amount", "created_at", ]
|
||||
search_fields = ['$reason', ]
|
||||
ordering_fields = ['created_at', 'amount']
|
||||
filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
|
||||
'destination', 'destination_alias', 'destination__alias__name',
|
||||
'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
|
||||
'created_at', 'valid', 'invalidity_reason', ]
|
||||
search_fields = ['$reason', '$source_alias', '$source__alias__name', '$source__alias__normalized_name',
|
||||
'$destination_alias', '$destination__alias__name', '$destination__alias__normalized_name',
|
||||
'$invalidity_reason', ]
|
||||
ordering_fields = ['created_at', 'amount', ]
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
|
19
apps/note/migrations/0005_auto_20210313_1235.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.2.19 on 2021-03-13 11:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('note', '0004_remove_null_tag_on_charfields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='alias',
|
||||
name='note',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='alias', to='note.Note'),
|
||||
),
|
||||
]
|
@ -248,6 +248,7 @@ class Alias(models.Model):
|
||||
note = models.ForeignKey(
|
||||
Note,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="alias",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -223,7 +223,8 @@ class Transaction(PolymorphicModel):
|
||||
# Check that the amounts stay between big integer bounds
|
||||
diff_source, diff_dest = self.validate()
|
||||
|
||||
if not self.source.is_active or not self.destination.is_active:
|
||||
if not (hasattr(self, '_force_save') and self._force_save) \
|
||||
and (not self.source.is_active or not self.destination.is_active):
|
||||
raise ValidationError(_("The transaction can't be saved since the source note "
|
||||
"or the destination note is not active."))
|
||||
|
||||
@ -271,7 +272,7 @@ class RecurrentTransaction(Transaction):
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.template.destination != self.destination:
|
||||
if self.template.destination != self.destination and not (hasattr(self, '_force_save') and self._force_save):
|
||||
raise ValidationError(
|
||||
_("The destination of this transaction must equal to the destination of the template."))
|
||||
return super().clean()
|
||||
@ -332,6 +333,36 @@ class SpecialTransaction(Transaction):
|
||||
self.clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def validate_payment_form(form):
|
||||
"""
|
||||
Ensure that last name and first name are filled for a form that creates a SpecialTransaction,
|
||||
and check that if the user pays with a check, then the bank field is filled.
|
||||
|
||||
Return True iff there is no error.
|
||||
Whenever there is an error, they are inserted in the form errors.
|
||||
"""
|
||||
|
||||
credit_type = form.cleaned_data["credit_type"]
|
||||
last_name = form.cleaned_data["last_name"]
|
||||
first_name = form.cleaned_data["first_name"]
|
||||
bank = form.cleaned_data["bank"]
|
||||
|
||||
error = False
|
||||
|
||||
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
|
||||
if not last_name:
|
||||
form.add_error('last_name', _("This field is required."))
|
||||
error = True
|
||||
if not first_name:
|
||||
form.add_error('first_name', _("This field is required."))
|
||||
error = True
|
||||
if not bank and credit_type.special_type == "Chèque":
|
||||
form.add_error('bank', _("This field is required."))
|
||||
error = True
|
||||
|
||||
return not error
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Special transaction")
|
||||
verbose_name_plural = _("Special transactions")
|
||||
|
@ -43,4 +43,5 @@ def delete_transaction(instance, **_kwargs):
|
||||
"""
|
||||
if not hasattr(instance, "_no_signal"):
|
||||
instance.valid = False
|
||||
instance._force_save = True
|
||||
instance.save()
|
||||
|
@ -28,7 +28,7 @@ $(document).ready(function () {
|
||||
|
||||
// Switching in double consumptions mode should update the layout
|
||||
$('#double_conso').change(function () {
|
||||
$('#consos_list_div').removeClass('d-none')
|
||||
document.getElementById('consos_list_div').classList.remove('d-none')
|
||||
$('#infos_div').attr('class', 'col-sm-5 col-xl-6')
|
||||
|
||||
const note_list_obj = $('#note_list')
|
||||
@ -37,7 +37,7 @@ $(document).ready(function () {
|
||||
note_list_obj.html('')
|
||||
|
||||
buttons.forEach(function (button) {
|
||||
$('#conso_button_' + button.id).click(function () {
|
||||
document.getElementById(`conso_button_${button.id}`).addEventListener('click', () => {
|
||||
if (LOCK) { return }
|
||||
removeNote(button, 'conso_button', buttons, 'consos_list')()
|
||||
})
|
||||
@ -46,7 +46,7 @@ $(document).ready(function () {
|
||||
})
|
||||
|
||||
$('#single_conso').change(function () {
|
||||
$('#consos_list_div').addClass('d-none')
|
||||
document.getElementById('consos_list_div').classList.add('d-none')
|
||||
$('#infos_div').attr('class', 'col-sm-5 col-md-4')
|
||||
|
||||
const consos_list_obj = $('#consos_list')
|
||||
@ -68,9 +68,9 @@ $(document).ready(function () {
|
||||
})
|
||||
|
||||
// Ensure we begin in single consumption. Fix issue with TurboLinks and BootstrapJS
|
||||
$("label[for='double_conso']").removeClass('active')
|
||||
document.querySelector("label[for='double_conso']").classList.remove('active')
|
||||
|
||||
$('#consume_all').click(consumeAll)
|
||||
document.getElementById("consume_all").addEventListener('click', consumeAll)
|
||||
})
|
||||
|
||||
notes = []
|
||||
@ -127,11 +127,10 @@ function addConso (dest, amount, type, category_id, category_name, template_id,
|
||||
html += li('conso_button_' + button.id, button.name +
|
||||
'<span class="badge badge-dark badge-pill">' + button.quantity + '</span>')
|
||||
})
|
||||
document.getElementById(list).innerHTML = html
|
||||
|
||||
$('#' + list).html(html)
|
||||
|
||||
buttons.forEach(function (button) {
|
||||
$('#conso_button_' + button.id).click(function () {
|
||||
buttons.forEach((button) => {
|
||||
document.getElementById(`conso_button_${button.id}`).addEventListener('click', () => {
|
||||
if (LOCK) { return }
|
||||
removeNote(button, 'conso_button', buttons, list)()
|
||||
})
|
||||
@ -146,12 +145,13 @@ function reset () {
|
||||
notes_display.length = 0
|
||||
notes.length = 0
|
||||
buttons.length = 0
|
||||
$('#note_list').html('')
|
||||
$('#consos_list').html('')
|
||||
$('#note').val('')
|
||||
$('#note').attr('data-original-title', '').tooltip('hide')
|
||||
$('#profile_pic').attr('src', '/static/member/img/default_picture.png')
|
||||
$('#profile_pic_link').attr('href', '#')
|
||||
document.getElementById('note_list').innerHTML = ''
|
||||
document.getElementById('consos_list').innerHTML = ''
|
||||
document.getElementById('note').value = ''
|
||||
document.getElementById('note').dataset.originTitle = ''
|
||||
$('#note').tooltip('hide')
|
||||
document.getElementById('profile_pic').src = '/static/member/img/default_picture.png'
|
||||
document.getElementById('profile_pic_link').href = '#'
|
||||
refreshHistory()
|
||||
refreshBalance()
|
||||
LOCK = false
|
||||
@ -168,7 +168,7 @@ function consumeAll () {
|
||||
let error = false
|
||||
|
||||
if (notes_display.length === 0) {
|
||||
$('#note').addClass('is-invalid')
|
||||
document.getElementById('note').classList.add('is-invalid')
|
||||
$('#note_list').html(li('', '<strong>Ajoutez des émetteurs.</strong>', 'text-danger'))
|
||||
error = true
|
||||
}
|
||||
@ -222,17 +222,15 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
|
||||
if (!isNaN(source.balance)) {
|
||||
const newBalance = source.balance - quantity * amount
|
||||
if (newBalance <= -5000) {
|
||||
addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
|
||||
'succès, mais la note émettrice ' + source_alias + ' est en négatif sévère.',
|
||||
'danger', 30000)
|
||||
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
|
||||
'but the emitter note %s is very negative.'), [source_alias, source_alias]), 'danger', 30000)
|
||||
} else if (newBalance < 0) {
|
||||
addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
|
||||
'succès, mais la note émettrice ' + source_alias + ' est en négatif.',
|
||||
'warning', 30000)
|
||||
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
|
||||
'but the emitter note %s is negative.'), [source_alias, source_alias]), 'warning', 30000)
|
||||
}
|
||||
if (source.membership && source.membership.date_end < new Date().toISOString()) {
|
||||
addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
|
||||
'danger', 30000)
|
||||
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source_alias]),
|
||||
'danger', 30000)
|
||||
}
|
||||
}
|
||||
reset()
|
||||
@ -253,7 +251,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
|
||||
template: template
|
||||
}).done(function () {
|
||||
reset()
|
||||
addMsg("La transaction n'a pas pu être validée pour cause de solde insuffisant.", 'danger', 10000)
|
||||
addMsg(gettext("The transaction couldn't be validated because of insufficient balance."), 'danger', 10000)
|
||||
}).fail(function () {
|
||||
reset()
|
||||
errMsg(e.responseJSON)
|
||||
|
@ -239,20 +239,20 @@ $('#btn_transfer').click(function () {
|
||||
|
||||
if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) {
|
||||
amount_field.addClass('is-invalid')
|
||||
$('#amount-required').html('<strong>Ce champ est requis et doit comporter un nombre décimal strictement positif.</strong>')
|
||||
$('#amount-required').html('<strong>' + gettext('This field is required and must contain a decimal positive number.') + '</strong>')
|
||||
error = true
|
||||
}
|
||||
|
||||
const amount = Math.floor(100 * amount_field.val())
|
||||
if (amount > 2147483647) {
|
||||
amount_field.addClass('is-invalid')
|
||||
$('#amount-required').html('<strong>Le montant ne doit pas excéder 21474836.47 €.</strong>')
|
||||
$('#amount-required').html('<strong>' + gettext('The amount must stay under 21,474,836.47 €.') + '</strong>')
|
||||
error = true
|
||||
}
|
||||
|
||||
if (!reason_field.val() && $('#type_transfer').is(':checked')) {
|
||||
reason_field.addClass('is-invalid')
|
||||
$('#reason-required').html('<strong>Ce champ est requis.</strong>')
|
||||
$('#reason-required').html('<strong>' + gettext('This field is required.') + '</strong>')
|
||||
error = true
|
||||
}
|
||||
|
||||
@ -278,9 +278,8 @@ $('#btn_transfer').click(function () {
|
||||
[...sources_notes_display].forEach(function (source) {
|
||||
[...dests_notes_display].forEach(function (dest) {
|
||||
if (source.note.id === dest.note.id) {
|
||||
addMsg('Attention : la transaction de ' + pretty_money(amount) + ' de la note ' + source.name +
|
||||
' vers la note ' + dest.name + " n'a pas été faite car il s'agit de la même note au départ" +
|
||||
" et à l'arrivée.", 'warning', 10000)
|
||||
addMsg(interpolate(gettext('Warning: the transaction of %s from %s to %s was not made because ' +
|
||||
'it is the same source and destination note.'), [pretty_money(amount), source.name, dest.name]), 'warning', 10000)
|
||||
LOCK = false
|
||||
return
|
||||
}
|
||||
@ -300,43 +299,35 @@ $('#btn_transfer').click(function () {
|
||||
destination_alias: dest.name
|
||||
}).done(function () {
|
||||
if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) {
|
||||
addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
|
||||
'danger', 30000)
|
||||
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000)
|
||||
}
|
||||
if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) {
|
||||
addMsg('Attention : la note destination ' + dest.name + " n'est plus adhérente.",
|
||||
'danger', 30000)
|
||||
addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [dest.name]), 'danger', 30000)
|
||||
}
|
||||
|
||||
if (!isNaN(source.note.balance)) {
|
||||
const newBalance = source.note.balance - source.quantity * dest.quantity * amount
|
||||
if (newBalance <= -5000) {
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
|
||||
source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
|
||||
'mais la note émettrice est en négatif sévère.', 'danger', 10000)
|
||||
addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000)
|
||||
reset()
|
||||
return
|
||||
} else if (newBalance < 0) {
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
|
||||
source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
|
||||
'mais la note émettrice est en négatif.', 'warning', 10000)
|
||||
addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is negative.'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000)
|
||||
reset()
|
||||
return
|
||||
}
|
||||
}
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
|
||||
' vers la note ' + dest.name + ' a été fait avec succès !', 'success', 10000)
|
||||
addMsg(interpolate(gettext('Transfer of %s from %s to %s succeed!'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name]), 'success', 10000)
|
||||
|
||||
reset()
|
||||
}).fail(function (err) { // do it again but valid = false
|
||||
const errObj = JSON.parse(err.responseText)
|
||||
if (errObj.non_field_errors) {
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
|
||||
' vers la note ' + dest.name + ' a échoué : ' + errObj.non_field_errors, 'danger')
|
||||
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, errObj.non_field_errors]), 'danger')
|
||||
LOCK = false
|
||||
return
|
||||
}
|
||||
@ -356,17 +347,15 @@ $('#btn_transfer').click(function () {
|
||||
destination: dest.note.id,
|
||||
destination_alias: dest.name
|
||||
}).done(function () {
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
|
||||
' vers la note ' + dest.name + ' a échoué : Solde insuffisant', 'danger', 10000)
|
||||
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, + dest.name, gettext('insufficient funds')]), 'danger', 10000)
|
||||
reset()
|
||||
}).fail(function (err) {
|
||||
const errObj = JSON.parse(err.responseText)
|
||||
let error = errObj.detail ? errObj.detail : errObj.non_field_errors
|
||||
if (!error) { error = err.responseText }
|
||||
addMsg('Le transfert de ' +
|
||||
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
|
||||
' vers la note ' + dest.name + ' a échoué : ' + error, 'danger')
|
||||
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
|
||||
[pretty_money(source.quantity * dest.quantity * amount), source.name, + dest.name, error]), 'danger')
|
||||
LOCK = false
|
||||
})
|
||||
})
|
||||
@ -412,14 +401,14 @@ $('#btn_transfer').click(function () {
|
||||
first_name: $('#first_name').val(),
|
||||
bank: $('#bank').val()
|
||||
}).done(function () {
|
||||
addMsg('Le crédit/retrait a bien été effectué !', 'success', 10000)
|
||||
if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg('Attention : la note ' + alias + " n'est plus adhérente.", 'danger', 10000) }
|
||||
addMsg(gettext('Credit/debit succeed!'), 'success', 10000)
|
||||
if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
|
||||
reset()
|
||||
}).fail(function (err) {
|
||||
const errObj = JSON.parse(err.responseText)
|
||||
let error = errObj.detail ? errObj.detail : errObj.non_field_errors
|
||||
if (!error) { error = err.responseText }
|
||||
addMsg('Le crédit/retrait a échoué : ' + error, 'danger', 10000)
|
||||
addMsg(interpolate(gettext('Credit/debit failed: %s'), [error]), 'danger', 10000)
|
||||
LOCK = false
|
||||
})
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from api.tests import TestAPI
|
||||
from member.models import Club, Membership
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from member.models import Club, Membership
|
||||
from note.models import NoteUser, Transaction, TemplateCategory, TransactionTemplate, RecurrentTransaction, \
|
||||
MembershipTransaction, SpecialTransaction, NoteSpecial, Alias
|
||||
from django.utils import timezone
|
||||
from permission.models import Role
|
||||
|
||||
from ..api.views import AliasViewSet, ConsumerViewSet, NotePolymorphicViewSet, TemplateCategoryViewSet,\
|
||||
TransactionTemplateViewSet, TransactionViewSet
|
||||
from ..models import NoteUser, Transaction, TemplateCategory, TransactionTemplate, RecurrentTransaction, \
|
||||
MembershipTransaction, SpecialTransaction, NoteSpecial, Alias, Note
|
||||
|
||||
|
||||
class TestTransactions(TestCase):
|
||||
fixtures = ('initial', )
|
||||
@ -297,8 +302,8 @@ class TestTransactions(TestCase):
|
||||
|
||||
def test_render_search_transactions(self):
|
||||
response = self.client.get(reverse("note:transactions", args=(self.user.note.pk,)), data=dict(
|
||||
source=self.second_user.note.alias_set.first().id,
|
||||
destination=self.user.note.alias_set.first().id,
|
||||
source=self.second_user.note.alias.first().id,
|
||||
destination=self.user.note.alias.first().id,
|
||||
type=[ContentType.objects.get_for_model(Transaction).id],
|
||||
reason="test",
|
||||
valid=True,
|
||||
@ -363,3 +368,69 @@ class TestTransactions(TestCase):
|
||||
self.assertTrue(Alias.objects.filter(name="test_updated_alias").exists())
|
||||
response = self.client.delete("/api/note/alias/" + str(alias.pk) + "/")
|
||||
self.assertEqual(response.status_code, 204)
|
||||
|
||||
|
||||
class TestNoteAPI(TestAPI):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
|
||||
membership = Membership.objects.create(club=Club.objects.get(name="BDE"), user=self.user)
|
||||
membership.roles.add(Role.objects.get(name="Respo info"))
|
||||
membership.save()
|
||||
Membership.objects.create(club=Club.objects.get(name="Kfet"), user=self.user)
|
||||
self.user.note.last_negative = timezone.now()
|
||||
self.user.note.save()
|
||||
|
||||
self.transaction = Transaction.objects.create(
|
||||
source=Note.objects.first(),
|
||||
destination=self.user.note,
|
||||
amount=4200,
|
||||
reason="Test transaction",
|
||||
)
|
||||
self.user.note.refresh_from_db()
|
||||
Alias.objects.create(note=self.user.note, name="I am a ¢omplex alias")
|
||||
|
||||
self.category = TemplateCategory.objects.create(name="Test")
|
||||
self.template = TransactionTemplate.objects.create(
|
||||
name="Test",
|
||||
destination=Club.objects.get(name="BDE").note,
|
||||
category=self.category,
|
||||
amount=100,
|
||||
description="Test template",
|
||||
)
|
||||
|
||||
def test_alias_api(self):
|
||||
"""
|
||||
Load Alias API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(AliasViewSet, "/api/note/alias/")
|
||||
|
||||
def test_consumer_api(self):
|
||||
"""
|
||||
Load Consumer API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ConsumerViewSet, "/api/note/consumer/")
|
||||
|
||||
def test_note_api(self):
|
||||
"""
|
||||
Load Note API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(NotePolymorphicViewSet, "/api/note/note/")
|
||||
|
||||
def test_template_category_api(self):
|
||||
"""
|
||||
Load TemplateCategory API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(TemplateCategoryViewSet, "/api/note/transaction/category/")
|
||||
|
||||
def test_transaction_template_api(self):
|
||||
"""
|
||||
Load TemplateTemplate API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(TransactionTemplateViewSet, "/api/note/transaction/template/")
|
||||
|
||||
def test_transaction_api(self):
|
||||
"""
|
||||
Load Transaction API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(TransactionViewSet, "/api/note/transaction/transaction/")
|
||||
|
@ -1,8 +1,9 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from api.viewsets import ReadOnlyProtectedModelViewSet
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
|
||||
from .serializers import PermissionSerializer, RoleSerializer
|
||||
from ..models import Permission, Role
|
||||
@ -14,10 +15,11 @@ class PermissionViewSet(ReadOnlyProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Permission` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/permission/permission/
|
||||
"""
|
||||
queryset = Permission.objects.all()
|
||||
queryset = Permission.objects.order_by('id')
|
||||
serializer_class = PermissionSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['model', 'type', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['model', 'type', 'query', 'mask', 'field', 'permanent', ]
|
||||
search_fields = ['$model__name', '$query', '$description', ]
|
||||
|
||||
|
||||
class RoleViewSet(ReadOnlyProtectedModelViewSet):
|
||||
@ -26,7 +28,8 @@ class RoleViewSet(ReadOnlyProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `RolePermission` objects, serialize it to JSON with the given serializer
|
||||
then render it on /api/permission/roles/
|
||||
"""
|
||||
queryset = Role.objects.all()
|
||||
queryset = Role.objects.order_by('id')
|
||||
serializer_class = RoleSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['role', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'permissions', 'for_club', 'memberships__user', ]
|
||||
search_fields = ['$name', '$for_club__name', ]
|
||||
|
@ -134,8 +134,6 @@ class PermissionBackend(ModelBackend):
|
||||
return False
|
||||
|
||||
sess = get_current_session()
|
||||
if sess is not None and sess.session_key is None:
|
||||
return False
|
||||
|
||||
if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
|
||||
return True
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from time import time
|
||||
|
||||
@ -38,6 +38,10 @@ def memoize(f):
|
||||
|
||||
nonlocal last_collect
|
||||
|
||||
if "test" in sys.argv:
|
||||
# In a test environment, don't memoize permissions
|
||||
return f(*args, **kwargs)
|
||||
|
||||
if time() - last_collect > 60:
|
||||
# Clear cache
|
||||
collect()
|
||||
|
@ -799,12 +799,12 @@
|
||||
"member",
|
||||
"membership"
|
||||
],
|
||||
"query": "{\"club\": [\"club\"]}",
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 3,
|
||||
"field": "roles",
|
||||
"permanent": false,
|
||||
"description": "Modifier les rôles d'un adhérent d'un club"
|
||||
"description": "Modifier les rôles d'une adhésion"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -819,7 +819,7 @@
|
||||
"type": "change",
|
||||
"mask": 1,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"permanent": true,
|
||||
"description": "Modifier son profil"
|
||||
}
|
||||
},
|
||||
@ -2081,7 +2081,7 @@
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 1,
|
||||
"mask": 2,
|
||||
"field": "invalidity_reason",
|
||||
"permanent": false,
|
||||
"description": "Modifier la raison d'invalidité d'une transaction"
|
||||
@ -2807,6 +2807,70 @@
|
||||
"description": "Voir ses propres alias, pour toujours"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 180,
|
||||
"fields": {
|
||||
"model": [
|
||||
"auth",
|
||||
"user"
|
||||
],
|
||||
"query": "{\"profile__registration_valid\": false}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir n'importe quel utilisateur non encore inscrit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 181,
|
||||
"fields": {
|
||||
"model": [
|
||||
"member",
|
||||
"profile"
|
||||
],
|
||||
"query": "{\"registration_valid\": false}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir n'importe quel profil non encore inscrit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 182,
|
||||
"fields": {
|
||||
"model": [
|
||||
"auth",
|
||||
"user"
|
||||
],
|
||||
"query": "{\"memberships__club__name\": \"BDE\", \"memberships__roles__name\": \"Adhérent BDE\", \"memberships__date_start__lte\": [\"today\"], \"memberships__date_end__gte\": [\"today\"]}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir n'importe quel utilisateur qui est adhérent BDE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 183,
|
||||
"fields": {
|
||||
"model": [
|
||||
"note",
|
||||
"note"
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 1,
|
||||
"field": "display_image",
|
||||
"permanent": false,
|
||||
"description": "Changer l'image de n'importe quelle note"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 1,
|
||||
@ -2939,14 +3003,14 @@
|
||||
62,
|
||||
127,
|
||||
133,
|
||||
135,
|
||||
136,
|
||||
141,
|
||||
142,
|
||||
150,
|
||||
166,
|
||||
167,
|
||||
168
|
||||
168,
|
||||
182
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -2960,7 +3024,9 @@
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27
|
||||
27,
|
||||
30,
|
||||
33
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3022,7 +3088,8 @@
|
||||
175,
|
||||
176,
|
||||
177,
|
||||
178
|
||||
178,
|
||||
183
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3205,7 +3272,12 @@
|
||||
175,
|
||||
176,
|
||||
177,
|
||||
178
|
||||
178,
|
||||
179,
|
||||
180,
|
||||
181,
|
||||
182,
|
||||
183
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3239,7 +3311,12 @@
|
||||
170,
|
||||
171,
|
||||
176,
|
||||
177
|
||||
177,
|
||||
178,
|
||||
179,
|
||||
180,
|
||||
181,
|
||||
182
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -3402,7 +3479,6 @@
|
||||
135,
|
||||
136,
|
||||
137,
|
||||
138,
|
||||
139,
|
||||
140,
|
||||
143,
|
||||
@ -3415,6 +3491,41 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"for_club": 2,
|
||||
"name": "PC Kfet",
|
||||
"permissions": [
|
||||
6,
|
||||
22,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
30,
|
||||
49,
|
||||
50,
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
135,
|
||||
137,
|
||||
143,
|
||||
147,
|
||||
150,
|
||||
166,
|
||||
167,
|
||||
168,
|
||||
176,
|
||||
177,
|
||||
180,
|
||||
181
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "wei.weirole",
|
||||
"pk": 12,
|
||||
|
@ -45,6 +45,7 @@ class InstancedPermission:
|
||||
with transaction.atomic():
|
||||
sid = transaction.savepoint()
|
||||
for o in self.model.model_class().objects.filter(pk=0).all():
|
||||
o._no_signal = True
|
||||
o._force_delete = True
|
||||
Model.delete(o)
|
||||
# An object with pk 0 wouldn't deleted. That's not normal, we alert admins.
|
||||
@ -62,10 +63,6 @@ class InstancedPermission:
|
||||
obj._no_signal = True
|
||||
Model.save(obj, force_insert=True)
|
||||
ret = self.model.model_class().objects.filter(self.query & Q(pk=0)).exists()
|
||||
# Delete testing object
|
||||
obj._no_signal = True
|
||||
obj._force_delete = True
|
||||
Model.delete(obj)
|
||||
transaction.savepoint_rollback(sid)
|
||||
|
||||
return ret
|
||||
|
@ -5,7 +5,6 @@ from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from django import template
|
||||
from note.models import Transaction
|
||||
from note_kfet.middlewares import get_current_authenticated_user, get_current_session
|
||||
from permission.backends import PermissionBackend
|
||||
|
||||
@ -25,21 +24,6 @@ def not_empty_model_list(model_name):
|
||||
return qs.exists()
|
||||
|
||||
|
||||
@stringfilter
|
||||
def not_empty_model_change_list(model_name):
|
||||
"""
|
||||
Return True if and only if the current user has right to change any object of the given model.
|
||||
"""
|
||||
user = get_current_authenticated_user()
|
||||
session = get_current_session()
|
||||
if user is None or isinstance(user, AnonymousUser):
|
||||
return False
|
||||
elif user.is_superuser and session.get("permission_mask", -1) >= 42:
|
||||
return True
|
||||
qs = model_list(model_name, "change")
|
||||
return qs.exists()
|
||||
|
||||
|
||||
@stringfilter
|
||||
def model_list(model_name, t="view", fetch=True):
|
||||
"""
|
||||
@ -68,33 +52,8 @@ def has_perm(perm, obj):
|
||||
return PermissionBackend.check_perm(get_current_authenticated_user(), perm, obj)
|
||||
|
||||
|
||||
def can_create_transaction():
|
||||
"""
|
||||
:return: True iff the authenticated user can create a transaction.
|
||||
"""
|
||||
user = get_current_authenticated_user()
|
||||
session = get_current_session()
|
||||
if user is None or isinstance(user, AnonymousUser):
|
||||
return False
|
||||
elif user.is_superuser and session.get("permission_mask", -1) >= 42:
|
||||
return True
|
||||
if session.get("can_create_transaction", None):
|
||||
return session.get("can_create_transaction", None) == 1
|
||||
|
||||
empty_transaction = Transaction(
|
||||
source=user.note,
|
||||
destination=user.note,
|
||||
quantity=1,
|
||||
amount=0,
|
||||
reason="Check permissions",
|
||||
)
|
||||
session["can_create_transaction"] = PermissionBackend.check_perm(user, "note.add_transaction", empty_transaction)
|
||||
return session.get("can_create_transaction") == 1
|
||||
|
||||
|
||||
register = template.Library()
|
||||
register.filter('not_empty_model_list', not_empty_model_list)
|
||||
register.filter('not_empty_model_change_list', not_empty_model_change_list)
|
||||
register.filter('model_list', model_list)
|
||||
register.filter('model_list_length', model_list_length)
|
||||
register.filter('has_perm', has_perm)
|
||||
|
@ -78,7 +78,7 @@ class PermissionQueryTestCase(TestCase):
|
||||
query = instanced.query
|
||||
model = perm.model.model_class()
|
||||
model.objects.filter(query).all()
|
||||
except (FieldError, AttributeError, ValueError, TypeError, JSONDecodeError):
|
||||
except (FieldError, AttributeError, ValueError, TypeError, JSONDecodeError): # pragma: no cover
|
||||
print("Query error for permission", perm)
|
||||
print("Query:", perm.query)
|
||||
if instanced.query:
|
||||
|
@ -51,8 +51,10 @@ class ProtectQuerysetMixin:
|
||||
# No worry if the user change the hidden fields: a 403 error will be performed if the user tries to make
|
||||
# a custom request.
|
||||
# We could also delete the field, but some views might be affected.
|
||||
meta = form.instance._meta
|
||||
for key in form.base_fields:
|
||||
if not PermissionBackend.check_perm(self.request.user, "wei.change_weiregistration_" + key, self.object):
|
||||
if not PermissionBackend.check_perm(self.request.user,
|
||||
f"{meta.app_label}.change_{meta.model_name}_" + key, self.object):
|
||||
form.fields[key].widget = HiddenInput()
|
||||
|
||||
return form
|
||||
@ -83,7 +85,7 @@ class ProtectedCreateView(LoginRequiredMixin, CreateView):
|
||||
If not, a 403 error is displayed.
|
||||
"""
|
||||
|
||||
def get_sample_object(self):
|
||||
def get_sample_object(self): # pragma: no cover
|
||||
"""
|
||||
return a sample instance of the Model.
|
||||
It should be valid (can be stored properly in database), but must not collide with existing data.
|
||||
|
@ -101,14 +101,14 @@ class ValidationForm(forms.Form):
|
||||
required=False,
|
||||
)
|
||||
|
||||
join_BDE = forms.BooleanField(
|
||||
join_bde = forms.BooleanField(
|
||||
label=_("Join BDE Club"),
|
||||
required=False,
|
||||
initial=True,
|
||||
)
|
||||
|
||||
# The user can join the Kfet club at the inscription
|
||||
join_Kfet = forms.BooleanField(
|
||||
join_kfet = forms.BooleanField(
|
||||
label=_("Join Kfet Club"),
|
||||
required=False,
|
||||
initial=True,
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import django_tables2 as tables
|
||||
from django.contrib.auth.models import User
|
||||
from treasury.models import SogeCredit
|
||||
|
||||
|
||||
class FutureUserTable(tables.Table):
|
||||
@ -21,6 +22,7 @@ class FutureUserTable(tables.Table):
|
||||
fields = ('last_name', 'first_name', 'username', 'email', )
|
||||
model = User
|
||||
row_attrs = {
|
||||
'class': 'table-row',
|
||||
'class': lambda record: 'table-row'
|
||||
+ (' bg-warning' if SogeCredit.objects.filter(user=record).exists() else ''),
|
||||
'data-href': lambda record: record.pk
|
||||
}
|
||||
|
@ -100,13 +100,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
bank.attr('disabled', true);
|
||||
bank.val('Société générale');
|
||||
|
||||
let join_BDE = $("#id_join_BDE");
|
||||
join_BDE.attr('disabled', true);
|
||||
join_BDE.attr('checked', 'checked');
|
||||
let join_bde = $("#id_join_bde");
|
||||
|
||||
let join_Kfet = $("#id_join_Kfet");
|
||||
join_Kfet.attr('disabled', true);
|
||||
join_Kfet.attr('checked', 'checked');
|
||||
join_bde.attr('disabled', true);
|
||||
join_bde.attr('checked', 'checked');
|
||||
|
||||
let join_kfet = $("#id_join_kfet");
|
||||
join_kfet.attr('disabled', true);
|
||||
join_kfet.attr('checked', 'checked');
|
||||
}
|
||||
|
||||
soge_field.change(fillFields);
|
||||
@ -116,4 +117,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
fillFields();
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -196,8 +196,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=False,
|
||||
join_Kfet=False,
|
||||
join_bde=False,
|
||||
join_kfet=False,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["form"].errors)
|
||||
@ -210,8 +210,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=False,
|
||||
join_Kfet=True,
|
||||
join_bde=False,
|
||||
join_kfet=True,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["form"].errors)
|
||||
@ -224,8 +224,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="J'ai pas d'argent",
|
||||
join_BDE=True,
|
||||
join_Kfet=True,
|
||||
join_bde=True,
|
||||
join_kfet=True,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["form"].errors)
|
||||
@ -238,8 +238,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="",
|
||||
first_name="",
|
||||
bank="",
|
||||
join_BDE=True,
|
||||
join_Kfet=True,
|
||||
join_bde=True,
|
||||
join_kfet=True,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["form"].errors)
|
||||
@ -255,8 +255,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=True,
|
||||
join_Kfet=False,
|
||||
join_bde=True,
|
||||
join_kfet=False,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.context["form"].errors)
|
||||
@ -281,8 +281,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=True,
|
||||
join_Kfet=False,
|
||||
join_bde=True,
|
||||
join_kfet=False,
|
||||
))
|
||||
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
|
||||
self.user.profile.refresh_from_db()
|
||||
@ -317,8 +317,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=True,
|
||||
join_Kfet=True,
|
||||
join_bde=True,
|
||||
join_kfet=True,
|
||||
))
|
||||
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
|
||||
self.user.profile.refresh_from_db()
|
||||
@ -353,8 +353,8 @@ class TestValidateRegistration(TestCase):
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
join_BDE=True,
|
||||
join_Kfet=True,
|
||||
join_bde=True,
|
||||
join_kfet=True,
|
||||
))
|
||||
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
|
||||
self.user.profile.refresh_from_db()
|
||||
|
@ -9,6 +9,7 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
|
||||
"""
|
||||
Create a unique token generator to confirm email addresses.
|
||||
"""
|
||||
|
||||
def _make_hash_value(self, user, timestamp):
|
||||
"""
|
||||
Hash the user's primary key and some user state that's sure to change
|
||||
@ -23,9 +24,18 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
|
||||
"""
|
||||
# Truncate microseconds so that tokens are consistent even if the
|
||||
# database doesn't support microseconds.
|
||||
login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)
|
||||
return str(user.pk) + str(user.email) + str(user.profile.email_confirmed)\
|
||||
+ str(login_timestamp) + str(timestamp)
|
||||
login_timestamp = (
|
||||
""
|
||||
if user.last_login is None
|
||||
else user.last_login.replace(microsecond=0, tzinfo=None)
|
||||
)
|
||||
return (
|
||||
str(user.pk)
|
||||
+ str(user.email)
|
||||
+ str(user.profile.email_confirmed)
|
||||
+ str(login_timestamp)
|
||||
+ str(timestamp)
|
||||
)
|
||||
|
||||
|
||||
email_validation_token = AccountActivationTokenGenerator()
|
||||
|
@ -235,7 +235,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
fee += 8000
|
||||
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
||||
|
||||
ctx["declare_soge_account"] = True
|
||||
ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
|
||||
|
||||
return ctx
|
||||
|
||||
@ -248,9 +248,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
Finally validate the registration, with creating the membership.
|
||||
"""
|
||||
user = self.get_object()
|
||||
|
||||
if Alias.objects.filter(normalized_name=Alias.normalize(user.username)).exists():
|
||||
# Don't try to hack an existing account.
|
||||
form.add_error(None, _("An alias with a similar name already exists."))
|
||||
return self.form_invalid(form)
|
||||
|
||||
@ -261,35 +265,36 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
last_name = form.cleaned_data["last_name"]
|
||||
first_name = form.cleaned_data["first_name"]
|
||||
bank = form.cleaned_data["bank"]
|
||||
join_BDE = form.cleaned_data["join_BDE"]
|
||||
join_Kfet = form.cleaned_data["join_Kfet"]
|
||||
join_bde = form.cleaned_data["join_bde"]
|
||||
join_kfet = form.cleaned_data["join_kfet"]
|
||||
|
||||
if soge:
|
||||
# If Société Générale pays the inscription, the user joins the two clubs
|
||||
join_BDE = True
|
||||
join_Kfet = True
|
||||
# If Société Générale pays the inscription, the user automatically joins the two clubs.
|
||||
join_bde = True
|
||||
join_kfet = True
|
||||
|
||||
if not join_BDE:
|
||||
form.add_error('join_BDE', _("You must join the BDE."))
|
||||
if not join_bde:
|
||||
# This software belongs to the BDE.
|
||||
form.add_error('join_bde', _("You must join the BDE."))
|
||||
return super().form_invalid(form)
|
||||
|
||||
# Calculate required registration fee
|
||||
fee = 0
|
||||
bde = Club.objects.get(name="BDE")
|
||||
bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
||||
if join_BDE:
|
||||
fee += bde_fee
|
||||
# This is mandatory.
|
||||
fee += bde_fee if join_bde else 0
|
||||
kfet = Club.objects.get(name="Kfet")
|
||||
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||
if join_Kfet:
|
||||
fee += kfet_fee
|
||||
# Add extra fee for the full membership
|
||||
fee += kfet_fee if join_kfet else 0
|
||||
|
||||
if soge:
|
||||
# If the bank pays, then we don't credit now. Treasurers will validate the transaction
|
||||
# and credit the note later.
|
||||
credit_type = None
|
||||
# If the bank pays, then we don't credit now. Treasurers will validate the transaction
|
||||
# and credit the note later.
|
||||
credit_type = None if soge else credit_type
|
||||
|
||||
if credit_type is None:
|
||||
credit_amount = 0
|
||||
# If the user does not select any payment method, then no credit will be performed.
|
||||
credit_amount = 0 if credit_type is None else credit_amount
|
||||
|
||||
if fee > credit_amount and not soge:
|
||||
# Check if the user credits enough money
|
||||
@ -298,15 +303,9 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
.format(pretty_money(fee)))
|
||||
return self.form_invalid(form)
|
||||
|
||||
if credit_type is not None and credit_amount > 0:
|
||||
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
|
||||
if not last_name:
|
||||
form.add_error('last_name', _("This field is required."))
|
||||
if not first_name:
|
||||
form.add_error('first_name', _("This field is required."))
|
||||
if not bank and credit_type.special_type == "Chèque":
|
||||
form.add_error('bank', _("This field is required."))
|
||||
return self.form_invalid(form)
|
||||
# Check that payment information are filled, like last name and first name
|
||||
if credit_type is not None and credit_amount > 0 and not SpecialTransaction.validate_payment_form(form):
|
||||
return self.form_invalid(form)
|
||||
|
||||
# Save the user and finally validate the registration
|
||||
# Saving the user creates the associated note
|
||||
@ -338,7 +337,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
valid=True,
|
||||
)
|
||||
|
||||
if join_BDE:
|
||||
if join_bde:
|
||||
# Create membership for the user to the BDE starting today
|
||||
membership = Membership(
|
||||
club=bde,
|
||||
@ -352,7 +351,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||
membership.roles.add(Role.objects.get(name="Adhérent BDE"))
|
||||
membership.save()
|
||||
|
||||
if join_Kfet:
|
||||
if join_kfet:
|
||||
# Create membership for the user to the Kfet starting today
|
||||
membership = Membership(
|
||||
club=kfet,
|
||||
|
@ -16,10 +16,11 @@ class InvoiceViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Invoice` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/treasury/invoice/
|
||||
"""
|
||||
queryset = Invoice.objects.order_by("id").all()
|
||||
queryset = Invoice.objects.order_by('id')
|
||||
serializer_class = InvoiceSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['bde', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['bde', 'object', 'description', 'name', 'address', 'date', 'acquitted', 'locked', ]
|
||||
search_fields = ['$object', '$description', '$name', '$address', ]
|
||||
|
||||
|
||||
class ProductViewSet(ReadProtectedModelViewSet):
|
||||
@ -28,10 +29,11 @@ class ProductViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Product` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/treasury/product/
|
||||
"""
|
||||
queryset = Product.objects.order_by("invoice_id", "id").all()
|
||||
queryset = Product.objects.order_by('invoice_id', 'id')
|
||||
serializer_class = ProductSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ['$designation', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['invoice', 'designation', 'quantity', 'amount', ]
|
||||
search_fields = ['$designation', '$invoice__object', ]
|
||||
|
||||
|
||||
class RemittanceTypeViewSet(ReadProtectedModelViewSet):
|
||||
@ -40,8 +42,11 @@ class RemittanceTypeViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `RemittanceType` objects, serialize it to JSON with the given serializer
|
||||
then render it on /api/treasury/remittance_type/
|
||||
"""
|
||||
queryset = RemittanceType.objects.order_by("id")
|
||||
queryset = RemittanceType.objects.order_by('id')
|
||||
serializer_class = RemittanceTypeSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['note', ]
|
||||
search_fields = ['$note__special_type', ]
|
||||
|
||||
|
||||
class RemittanceViewSet(ReadProtectedModelViewSet):
|
||||
@ -50,8 +55,11 @@ class RemittanceViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Remittance` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/treasury/remittance/
|
||||
"""
|
||||
queryset = Remittance.objects.order_by("id")
|
||||
queryset = Remittance.objects.order_by('id')
|
||||
serializer_class = RemittanceSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['date', 'remittance_type', 'comment', 'closed', 'transaction_proxies__transaction', ]
|
||||
search_fields = ['$remittance_type__note__special_type', '$comment', ]
|
||||
|
||||
|
||||
class SogeCreditViewSet(ReadProtectedModelViewSet):
|
||||
@ -60,5 +68,10 @@ class SogeCreditViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `SogeCredit` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/treasury/soge_credit/
|
||||
"""
|
||||
queryset = SogeCredit.objects.order_by("id")
|
||||
queryset = SogeCredit.objects.order_by('id')
|
||||
serializer_class = SogeCreditSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['user', 'user__last_name', 'user__first_name', 'user__email', 'user__note__alias__name',
|
||||
'user__note__alias__normalized_name', 'transactions', 'credit_transaction', ]
|
||||
search_fields = ['$user__last_name', '$user__first_name', '$user__email', '$user__note__alias__name',
|
||||
'$user__note__alias__normalized_name', ]
|
||||
|
@ -28,6 +28,8 @@ class TreasuryConfig(AppConfig):
|
||||
source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
|
||||
specialtransactionproxy=None,
|
||||
):
|
||||
SpecialTransactionProxy.objects.create(transaction=transaction, remittance=None)
|
||||
proxy = SpecialTransactionProxy(transaction=transaction, remittance=None)
|
||||
proxy._force_save = True
|
||||
proxy.save()
|
||||
|
||||
post_migrate.connect(setup_specialtransactions_proxies, sender=SpecialTransactionProxy)
|
||||
|
25
apps/treasury/migrations/0003_auto_20210321_1034.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 2.2.19 on 2021-03-21 09:34
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('treasury', '0002_invoice_remove_png_extension'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='quantity',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=7, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='specialtransactionproxy',
|
||||
name='remittance',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transaction_proxies', to='treasury.Remittance', verbose_name='Remittance'),
|
||||
),
|
||||
]
|
@ -5,6 +5,7 @@ from datetime import date
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models, transaction
|
||||
from django.db.models import Q
|
||||
from django.template.loader import render_to_string
|
||||
@ -131,12 +132,15 @@ class Product(models.Model):
|
||||
verbose_name=_("Designation"),
|
||||
)
|
||||
|
||||
quantity = models.PositiveIntegerField(
|
||||
verbose_name=_("Quantity")
|
||||
quantity = models.DecimalField(
|
||||
decimal_places=2,
|
||||
max_digits=7,
|
||||
verbose_name=_("Quantity"),
|
||||
validators=[MinValueValidator(0)],
|
||||
)
|
||||
|
||||
amount = models.IntegerField(
|
||||
verbose_name=_("Unit price")
|
||||
verbose_name=_("Unit price"),
|
||||
)
|
||||
|
||||
@property
|
||||
@ -257,6 +261,7 @@ class SpecialTransactionProxy(models.Model):
|
||||
Remittance,
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
related_name="transaction_proxies",
|
||||
verbose_name=_("Remittance"),
|
||||
)
|
||||
|
||||
@ -380,9 +385,14 @@ class SogeCredit(models.Model):
|
||||
tr.valid = True
|
||||
tr.created_at = timezone.now()
|
||||
tr.save()
|
||||
self.credit_transaction.valid = False
|
||||
self.credit_transaction.reason += " (invalide)"
|
||||
self.credit_transaction.save()
|
||||
if self.credit_transaction:
|
||||
# If the soge credit is deleted while the user is not validated yet,
|
||||
# there is not credit transaction.
|
||||
# There is a credit transaction iff the user declares that no bank account
|
||||
# was opened after the validation of the account.
|
||||
self.credit_transaction.valid = False
|
||||
self.credit_transaction.reason += " (invalide)"
|
||||
self.credit_transaction.save()
|
||||
super().delete(**kwargs)
|
||||
|
||||
class Meta:
|
||||
|
@ -10,9 +10,8 @@ def save_special_transaction(instance, created, **kwargs):
|
||||
"""
|
||||
|
||||
if not hasattr(instance, "_no_signal"):
|
||||
if instance.is_credit():
|
||||
if created and RemittanceType.objects.filter(note=instance.source).exists():
|
||||
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
|
||||
else:
|
||||
if created and RemittanceType.objects.filter(note=instance.destination).exists():
|
||||
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
|
||||
if created and RemittanceType.objects.filter(
|
||||
note=instance.source if instance.is_credit() else instance.destination).exists():
|
||||
proxy = SpecialTransactionProxy(transaction=instance, remittance=None)
|
||||
proxy._force_save = True
|
||||
proxy.save()
|
||||
|
@ -109,9 +109,6 @@ class SpecialTransactionTable(tables.Table):
|
||||
'a': {'class': 'btn btn-primary btn-danger'}
|
||||
}, )
|
||||
|
||||
def render_id(self, record):
|
||||
return record.specialtransactionproxy.pk
|
||||
|
||||
def render_amount(self, value):
|
||||
return pretty_money(value)
|
||||
|
||||
@ -147,4 +144,4 @@ class SogeCreditTable(tables.Table):
|
||||
|
||||
class Meta:
|
||||
model = SogeCredit
|
||||
fields = ('user', 'amount', 'valid', )
|
||||
fields = ('user', 'user__last_name', 'user__first_name', 'amount', 'valid', )
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% load escape_tex %}
|
||||
{% load escape_tex i18n %}
|
||||
{% language fr %}
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
|
||||
\usepackage{fontspec}
|
||||
@ -176,3 +177,4 @@ TVA non applicable, article 293 B du CGI.
|
||||
\end{center}
|
||||
|
||||
\end{document}
|
||||
{% endlanguage %}
|
||||
|
@ -11,8 +11,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-xl-6 text-right">{% trans 'user'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user }}</a></dd>
|
||||
<dt class="col-xl-6 text-right">{% trans 'last name'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ object.user.last_name }}</dd>
|
||||
|
||||
<dt class="col-xl-6 text-right">{% trans 'first name'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ object.user.first_name }}</dd>
|
||||
|
||||
<dt class="col-xl-6 text-right">{% trans 'username'|capfirst %}</dt>
|
||||
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user.username }}</a></dd>
|
||||
|
||||
{% if "note.view_note_balance"|has_perm:object.user.note %}
|
||||
<dt class="col-xl-6 text-right">{% trans 'balance'|capfirst %}</dt>
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from api.tests import TestAPI
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
@ -8,7 +9,10 @@ from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from member.models import Membership, Club
|
||||
from note.models import SpecialTransaction, NoteSpecial, Transaction
|
||||
from treasury.models import Invoice, Product, Remittance, RemittanceType, SogeCredit
|
||||
|
||||
from ..api.views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet, \
|
||||
SogeCreditViewSet
|
||||
from ..models import Invoice, Product, Remittance, RemittanceType, SogeCredit
|
||||
|
||||
|
||||
class TestInvoices(TestCase):
|
||||
@ -366,11 +370,8 @@ class TestSogeCredits(TestCase):
|
||||
response = self.client.get(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
try:
|
||||
self.client.post(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), data=dict(delete=True))
|
||||
raise AssertionError("It is not possible to delete the soge credit until the note is not credited.")
|
||||
except ValidationError:
|
||||
pass
|
||||
self.assertRaises(ValidationError, self.client.post,
|
||||
reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), data=dict(delete=True))
|
||||
|
||||
SpecialTransaction.objects.create(
|
||||
source=NoteSpecial.objects.get(special_type="Carte bancaire"),
|
||||
@ -399,3 +400,82 @@ class TestSogeCredits(TestCase):
|
||||
"""
|
||||
response = self.client.get("/api/treasury/soge_credit/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TestTreasuryAPI(TestAPI):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
|
||||
self.invoice = Invoice.objects.create(
|
||||
id=1,
|
||||
object="Object",
|
||||
description="Description",
|
||||
name="Me",
|
||||
address="Earth",
|
||||
acquitted=False,
|
||||
)
|
||||
self.product = Product.objects.create(
|
||||
invoice=self.invoice,
|
||||
designation="Product",
|
||||
quantity=3,
|
||||
amount=3.14,
|
||||
)
|
||||
|
||||
self.credit = SpecialTransaction.objects.create(
|
||||
source=NoteSpecial.objects.get(special_type="Chèque"),
|
||||
destination=self.user.note,
|
||||
amount=4200,
|
||||
reason="Credit",
|
||||
last_name="TOTO",
|
||||
first_name="Toto",
|
||||
bank="Société générale",
|
||||
)
|
||||
|
||||
self.remittance = Remittance.objects.create(
|
||||
remittance_type=RemittanceType.objects.get(),
|
||||
comment="Test remittance",
|
||||
closed=False,
|
||||
)
|
||||
self.credit.specialtransactionproxy.remittance = self.remittance
|
||||
self.credit.specialtransactionproxy.save()
|
||||
|
||||
self.kfet = Club.objects.get(name="Kfet")
|
||||
self.bde = self.kfet.parent_club
|
||||
|
||||
self.kfet_membership = Membership(
|
||||
user=self.user,
|
||||
club=self.kfet,
|
||||
)
|
||||
self.kfet_membership._force_renew_parent = True
|
||||
self.kfet_membership._soge = True
|
||||
self.kfet_membership.save()
|
||||
|
||||
def test_invoice_api(self):
|
||||
"""
|
||||
Load Invoice API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(InvoiceViewSet, "/api/treasury/invoice/")
|
||||
|
||||
def test_product_api(self):
|
||||
"""
|
||||
Load Product API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(ProductViewSet, "/api/treasury/product/")
|
||||
|
||||
def test_remittance_api(self):
|
||||
"""
|
||||
Load Remittance API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(RemittanceViewSet, "/api/treasury/remittance/")
|
||||
|
||||
def test_remittance_type_api(self):
|
||||
"""
|
||||
Load RemittanceType API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(RemittanceTypeViewSet, "/api/treasury/remittance_type/")
|
||||
|
||||
def test_sogecredit_api(self):
|
||||
"""
|
||||
Load SogeCredit API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(SogeCreditViewSet, "/api/treasury/soge_credit/")
|
||||
|
@ -210,7 +210,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
|
||||
del tex
|
||||
|
||||
# The file has to be rendered twice
|
||||
for ignored in range(2):
|
||||
for _ignored in range(2):
|
||||
error = subprocess.Popen(
|
||||
["/usr/bin/xelatex", "-interaction=nonstopmode", "invoice-{}.tex".format(pk)],
|
||||
cwd=tmp_dir,
|
||||
|
@ -1,7 +1,8 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
|
||||
from .serializers import WEIClubSerializer, BusSerializer, BusTeamSerializer, WEIRoleSerializer, \
|
||||
@ -15,11 +16,14 @@ class WEIClubViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `WEIClub` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/club/
|
||||
"""
|
||||
queryset = WEIClub.objects.all()
|
||||
queryset = WEIClub.objects.order_by('id')
|
||||
serializer_class = WEIClubSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'year', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'year', 'date_start', 'date_end', 'email', 'note__alias__name',
|
||||
'note__alias__normalized_name', 'parent_club', 'parent_club__name', 'require_memberships',
|
||||
'membership_fee_paid', 'membership_fee_unpaid', 'membership_duration', 'membership_start',
|
||||
'membership_end', ]
|
||||
search_fields = ['$name', '$email', '$note__alias__name', '$note__alias__normalized_name', ]
|
||||
|
||||
|
||||
class BusViewSet(ReadProtectedModelViewSet):
|
||||
@ -28,11 +32,11 @@ class BusViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `Bus` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/bus/
|
||||
"""
|
||||
queryset = Bus.objects
|
||||
queryset = Bus.objects.order_by('id')
|
||||
serializer_class = BusSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'wei', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'wei', 'description', ]
|
||||
search_fields = ['$name', '$wei__name', '$description', ]
|
||||
|
||||
|
||||
class BusTeamViewSet(ReadProtectedModelViewSet):
|
||||
@ -41,11 +45,11 @@ class BusTeamViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `BusTeam` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/team/
|
||||
"""
|
||||
queryset = BusTeam.objects
|
||||
queryset = BusTeam.objects.order_by('id')
|
||||
serializer_class = BusTeamSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$name', ]
|
||||
filterset_fields = ['name', 'bus', 'bus__wei', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'bus', 'color', 'description', 'bus__wei', ]
|
||||
search_fields = ['$name', '$bus__name', '$bus__wei__name', '$description', ]
|
||||
|
||||
|
||||
class WEIRoleViewSet(ReadProtectedModelViewSet):
|
||||
@ -54,9 +58,10 @@ class WEIRoleViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `WEIRole` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/role/
|
||||
"""
|
||||
queryset = WEIRole.objects
|
||||
queryset = WEIRole.objects.order_by('id')
|
||||
serializer_class = WEIRoleSerializer
|
||||
filter_backends = [SearchFilter]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', 'permissions', 'memberships', ]
|
||||
search_fields = ['$name', ]
|
||||
|
||||
|
||||
@ -66,11 +71,17 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all WEIRegistration objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/registration/
|
||||
"""
|
||||
queryset = WEIRegistration.objects
|
||||
queryset = WEIRegistration.objects.order_by('id')
|
||||
serializer_class = WEIRegistrationSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$user__username', ]
|
||||
filterset_fields = ['user', 'wei', ]
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
|
||||
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
|
||||
'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
|
||||
'clothing_cut', 'clothing_size', 'first_year', 'emergency_contact_name',
|
||||
'emergency_contact_phone', ]
|
||||
search_fields = ['$user__username', '$user__first_name', '$user__last_name', '$user__email',
|
||||
'$user__note__alias__name', '$user__note__alias__normalized_name', '$wei__name',
|
||||
'$wei__email', '$health_issues', '$emergency_contact_name', '$emergency_contact_phone', ]
|
||||
|
||||
|
||||
class WEIMembershipViewSet(ReadProtectedModelViewSet):
|
||||
@ -79,8 +90,16 @@ class WEIMembershipViewSet(ReadProtectedModelViewSet):
|
||||
The djangorestframework plugin will get all `BusTeam` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/wei/membership/
|
||||
"""
|
||||
queryset = WEIMembership.objects
|
||||
queryset = WEIMembership.objects.order_by('id')
|
||||
serializer_class = WEIMembershipSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$user__username', '$bus__name', '$team__name', ]
|
||||
filterset_fields = ['user', 'club', 'bus', 'team', ]
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name',
|
||||
'club__note__alias__normalized_name', 'user__username', 'user__last_name',
|
||||
'user__first_name', 'user__email', 'user__note__alias__name',
|
||||
'user__note__alias__normalized_name', 'date_start', 'date_end', 'fee', 'roles', 'bus',
|
||||
'bus__name', 'team', 'team__name', 'registration', ]
|
||||
ordering_fields = ['id', 'date_start', 'date_end', ]
|
||||
search_fields = ['$club__name', '$club__email', '$club__note__alias__name',
|
||||
'$club__note__alias__normalized_name', '$user__username', '$user__last_name',
|
||||
'$user__first_name', '$user__email', '$user__note__alias__name',
|
||||
'$user__note__alias__normalized_name', '$roles__name', '$bus__name', '$team__name', ]
|
||||
|
23
apps/wei/migrations/0002_auto_20210313_1235.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.19 on 2021-03-13 11:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='weiclub',
|
||||
name='year',
|
||||
field=models.PositiveIntegerField(default=2021, unique=True, verbose_name='year'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='weiregistration',
|
||||
name='information_json',
|
||||
field=models.TextField(default='{}', help_text='Information about the registration (buses for old members, survey for the new members), encoded in JSON', verbose_name='registration information'),
|
||||
),
|
||||
]
|
@ -61,10 +61,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<dd class="col-xl-6">{{ club.note.balance | pretty_money }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if "note.change_alias"|has_perm:club.note.alias_set.first %}
|
||||
{% if "note.change_alias"|has_perm:club.note.alias.first %}
|
||||
<dt class="col-xl-4"><a
|
||||
href="{% url 'member:club_alias' club.pk %}">{% trans 'aliases'|capfirst %}</a></dt>
|
||||
<dd class="col-xl-8 text-truncate">{{ club.note.alias_set.all|join:", " }}</dd>
|
||||
<dd class="col-xl-8 text-truncate">{{ club.note.alias.all|join:", " }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt class="col-xl-4">{% trans 'email'|capfirst %}</dt>
|
||||
|
@ -4,16 +4,19 @@
|
||||
import subprocess
|
||||
from datetime import timedelta, date
|
||||
|
||||
from api.tests import TestAPI
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from member.models import Membership
|
||||
from member.models import Membership, Club
|
||||
from note.models import NoteClub, SpecialTransaction
|
||||
from treasury.models import SogeCredit
|
||||
|
||||
from ..api.views import BusViewSet, BusTeamViewSet, WEIClubViewSet, WEIMembershipViewSet, WEIRegistrationViewSet, \
|
||||
WEIRoleViewSet
|
||||
from ..forms import CurrentSurvey, WEISurveyAlgorithm, WEISurvey
|
||||
from ..models import WEIClub, Bus, BusTeam, WEIRole, WEIRegistration, WEIMembership
|
||||
|
||||
@ -524,7 +527,7 @@ class TestWEIRegistration(TestCase):
|
||||
sess["permission_mask"] = 0
|
||||
sess.save()
|
||||
response = self.client.get(reverse("wei:wei_update_registration", kwargs=dict(pk=self.registration.pk)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
sess["permission_mask"] = 42
|
||||
sess.save()
|
||||
|
||||
@ -807,3 +810,97 @@ class TestWEISurveyAlgorithm(TestCase):
|
||||
|
||||
def test_survey_algorithm(self):
|
||||
CurrentSurvey.get_algorithm_class()().run_algorithm()
|
||||
|
||||
|
||||
class TestWeiAPI(TestAPI):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
|
||||
self.year = timezone.now().year
|
||||
self.wei = WEIClub.objects.create(
|
||||
name="Test WEI",
|
||||
email="gc.wei@example.com",
|
||||
parent_club_id=2,
|
||||
membership_fee_paid=12500,
|
||||
membership_fee_unpaid=5500,
|
||||
membership_start=date(self.year, 1, 1),
|
||||
membership_end=date(self.year, 12, 31),
|
||||
membership_duration=396,
|
||||
year=self.year,
|
||||
date_start=date.today() + timedelta(days=2),
|
||||
date_end=date(self.year, 12, 31),
|
||||
)
|
||||
NoteClub.objects.create(club=self.wei)
|
||||
self.bus = Bus.objects.create(
|
||||
name="Test Bus",
|
||||
wei=self.wei,
|
||||
description="Test Bus",
|
||||
)
|
||||
self.team = BusTeam.objects.create(
|
||||
name="Test Team",
|
||||
bus=self.bus,
|
||||
color=0xFFFFFF,
|
||||
description="Test Team",
|
||||
)
|
||||
self.registration = WEIRegistration.objects.create(
|
||||
user_id=self.user.id,
|
||||
wei_id=self.wei.id,
|
||||
soge_credit=True,
|
||||
caution_check=True,
|
||||
birth_date=date(2000, 1, 1),
|
||||
gender="nonbinary",
|
||||
clothing_cut="male",
|
||||
clothing_size="XL",
|
||||
health_issues="I am a bot",
|
||||
emergency_contact_name="Pikachu",
|
||||
emergency_contact_phone="+33123456789",
|
||||
first_year=False,
|
||||
)
|
||||
Membership.objects.create(user=self.user, club=Club.objects.get(name="BDE"))
|
||||
Membership.objects.create(user=self.user, club=Club.objects.get(name="Kfet"))
|
||||
self.membership = WEIMembership.objects.create(
|
||||
user=self.user,
|
||||
club=self.wei,
|
||||
fee=125,
|
||||
bus=self.bus,
|
||||
team=self.team,
|
||||
registration=self.registration,
|
||||
)
|
||||
self.membership.roles.add(WEIRole.objects.last())
|
||||
self.membership.save()
|
||||
|
||||
def test_weiclub_api(self):
|
||||
"""
|
||||
Load WEI API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(WEIClubViewSet, "/api/wei/club/")
|
||||
|
||||
def test_wei_bus_api(self):
|
||||
"""
|
||||
Load Bus API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(BusViewSet, "/api/wei/bus/")
|
||||
|
||||
def test_wei_team_api(self):
|
||||
"""
|
||||
Load BusTeam API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(BusTeamViewSet, "/api/wei/team/")
|
||||
|
||||
def test_weirole_api(self):
|
||||
"""
|
||||
Load WEIRole API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(WEIRoleViewSet, "/api/wei/role/")
|
||||
|
||||
def test_weiregistration_api(self):
|
||||
"""
|
||||
Load WEIRegistration API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(WEIRegistrationViewSet, "/api/wei/registration/")
|
||||
|
||||
def test_weimembership_api(self):
|
||||
"""
|
||||
Load WEIMembership API page and test all filters and permissions
|
||||
"""
|
||||
self.check_viewset(WEIMembershipViewSet, "/api/wei/membership/")
|
||||
|
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
BIN
docs/_static/img/create_transaction.png
vendored
Normal file
After Width: | Height: | Size: 143 KiB |
344
docs/_static/img/graphs/activity.svg
vendored
Normal file
@ -0,0 +1,344 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="578pt" height="729pt"
|
||||
viewBox="0.00 0.00 578.00 729.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 725)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-725 574,-725 574,4 -4,4"/>
|
||||
<!-- activity_models_ActivityType -->
|
||||
<g id="node1" class="node">
|
||||
<title>activity_models_ActivityType</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-4 8,-92 186,-92 186,-4 8,-4"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9,-70 9,-91 185,-91 185,-70 9,-70"/>
|
||||
<text text-anchor="start" x="51" y="-79" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="61" y="-79" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    ActivityType    </text>
|
||||
<text text-anchor="start" x="11" y="-62.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-62.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="29" y="-62.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="93" y="-62.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="103" y="-62.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="141" y="-62.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-49.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-49.6" font-family="Roboto" font-size="8.00">can_invite</text>
|
||||
<text text-anchor="start" x="57" y="-49.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="93" y="-49.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="103" y="-49.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="149" y="-49.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-36.6" font-family="Roboto" font-size="8.00">guest_entry_fee</text>
|
||||
<text text-anchor="start" x="79" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="93" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="103" y="-36.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="173" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-23.6" font-family="Roboto" font-size="8.00">manage_entries</text>
|
||||
<text text-anchor="start" x="79" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="93" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="103" y="-23.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="149" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-10.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="42" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="93" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="103" y="-10.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="138" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-4 8,-92 186,-92 186,-4 8,-4"/>
|
||||
</g>
|
||||
<!-- activity_models_Activity -->
|
||||
<g id="node2" class="node">
|
||||
<title>activity_models_Activity</title>
|
||||
<polygon fill="white" stroke="transparent" points="197,-145 197,-324 369,-324 369,-145 197,-145"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="198,-301.5 198,-322.5 368,-322.5 368,-301.5 198,-301.5"/>
|
||||
<text text-anchor="start" x="248" y="-310.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="258" y="-310.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Activity    </text>
|
||||
<text text-anchor="start" x="200" y="-294.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-294.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="218" y="-294.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-294.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-294.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="333" y="-294.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-281.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-281.1" font-family="Roboto" font-weight="bold" font-size="8.00">activity_type</text>
|
||||
<text text-anchor="start" x="261" y="-281.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-281.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-281.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="356" y="-281.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-268.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-268.1" font-family="Roboto" font-weight="bold" font-size="8.00">attendees_club</text>
|
||||
<text text-anchor="start" x="271" y="-268.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-268.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-268.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="356" y="-268.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-255.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-255.1" font-family="Roboto" font-weight="bold" font-size="8.00">creater</text>
|
||||
<text text-anchor="start" x="238" y="-255.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-255.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-255.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="356" y="-255.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-242.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-242.1" font-family="Roboto" font-weight="bold" font-size="8.00">organizer</text>
|
||||
<text text-anchor="start" x="247" y="-242.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-242.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-242.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="356" y="-242.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-229.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-229.1" font-family="Roboto" font-size="8.00">date_end</text>
|
||||
<text text-anchor="start" x="244" y="-229.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-229.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-229.1" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="347" y="-229.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-216.1" font-family="Roboto" font-size="8.00">date_start</text>
|
||||
<text text-anchor="start" x="247" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-216.1" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="347" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-203.1" font-family="Roboto" font-size="8.00">description</text>
|
||||
<text text-anchor="start" x="249" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-203.1" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="327" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-190.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">location</text>
|
||||
<text text-anchor="start" x="238" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-190.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="330" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-177.1" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="231" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-177.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="330" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-164.1" font-family="Roboto" font-size="8.00">open</text>
|
||||
<text text-anchor="start" x="229" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-164.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="341" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="200" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="210" y="-151.1" font-family="Roboto" font-size="8.00">valid</text>
|
||||
<text text-anchor="start" x="226" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="285" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="295" y="-151.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="341" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="197,-145 197,-324 369,-324 369,-145 197,-145"/>
|
||||
</g>
|
||||
<!-- activity_models_Activity->activity_models_ActivityType -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>activity_models_Activity->activity_models_ActivityType</title>
|
||||
<path fill="none" stroke="black" d="M183.88,-135.18C170.27,-121.68 156.83,-108.35 144.74,-96.35"/>
|
||||
<ellipse fill="black" stroke="black" cx="186.89" cy="-138.16" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="197" y="-116.6" font-family="Roboto" font-size="8.00"> activity_type (+)</text>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node6" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="220,-37.5 220,-58.5 264,-58.5 264,-37.5 220,-37.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="220,-37 220,-58 264,-58 264,-37 220,-37"/>
|
||||
<text text-anchor="start" x="224" y="-45.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="229" y="-45.4" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="255" y="-45.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- activity_models_Activity->django_contrib_auth_models_User -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>activity_models_Activity->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M245.08,-133.07C244.3,-129.69 243.61,-126.33 243,-123 239.5,-103.79 239.8,-81.3 240.63,-66.2"/>
|
||||
<ellipse fill="black" stroke="black" cx="246.06" cy="-137.08" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="272.5" y="-116.6" font-family="Roboto" font-size="8.00"> creater (activity)</text>
|
||||
</g>
|
||||
<!-- member_models_Club -->
|
||||
<g id="node7" class="node">
|
||||
<title>member_models_Club</title>
|
||||
<polygon fill="white" stroke="transparent" points="338,-37.5 338,-58.5 382,-58.5 382,-37.5 338,-37.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="338,-37 338,-58 382,-58 382,-37 338,-37"/>
|
||||
<text text-anchor="start" x="342" y="-45.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="347" y="-45.4" font-family="Roboto" font-size="12.00" fill="white">Club</text>
|
||||
<text text-anchor="start" x="373" y="-45.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- activity_models_Activity->member_models_Club -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>activity_models_Activity->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M316.94,-133.22C319.56,-126.67 322.25,-120.22 325,-114 332.41,-97.23 342.46,-78.92 349.89,-66.01"/>
|
||||
<ellipse fill="black" stroke="black" cx="315.37" cy="-137.22" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="349.5" y="-116.6" font-family="Roboto" font-size="8.00"> organizer (+)</text>
|
||||
</g>
|
||||
<!-- activity_models_Activity->member_models_Club -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>activity_models_Activity->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M369.62,-133.72C371.23,-130.19 372.7,-126.61 374,-123 380.76,-104.17 374.44,-81.42 368.26,-66.15"/>
|
||||
<ellipse fill="black" stroke="black" cx="367.85" cy="-137.32" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="412" y="-116.6" font-family="Roboto" font-size="8.00"> attendees_club (+)</text>
|
||||
</g>
|
||||
<!-- activity_models_Entry -->
|
||||
<g id="node3" class="node">
|
||||
<title>activity_models_Entry</title>
|
||||
<polygon fill="white" stroke="transparent" points="284,-518 284,-606 448,-606 448,-518 284,-518"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="285,-584 285,-605 447,-605 447,-584 285,-584"/>
|
||||
<text text-anchor="start" x="336.5" y="-593" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="346.5" y="-593" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Entry    </text>
|
||||
<text text-anchor="start" x="287" y="-576.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297" y="-576.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="305" y="-576.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="340" y="-576.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="350" y="-576.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="388" y="-576.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287" y="-563.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297" y="-563.6" font-family="Roboto" font-weight="bold" font-size="8.00">activity</text>
|
||||
<text text-anchor="start" x="326" y="-563.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="340" y="-563.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="350" y="-563.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="411" y="-563.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287" y="-550.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297" y="-550.6" font-family="Roboto" font-weight="bold" font-size="8.00">guest</text>
|
||||
<text text-anchor="start" x="320" y="-550.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="340" y="-550.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="350" y="-550.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="426" y="-550.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287" y="-537.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297" y="-537.6" font-family="Roboto" font-weight="bold" font-size="8.00">note</text>
|
||||
<text text-anchor="start" x="315" y="-537.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="340" y="-537.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="350" y="-537.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (note_ptr)</text>
|
||||
<text text-anchor="start" x="435" y="-537.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287" y="-524.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297" y="-524.6" font-family="Roboto" font-size="8.00">time</text>
|
||||
<text text-anchor="start" x="313" y="-524.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="340" y="-524.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="350" y="-524.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="402" y="-524.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="284,-518 284,-606 448,-606 448,-518 284,-518"/>
|
||||
</g>
|
||||
<!-- activity_models_Entry->activity_models_Activity -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>activity_models_Entry->activity_models_Activity</title>
|
||||
<path fill="none" stroke="black" d="M268.49,-526.87C243.03,-513.01 218.65,-494.17 204,-469 178.65,-425.45 193.33,-372.56 216.59,-328.14"/>
|
||||
<ellipse fill="black" stroke="black" cx="272.15" cy="-528.79" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="233" y="-419.1" font-family="Roboto" font-size="8.00"> activity (entries)</text>
|
||||
</g>
|
||||
<!-- activity_models_Guest -->
|
||||
<g id="node4" class="node">
|
||||
<title>activity_models_Guest</title>
|
||||
<polygon fill="white" stroke="transparent" points="279.5,-377 279.5,-465 452.5,-465 452.5,-377 279.5,-377"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="281,-443 281,-464 452,-464 452,-443 281,-443"/>
|
||||
<text text-anchor="start" x="335.5" y="-452" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345.5" y="-452" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Guest    </text>
|
||||
<text text-anchor="start" x="283" y="-435.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="293" y="-435.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="301" y="-435.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345" y="-435.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="355" y="-435.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="393" y="-435.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="283" y="-422.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="293" y="-422.6" font-family="Roboto" font-weight="bold" font-size="8.00">activity</text>
|
||||
<text text-anchor="start" x="322" y="-422.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345" y="-422.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="355" y="-422.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="416" y="-422.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="283" y="-409.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="293" y="-409.6" font-family="Roboto" font-weight="bold" font-size="8.00">inviter</text>
|
||||
<text text-anchor="start" x="318" y="-409.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345" y="-409.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="355" y="-409.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (note_ptr)</text>
|
||||
<text text-anchor="start" x="440" y="-409.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="283" y="-396.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="293" y="-396.6" font-family="Roboto" font-size="8.00">first_name</text>
|
||||
<text text-anchor="start" x="331" y="-396.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345" y="-396.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="355" y="-396.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="390" y="-396.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="283" y="-383.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="293" y="-383.6" font-family="Roboto" font-size="8.00">last_name</text>
|
||||
<text text-anchor="start" x="330" y="-383.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="345" y="-383.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="355" y="-383.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="390" y="-383.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="279.5,-377 279.5,-465 452.5,-465 452.5,-377 279.5,-377"/>
|
||||
</g>
|
||||
<!-- activity_models_Entry->activity_models_Guest -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>activity_models_Entry->activity_models_Guest</title>
|
||||
<path fill="none" stroke="black" d="M366,-513.94C366,-499.45 366,-483.5 366,-469.01"/>
|
||||
<text text-anchor="middle" x="390.5" y="-489.6" font-family="Roboto" font-size="8.00"> guest (entry)</text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteUser -->
|
||||
<g id="node8" class="node">
|
||||
<title>note_models_notes_NoteUser</title>
|
||||
<polygon fill="white" stroke="transparent" points="411,-224 411,-245 481,-245 481,-224 411,-224"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="411,-223.5 411,-244.5 481,-244.5 481,-223.5 411,-223.5"/>
|
||||
<text text-anchor="start" x="415" y="-231.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="420" y="-231.9" font-family="Roboto" font-size="12.00" fill="white">NoteUser</text>
|
||||
<text text-anchor="start" x="472" y="-231.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- activity_models_Entry->note_models_notes_NoteUser -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>activity_models_Entry->note_models_notes_NoteUser</title>
|
||||
<path fill="none" stroke="black" d="M442.25,-507.94C453.52,-496.48 463.51,-483.41 470,-469 503.73,-394.13 469.49,-292.46 453.39,-252.56"/>
|
||||
<ellipse fill="black" stroke="black" cx="439.12" cy="-510.99" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="506.5" y="-419.1" font-family="Roboto" font-size="8.00"> note (entry)</text>
|
||||
</g>
|
||||
<!-- activity_models_Guest->activity_models_Activity -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>activity_models_Guest->activity_models_Activity</title>
|
||||
<path fill="none" stroke="black" d="M341.43,-365.38C336.13,-353.6 330.39,-340.84 324.64,-328.06"/>
|
||||
<ellipse fill="black" stroke="black" cx="343.2" cy="-369.32" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="354.5" y="-348.6" font-family="Roboto" font-size="8.00"> activity (+)</text>
|
||||
</g>
|
||||
<!-- activity_models_Guest->note_models_notes_NoteUser -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>activity_models_Guest->note_models_notes_NoteUser</title>
|
||||
<path fill="none" stroke="black" d="M389.66,-365.44C406.3,-327.05 427.51,-278.15 438.55,-252.68"/>
|
||||
<ellipse fill="black" stroke="black" cx="387.99" cy="-369.3" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="424.5" y="-348.6" font-family="Roboto" font-size="8.00"> inviter (guests)</text>
|
||||
</g>
|
||||
<!-- activity_models_GuestTransaction -->
|
||||
<g id="node5" class="node">
|
||||
<title>activity_models_GuestTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="350.5,-668 350.5,-717 537.5,-717 537.5,-668 350.5,-668"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="352,-694.5 352,-715.5 537,-715.5 537,-694.5 352,-694.5"/>
|
||||
<text text-anchor="start" x="386.5" y="-703.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="396.5" y="-703.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    GuestTransaction    </text>
|
||||
<text text-anchor="start" x="354" y="-687.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="364" y="-687.1" font-family="Roboto" font-weight="bold" font-size="8.00">transaction_ptr</text>
|
||||
<text text-anchor="start" x="425" y="-687.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="439" y="-687.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="449" y="-687.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="525" y="-687.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="354" y="-674.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="364" y="-674.1" font-family="Roboto" font-weight="bold" font-size="8.00">entry</text>
|
||||
<text text-anchor="start" x="385" y="-674.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="439" y="-674.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="449" y="-674.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="525" y="-674.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="350.5,-668 350.5,-717 537.5,-717 537.5,-668 350.5,-668"/>
|
||||
</g>
|
||||
<!-- activity_models_GuestTransaction->activity_models_Entry -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>activity_models_GuestTransaction->activity_models_Entry</title>
|
||||
<path fill="none" stroke="black" d="M423.89,-663.91C419.84,-658.07 415.69,-651.89 412,-646 404.83,-634.55 397.53,-621.92 390.93,-610.04"/>
|
||||
<text text-anchor="middle" x="456" y="-635.1" font-family="Roboto" font-size="8.00"> entry (guesttransaction)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_Transaction -->
|
||||
<g id="node9" class="node">
|
||||
<title>note_models_transactions_Transaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="482,-551.5 482,-572.5 562,-572.5 562,-551.5 482,-551.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="482,-551 482,-572 562,-572 562,-551 482,-551"/>
|
||||
<text text-anchor="start" x="486" y="-559.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="491" y="-559.4" font-family="Roboto" font-size="12.00" fill="white">Transaction</text>
|
||||
<text text-anchor="start" x="553" y="-559.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- activity_models_GuestTransaction->note_models_transactions_Transaction -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>activity_models_GuestTransaction->note_models_transactions_Transaction</title>
|
||||
<path fill="none" stroke="black" d="M484.47,-663.95C490.33,-658.55 495.78,-652.52 500,-646 510.86,-629.2 516.38,-607.22 519.17,-590.09"/>
|
||||
<polygon fill="none" stroke="black" points="522.64,-590.54 520.58,-580.15 515.71,-589.56 522.64,-590.54"/>
|
||||
<text text-anchor="middle" x="528" y="-639.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="528" y="-630.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 30 KiB |
108
docs/_static/img/graphs/logs.svg
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="209pt" height="237pt"
|
||||
viewBox="0.00 0.00 208.50 237.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 233)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-233 204.5,-233 204.5,4 -4,4"/>
|
||||
<!-- logs_models_Changelog -->
|
||||
<g id="node1" class="node">
|
||||
<title>logs_models_Changelog</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-85 8,-225 181,-225 181,-85 8,-85"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9.5,-203 9.5,-224 180.5,-224 180.5,-203 9.5,-203"/>
|
||||
<text text-anchor="start" x="52" y="-212" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="62" y="-212" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Changelog    </text>
|
||||
<text text-anchor="start" x="11.5" y="-195.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-195.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="29.5" y="-195.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-195.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-195.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="126.5" y="-195.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-182.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-182.6" font-family="Roboto" font-weight="bold" font-size="8.00">model</text>
|
||||
<text text-anchor="start" x="46.5" y="-182.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-182.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-182.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="149.5" y="-182.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-169.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-169.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="39.5" y="-169.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-169.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-169.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="149.5" y="-169.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-156.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-156.6" font-family="Roboto" font-size="8.00">action</text>
|
||||
<text text-anchor="start" x="43.5" y="-156.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-156.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-156.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="123.5" y="-156.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-143.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-143.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">data</text>
|
||||
<text text-anchor="start" x="37.5" y="-143.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-143.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-143.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">TextField</text>
|
||||
<text text-anchor="start" x="120.5" y="-143.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-130.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-130.6" font-family="Roboto" font-size="8.00">instance_pk</text>
|
||||
<text text-anchor="start" x="64.5" y="-130.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-130.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-130.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="123.5" y="-130.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-117.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-117.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">ip</text>
|
||||
<text text-anchor="start" x="28.5" y="-117.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-117.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-117.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">GenericIPAddressField</text>
|
||||
<text text-anchor="start" x="168.5" y="-117.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-104.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-104.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">previous</text>
|
||||
<text text-anchor="start" x="51.5" y="-104.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-104.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-104.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">TextField</text>
|
||||
<text text-anchor="start" x="120.5" y="-104.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-91.6" font-family="Roboto" font-size="8.00">timestamp</text>
|
||||
<text text-anchor="start" x="58.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="88.5" y="-91.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="140.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-85 8,-225 181,-225 181,-85 8,-85"/>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node2" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="23.5,-7.5 23.5,-28.5 67.5,-28.5 67.5,-7.5 23.5,-7.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="23.5,-7 23.5,-28 67.5,-28 67.5,-7 23.5,-7"/>
|
||||
<text text-anchor="start" x="27.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="32.5" y="-15.4" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="58.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- logs_models_Changelog->django_contrib_auth_models_User -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>logs_models_Changelog->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M62.09,-73.17C60.86,-69.74 59.65,-66.34 58.5,-63 55.49,-54.26 52.54,-44.41 50.21,-36.25"/>
|
||||
<ellipse fill="black" stroke="black" cx="63.48" cy="-76.95" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="90.5" y="-56.6" font-family="Roboto" font-size="8.00"> user (changelog)</text>
|
||||
</g>
|
||||
<!-- django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="node3" class="node">
|
||||
<title>django_contrib_contenttypes_models_ContentType</title>
|
||||
<polygon fill="white" stroke="transparent" points="101.5,-7.5 101.5,-28.5 187.5,-28.5 187.5,-7.5 101.5,-7.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="101.5,-7 101.5,-28 187.5,-28 187.5,-7 101.5,-7"/>
|
||||
<text text-anchor="start" x="105.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="110.5" y="-15.4" font-family="Roboto" font-size="12.00" fill="white">ContentType</text>
|
||||
<text text-anchor="start" x="178.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- logs_models_Changelog->django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>logs_models_Changelog->django_contrib_contenttypes_models_ContentType</title>
|
||||
<path fill="none" stroke="black" d="M124.44,-73.16C129.61,-59.21 134.48,-46.05 138.13,-36.2"/>
|
||||
<ellipse fill="black" stroke="black" cx="122.94" cy="-77.22" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="165.5" y="-56.6" font-family="Roboto" font-size="8.00"> model (changelog)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.3 KiB |
279
docs/_static/img/graphs/member.svg
vendored
Normal file
@ -0,0 +1,279 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="601pt" height="440pt"
|
||||
viewBox="0.00 0.00 600.50 440.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 436)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-436 596.5,-436 596.5,4 -4,4"/>
|
||||
<!-- member_models_Profile -->
|
||||
<g id="node1" class="node">
|
||||
<title>member_models_Profile</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-210 8,-428 227,-428 227,-210 8,-210"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9.5,-406 9.5,-427 226.5,-427 226.5,-406 9.5,-406"/>
|
||||
<text text-anchor="start" x="85.5" y="-415" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="95.5" y="-415" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Profile    </text>
|
||||
<text text-anchor="start" x="11.5" y="-398.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-398.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="29.5" y="-398.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-398.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-398.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="163.5" y="-398.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-385.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-385.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="39.5" y="-385.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-385.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-385.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="201.5" y="-385.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-372.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-372.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">address</text>
|
||||
<text text-anchor="start" x="49.5" y="-372.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-372.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-372.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="160.5" y="-372.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-359.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-359.6" font-family="Roboto" font-size="8.00">department</text>
|
||||
<text text-anchor="start" x="63.5" y="-359.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-359.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-359.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="160.5" y="-359.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-346.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-346.6" font-family="Roboto" font-size="8.00">email_confirmed</text>
|
||||
<text text-anchor="start" x="80.5" y="-346.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-346.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-346.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="171.5" y="-346.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-333.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-333.6" font-family="Roboto" font-size="8.00">last_report</text>
|
||||
<text text-anchor="start" x="59.5" y="-333.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-333.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-333.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="177.5" y="-333.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-320.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-320.6" font-family="Roboto" font-size="8.00">ml_art_registration</text>
|
||||
<text text-anchor="start" x="88.5" y="-320.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-320.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-320.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="171.5" y="-320.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-307.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-307.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">ml_events_registration</text>
|
||||
<text text-anchor="start" x="101.5" y="-307.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-307.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-307.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="160.5" y="-307.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-294.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-294.6" font-family="Roboto" font-size="8.00">ml_sport_registration</text>
|
||||
<text text-anchor="start" x="96.5" y="-294.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-294.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-294.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="171.5" y="-294.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-281.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-281.6" font-family="Roboto" font-size="8.00">paid</text>
|
||||
<text text-anchor="start" x="37.5" y="-281.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-281.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-281.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="171.5" y="-281.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-268.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-268.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">phone_number</text>
|
||||
<text text-anchor="start" x="76.5" y="-268.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-268.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-268.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">PhoneNumberField</text>
|
||||
<text text-anchor="start" x="195.5" y="-268.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-255.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-255.6" font-family="Roboto" font-size="8.00">promotion</text>
|
||||
<text text-anchor="start" x="57.5" y="-255.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-255.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-255.6" font-family="Roboto" font-size="8.00">PositiveSmallIntegerField</text>
|
||||
<text text-anchor="start" x="214.5" y="-255.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-242.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-242.6" font-family="Roboto" font-size="8.00">registration_valid</text>
|
||||
<text text-anchor="start" x="81.5" y="-242.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-242.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-242.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="171.5" y="-242.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-229.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-229.6" font-family="Roboto" font-size="8.00">report_frequency</text>
|
||||
<text text-anchor="start" x="82.5" y="-229.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-229.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-229.6" font-family="Roboto" font-size="8.00">PositiveSmallIntegerField</text>
|
||||
<text text-anchor="start" x="214.5" y="-229.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-216.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-216.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">section</text>
|
||||
<text text-anchor="start" x="46.5" y="-216.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.5" y="-216.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.5" y="-216.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="160.5" y="-216.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-210 8,-428 227,-428 227,-210 8,-210"/>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node4" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="125.5,-70 125.5,-91 169.5,-91 169.5,-70 125.5,-70"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="125.5,-69.5 125.5,-90.5 169.5,-90.5 169.5,-69.5 125.5,-69.5"/>
|
||||
<text text-anchor="start" x="129.5" y="-77.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="134.5" y="-77.9" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="160.5" y="-77.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- member_models_Profile->django_contrib_auth_models_User -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>member_models_Profile->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M131.71,-205.98C136.98,-164.44 142.38,-121.86 145.3,-98.85"/>
|
||||
<text text-anchor="middle" x="158.5" y="-181.6" font-family="Roboto" font-size="8.00"> user (profile)</text>
|
||||
</g>
|
||||
<!-- member_models_Club -->
|
||||
<g id="node2" class="node">
|
||||
<title>member_models_Club</title>
|
||||
<polygon fill="white" stroke="transparent" points="212,-4 212,-157 421,-157 421,-4 212,-4"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="213.5,-134.5 213.5,-155.5 420.5,-155.5 420.5,-134.5 213.5,-134.5"/>
|
||||
<text text-anchor="start" x="288.5" y="-143.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="298.5" y="-143.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Club    </text>
|
||||
<text text-anchor="start" x="215.5" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-127.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="233.5" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-127.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="376.5" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-114.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">parent_club</text>
|
||||
<text text-anchor="start" x="273.5" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-114.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="399.5" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-101.1" font-family="Roboto" font-size="8.00">email</text>
|
||||
<text text-anchor="start" x="244.5" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-101.1" font-family="Roboto" font-size="8.00">EmailField</text>
|
||||
<text text-anchor="start" x="375.5" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-88.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_duration</text>
|
||||
<text text-anchor="start" x="304.5" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-88.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="408.5" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-75.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_end</text>
|
||||
<text text-anchor="start" x="288.5" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-75.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">DateField</text>
|
||||
<text text-anchor="start" x="372.5" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-62.1" font-family="Roboto" font-size="8.00">membership_fee_paid</text>
|
||||
<text text-anchor="start" x="305.5" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-62.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="408.5" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-49.1" font-family="Roboto" font-size="8.00">membership_fee_unpaid</text>
|
||||
<text text-anchor="start" x="314.5" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-49.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="408.5" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-36.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_start</text>
|
||||
<text text-anchor="start" x="290.5" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-36.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">DateField</text>
|
||||
<text text-anchor="start" x="372.5" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-23.1" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="246.5" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-23.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="373.5" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="215.5" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-10.1" font-family="Roboto" font-size="8.00">require_memberships</text>
|
||||
<text text-anchor="start" x="302.5" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="328.5" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="338.5" y="-10.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="384.5" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="212,-4 212,-157 421,-157 421,-4 212,-4"/>
|
||||
</g>
|
||||
<!-- member_models_Club->member_models_Club -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>member_models_Club->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M437.02,-93.04C443.29,-89.95 447,-85.77 447,-80.5 447,-73.33 440.12,-68.17 429.26,-65.04"/>
|
||||
<ellipse fill="black" stroke="black" cx="433" cy="-94.55" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="480.5" y="-78.6" font-family="Roboto" font-size="8.00"> parent_club (club)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership -->
|
||||
<g id="node3" class="node">
|
||||
<title>member_models_Membership</title>
|
||||
<polygon fill="white" stroke="transparent" points="261,-268.5 261,-369.5 418,-369.5 418,-268.5 261,-268.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="262.5,-347 262.5,-368 417.5,-368 417.5,-347 262.5,-347"/>
|
||||
<text text-anchor="start" x="294" y="-356" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304" y="-356" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Membership    </text>
|
||||
<text text-anchor="start" x="264.5" y="-339.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-339.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="282.5" y="-339.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-339.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-339.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="373.5" y="-339.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264.5" y="-326.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-326.6" font-family="Roboto" font-weight="bold" font-size="8.00">club</text>
|
||||
<text text-anchor="start" x="292.5" y="-326.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-326.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-326.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="396.5" y="-326.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264.5" y="-313.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-313.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="292.5" y="-313.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-313.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-313.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="396.5" y="-313.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264.5" y="-300.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-300.6" font-family="Roboto" font-size="8.00">date_end</text>
|
||||
<text text-anchor="start" x="308.5" y="-300.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-300.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-300.6" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="369.5" y="-300.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264.5" y="-287.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-287.6" font-family="Roboto" font-size="8.00">date_start</text>
|
||||
<text text-anchor="start" x="311.5" y="-287.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-287.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-287.6" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="369.5" y="-287.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264.5" y="-274.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="274.5" y="-274.6" font-family="Roboto" font-size="8.00">fee</text>
|
||||
<text text-anchor="start" x="286.5" y="-274.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="325.5" y="-274.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="335.5" y="-274.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="405.5" y="-274.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="261,-268.5 261,-369.5 418,-369.5 418,-268.5 261,-268.5"/>
|
||||
</g>
|
||||
<!-- member_models_Membership->member_models_Club -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>member_models_Membership->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M333.5,-256.33C330.68,-227.34 327.29,-192.43 324.27,-161.45"/>
|
||||
<ellipse fill="black" stroke="black" cx="333.89" cy="-260.35" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="362.5" y="-181.6" font-family="Roboto" font-size="8.00"> club (membership)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership->django_contrib_auth_models_User -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>member_models_Membership->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M290.49,-258.16C275.79,-240.95 259.35,-222.4 243.5,-206 222.95,-184.74 213.11,-183.97 194.5,-161 178.45,-141.19 164.21,-115.32 155.72,-98.56"/>
|
||||
<ellipse fill="black" stroke="black" cx="293.24" cy="-261.39" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="261" y="-181.6" font-family="Roboto" font-size="8.00"> user (memberships)</text>
|
||||
</g>
|
||||
<!-- permission_models_Role -->
|
||||
<g id="node5" class="node">
|
||||
<title>permission_models_Role</title>
|
||||
<polygon fill="white" stroke="transparent" points="540.5,-70 540.5,-91 584.5,-91 584.5,-70 540.5,-70"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="540.5,-69.5 540.5,-90.5 584.5,-90.5 584.5,-69.5 540.5,-69.5"/>
|
||||
<text text-anchor="start" x="545" y="-77.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="550" y="-77.9" font-family="Roboto" font-size="12.00" fill="white">Role</text>
|
||||
<text text-anchor="start" x="575" y="-77.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- member_models_Membership->permission_models_Role -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>member_models_Membership->permission_models_Role</title>
|
||||
<path fill="none" stroke="black" d="M422.13,-259.45C456.46,-232.28 494.89,-197.84 523.5,-161 536.38,-144.41 546.62,-122.88 553.37,-106.41"/>
|
||||
<ellipse fill="black" stroke="black" cx="418.91" cy="-261.97" rx="4" ry="4"/>
|
||||
<ellipse fill="black" stroke="black" cx="554.93" cy="-102.49" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="542.5" y="-181.6" font-family="Roboto" font-size="8.00"> roles (membership)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 26 KiB |
588
docs/_static/img/graphs/note.svg
vendored
Normal file
@ -0,0 +1,588 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="1306pt" height="872pt"
|
||||
viewBox="0.00 0.00 1306.00 872.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 868)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-868 1302,-868 1302,4 -4,4"/>
|
||||
<!-- polymorphic_models_PolymorphicModel -->
|
||||
<g id="node1" class="node">
|
||||
<title>polymorphic_models_PolymorphicModel</title>
|
||||
<polygon fill="white" stroke="transparent" points="254,-85 254,-121 441,-121 441,-85 254,-85"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="255.5,-99 255.5,-120 440.5,-120 440.5,-99 255.5,-99"/>
|
||||
<text text-anchor="start" x="286.5" y="-108" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="296.5" y="-108" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    PolymorphicModel    </text>
|
||||
<text text-anchor="start" x="257.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="267.5" y="-91.6" font-family="Roboto" font-weight="bold" font-size="8.00">polymorphic_ctype</text>
|
||||
<text text-anchor="start" x="343.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="357.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="367.5" y="-91.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="428.5" y="-91.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="254,-85 254,-121 441,-121 441,-85 254,-85"/>
|
||||
</g>
|
||||
<!-- django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="node13" class="node">
|
||||
<title>django_contrib_contenttypes_models_ContentType</title>
|
||||
<polygon fill="white" stroke="transparent" points="304.5,-7.5 304.5,-28.5 390.5,-28.5 390.5,-7.5 304.5,-7.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="304.5,-7 304.5,-28 390.5,-28 390.5,-7 304.5,-7"/>
|
||||
<text text-anchor="start" x="308.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="313.5" y="-15.4" font-family="Roboto" font-size="12.00" fill="white">ContentType</text>
|
||||
<text text-anchor="start" x="381.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- polymorphic_models_PolymorphicModel->django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>polymorphic_models_PolymorphicModel->django_contrib_contenttypes_models_ContentType</title>
|
||||
<path fill="none" stroke="black" d="M347.5,-72.49C347.5,-60.3 347.5,-46.6 347.5,-36.12"/>
|
||||
<ellipse fill="black" stroke="black" cx="347.5" cy="-76.63" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="463" y="-56.6" font-family="Roboto" font-size="8.00"> polymorphic_ctype (polymorphic_%(app_label)s.%(class)s_set+)</text>
|
||||
</g>
|
||||
<!-- note_models_notes_Note -->
|
||||
<g id="node2" class="node">
|
||||
<title>note_models_notes_Note</title>
|
||||
<polygon fill="white" stroke="transparent" points="366,-183 366,-319 553,-319 553,-183 366,-183"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="367.5,-288 367.5,-318 552.5,-318 552.5,-288 367.5,-288"/>
|
||||
<text text-anchor="start" x="438" y="-306" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="448" y="-306" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Note</text>
|
||||
<text text-anchor="start" x="402.5" y="-296" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white"><</text>
|
||||
<text text-anchor="start" x="409.5" y="-296" font-family="Roboto" font-weight="bold" font-style="italic" font-size="10.00" fill="white">PolymorphicModel</text>
|
||||
<text text-anchor="start" x="498.5" y="-296" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">>    </text>
|
||||
<text text-anchor="start" x="369.5" y="-280.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-280.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="387.5" y="-280.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-280.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-280.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="517.5" y="-280.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-267.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-267.6" font-family="Roboto" font-weight="bold" font-style="italic" font-size="8.00">polymorphic_ctype</text>
|
||||
<text text-anchor="start" x="455.5" y="-267.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-267.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-267.6" font-family="Roboto" font-weight="bold" font-style="italic" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="540.5" y="-267.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-254.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-254.6" font-family="Roboto" font-size="8.00">balance</text>
|
||||
<text text-anchor="start" x="407.5" y="-254.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-254.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-254.6" font-family="Roboto" font-size="8.00">BigIntegerField</text>
|
||||
<text text-anchor="start" x="533.5" y="-254.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-241.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-241.6" font-family="Roboto" font-size="8.00">created_at</text>
|
||||
<text text-anchor="start" x="417.5" y="-241.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-241.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-241.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="531.5" y="-241.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-228.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-228.6" font-family="Roboto" font-size="8.00">display_image</text>
|
||||
<text text-anchor="start" x="430.5" y="-228.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-228.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-228.6" font-family="Roboto" font-size="8.00">ImageField</text>
|
||||
<text text-anchor="start" x="519.5" y="-228.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-215.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-215.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">inactivity_reason</text>
|
||||
<text text-anchor="start" x="437.5" y="-215.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-215.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-215.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="514.5" y="-215.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-202.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-202.6" font-family="Roboto" font-size="8.00">is_active</text>
|
||||
<text text-anchor="start" x="410.5" y="-202.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-202.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-202.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="525.5" y="-202.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="369.5" y="-189.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="379.5" y="-189.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">last_negative</text>
|
||||
<text text-anchor="start" x="426.5" y="-189.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="469.5" y="-189.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="479.5" y="-189.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">DateTimeField</text>
|
||||
<text text-anchor="start" x="531.5" y="-189.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="366,-183 366,-319 553,-319 553,-183 366,-183"/>
|
||||
</g>
|
||||
<!-- note_models_notes_Note->polymorphic_models_PolymorphicModel -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>note_models_notes_Note->polymorphic_models_PolymorphicModel</title>
|
||||
<path fill="none" stroke="black" d="M404.81,-178.71C392.51,-162.68 380.13,-146.54 370.04,-133.38"/>
|
||||
<polygon fill="none" stroke="black" points="372.62,-131 363.76,-125.2 367.07,-135.26 372.62,-131"/>
|
||||
<text text-anchor="middle" x="410.5" y="-154.6" font-family="Roboto" font-size="8.00"> abstract</text>
|
||||
<text text-anchor="middle" x="410.5" y="-145.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteUser -->
|
||||
<g id="node3" class="node">
|
||||
<title>note_models_notes_NoteUser</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-450.5 8,-499.5 167,-499.5 167,-450.5 8,-450.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9.5,-477 9.5,-498 166.5,-498 166.5,-477 9.5,-477"/>
|
||||
<text text-anchor="start" x="49" y="-486" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="59" y="-486" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    NoteUser    </text>
|
||||
<text text-anchor="start" x="11.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">note_ptr</text>
|
||||
<text text-anchor="start" x="54.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="68.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="154.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21.5" y="-456.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="39.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="68.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="78.5" y="-456.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="154.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-450.5 8,-499.5 167,-499.5 167,-450.5 8,-450.5"/>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteUser->note_models_notes_Note -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>note_models_notes_NoteUser->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M109.11,-446.28C127.2,-424.73 154.77,-395.49 184.5,-377 247.87,-337.59 275.92,-354.68 343.5,-323 345.12,-322.24 346.74,-321.47 348.37,-320.68"/>
|
||||
<polygon fill="none" stroke="black" points="350.34,-323.6 357.71,-315.99 347.2,-317.34 350.34,-323.6"/>
|
||||
<text text-anchor="middle" x="311.5" y="-352.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="311.5" y="-343.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node14" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="65.5,-240.5 65.5,-261.5 109.5,-261.5 109.5,-240.5 65.5,-240.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="65.5,-240 65.5,-261 109.5,-261 109.5,-240 65.5,-240"/>
|
||||
<text text-anchor="start" x="69.5" y="-248.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="74.5" y="-248.4" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="100.5" y="-248.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteUser->django_contrib_auth_models_User -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>note_models_notes_NoteUser->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M87.5,-446.33C87.5,-399.84 87.5,-307.38 87.5,-269.15"/>
|
||||
<text text-anchor="middle" x="109" y="-348.1" font-family="Roboto" font-size="8.00"> user (note)</text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteClub -->
|
||||
<g id="node4" class="node">
|
||||
<title>note_models_notes_NoteClub</title>
|
||||
<polygon fill="white" stroke="transparent" points="1131,-450.5 1131,-499.5 1290,-499.5 1290,-450.5 1131,-450.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="1132.5,-477 1132.5,-498 1289.5,-498 1289.5,-477 1132.5,-477"/>
|
||||
<text text-anchor="start" x="1171.5" y="-486" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1181.5" y="-486" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    NoteClub    </text>
|
||||
<text text-anchor="start" x="1134.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1144.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">note_ptr</text>
|
||||
<text text-anchor="start" x="1177.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1191.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1201.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="1277.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1134.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1144.5" y="-456.6" font-family="Roboto" font-weight="bold" font-size="8.00">club</text>
|
||||
<text text-anchor="start" x="1162.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1191.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1201.5" y="-456.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="1277.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="1131,-450.5 1131,-499.5 1290,-499.5 1290,-450.5 1131,-450.5"/>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteClub->note_models_notes_Note -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>note_models_notes_NoteClub->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M1190.62,-446.48C1172.94,-424.15 1145.07,-393.72 1113.5,-377 939.22,-284.69 706.77,-259.9 571.09,-253.63"/>
|
||||
<polygon fill="none" stroke="black" points="571.23,-250.13 561.09,-253.2 570.92,-257.13 571.23,-250.13"/>
|
||||
<text text-anchor="middle" x="1084.5" y="-352.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="1084.5" y="-343.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- member_models_Club -->
|
||||
<g id="node15" class="node">
|
||||
<title>member_models_Club</title>
|
||||
<polygon fill="white" stroke="transparent" points="1188.5,-240.5 1188.5,-261.5 1232.5,-261.5 1232.5,-240.5 1188.5,-240.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="1188.5,-240 1188.5,-261 1232.5,-261 1232.5,-240 1188.5,-240"/>
|
||||
<text text-anchor="start" x="1192.5" y="-248.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="1197.5" y="-248.4" font-family="Roboto" font-size="12.00" fill="white">Club</text>
|
||||
<text text-anchor="start" x="1223.5" y="-248.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteClub->member_models_Club -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>note_models_notes_NoteClub->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M1210.5,-446.33C1210.5,-399.84 1210.5,-307.38 1210.5,-269.15"/>
|
||||
<text text-anchor="middle" x="1231" y="-348.1" font-family="Roboto" font-size="8.00"> club (note)</text>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteSpecial -->
|
||||
<g id="node5" class="node">
|
||||
<title>note_models_notes_NoteSpecial</title>
|
||||
<polygon fill="white" stroke="transparent" points="926.5,-450.5 926.5,-499.5 1096.5,-499.5 1096.5,-450.5 926.5,-450.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="927.5,-477 927.5,-498 1095.5,-498 1095.5,-477 927.5,-477"/>
|
||||
<text text-anchor="start" x="966" y="-486" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="976" y="-486" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    NoteSpecial    </text>
|
||||
<text text-anchor="start" x="929.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="939.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">note_ptr</text>
|
||||
<text text-anchor="start" x="972.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="997.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1007.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="1083.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="929.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="939.5" y="-456.6" font-family="Roboto" font-size="8.00">special_type</text>
|
||||
<text text-anchor="start" x="983.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="997.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1007.5" y="-456.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="1042.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="926.5,-450.5 926.5,-499.5 1096.5,-499.5 1096.5,-450.5 926.5,-450.5"/>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteSpecial->note_models_notes_Note -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>note_models_notes_NoteSpecial->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M989.16,-446.3C970.14,-424.47 940.97,-394.83 909.5,-377 803.05,-316.67 665.72,-284.03 570.92,-267.4"/>
|
||||
<polygon fill="none" stroke="black" points="571.47,-263.94 561.02,-265.69 570.28,-270.84 571.47,-263.94"/>
|
||||
<text text-anchor="middle" x="884.5" y="-352.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="884.5" y="-343.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- note_models_notes_Alias -->
|
||||
<g id="node6" class="node">
|
||||
<title>note_models_notes_Alias</title>
|
||||
<polygon fill="white" stroke="transparent" points="717,-437.5 717,-512.5 892,-512.5 892,-437.5 717,-437.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="718.5,-490 718.5,-511 891.5,-511 891.5,-490 718.5,-490"/>
|
||||
<text text-anchor="start" x="775.5" y="-499" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="785.5" y="-499" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Alias    </text>
|
||||
<text text-anchor="start" x="720.5" y="-482.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="730.5" y="-482.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="738.5" y="-482.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="808.5" y="-482.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="818.5" y="-482.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="856.5" y="-482.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="720.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="730.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">note</text>
|
||||
<text text-anchor="start" x="748.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="808.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="818.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="879.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="720.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="730.5" y="-456.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="751.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="808.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="818.5" y="-456.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="853.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="720.5" y="-443.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="730.5" y="-443.6" font-family="Roboto" font-size="8.00">normalized_name</text>
|
||||
<text text-anchor="start" x="794.5" y="-443.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="808.5" y="-443.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="818.5" y="-443.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="853.5" y="-443.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="717,-437.5 717,-512.5 892,-512.5 892,-437.5 717,-437.5"/>
|
||||
</g>
|
||||
<!-- note_models_notes_Alias->note_models_notes_Note -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>note_models_notes_Alias->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M759.38,-427.39C741.62,-410.4 720.47,-391.75 699.5,-377 656.46,-346.73 605.15,-319.02 561.03,-297.42"/>
|
||||
<ellipse fill="black" stroke="black" cx="762.4" cy="-430.3" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="691" y="-348.1" font-family="Roboto" font-size="8.00"> note (alias)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_TemplateCategory -->
|
||||
<g id="node7" class="node">
|
||||
<title>note_models_transactions_TemplateCategory</title>
|
||||
<polygon fill="white" stroke="transparent" points="552,-450.5 552,-499.5 683,-499.5 683,-450.5 552,-450.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="553.5,-477 553.5,-498 682.5,-498 682.5,-477 553.5,-477"/>
|
||||
<text text-anchor="start" x="558.5" y="-486" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.5" y="-486" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    TemplateCategory    </text>
|
||||
<text text-anchor="start" x="555.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="565.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="573.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="611.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="621.5" y="-469.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="659.5" y="-469.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="555.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="565.5" y="-456.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="586.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="611.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="621.5" y="-456.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="656.5" y="-456.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="552,-450.5 552,-499.5 683,-499.5 683,-450.5 552,-450.5"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_TransactionTemplate -->
|
||||
<g id="node8" class="node">
|
||||
<title>note_models_transactions_TransactionTemplate</title>
|
||||
<polygon fill="white" stroke="transparent" points="625,-631 625,-758 806,-758 806,-631 625,-631"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="626.5,-735.5 626.5,-756.5 805.5,-756.5 805.5,-735.5 626.5,-735.5"/>
|
||||
<text text-anchor="start" x="651" y="-744.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="661" y="-744.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    TransactionTemplate    </text>
|
||||
<text text-anchor="start" x="628.5" y="-728.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-728.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="646.5" y="-728.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-728.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-728.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="746.5" y="-728.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-715.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-715.1" font-family="Roboto" font-weight="bold" font-size="8.00">category</text>
|
||||
<text text-anchor="start" x="672.5" y="-715.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-715.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-715.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="769.5" y="-715.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-702.1" font-family="Roboto" font-weight="bold" font-size="8.00">destination</text>
|
||||
<text text-anchor="start" x="684.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-702.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (note_ptr)</text>
|
||||
<text text-anchor="start" x="793.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-689.1" font-family="Roboto" font-size="8.00">amount</text>
|
||||
<text text-anchor="start" x="666.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-689.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="778.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-676.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">description</text>
|
||||
<text text-anchor="start" x="677.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-676.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="743.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-663.1" font-family="Roboto" font-size="8.00">display</text>
|
||||
<text text-anchor="start" x="663.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-663.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="754.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-650.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-650.1" font-family="Roboto" font-size="8.00">highlighted</text>
|
||||
<text text-anchor="start" x="677.5" y="-650.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-650.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-650.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="754.5" y="-650.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="628.5" y="-637.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="638.5" y="-637.1" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="659.5" y="-637.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="698.5" y="-637.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="708.5" y="-637.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="743.5" y="-637.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="625,-631 625,-758 806,-758 806,-631 625,-631"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_TransactionTemplate->note_models_notes_NoteClub -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>note_models_transactions_TransactionTemplate->note_models_notes_NoteClub</title>
|
||||
<path fill="none" stroke="black" d="M822.23,-675.98C905.89,-658.86 1022.9,-627.27 1113.5,-573 1143.74,-554.89 1171.43,-525.3 1189.42,-503.55"/>
|
||||
<ellipse fill="black" stroke="black" cx="818.16" cy="-676.8" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="1100.5" y="-598.1" font-family="Roboto" font-size="8.00"> destination (+)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_TransactionTemplate->note_models_transactions_TemplateCategory -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>note_models_transactions_TransactionTemplate->note_models_transactions_TemplateCategory</title>
|
||||
<path fill="none" stroke="black" d="M682.12,-619.41C664.15,-579.54 643.06,-532.72 629.98,-503.71"/>
|
||||
<ellipse fill="black" stroke="black" cx="683.79" cy="-623.12" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="713.5" y="-598.1" font-family="Roboto" font-size="8.00"> category (templates)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_Transaction -->
|
||||
<g id="node9" class="node">
|
||||
<title>note_models_transactions_Transaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="201.5,-381 201.5,-569 397.5,-569 397.5,-381 201.5,-381"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="202.5,-538 202.5,-568 396.5,-568 396.5,-538 202.5,-538"/>
|
||||
<text text-anchor="start" x="261" y="-556" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="271" y="-556" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Transaction</text>
|
||||
<text text-anchor="start" x="242" y="-546" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white"><</text>
|
||||
<text text-anchor="start" x="249" y="-546" font-family="Roboto" font-weight="bold" font-style="italic" font-size="10.00" fill="white">PolymorphicModel</text>
|
||||
<text text-anchor="start" x="338" y="-546" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">>    </text>
|
||||
<text text-anchor="start" x="204.5" y="-530.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-530.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="222.5" y="-530.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-530.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-530.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="352.5" y="-530.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-517.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-517.6" font-family="Roboto" font-weight="bold" font-size="8.00">destination</text>
|
||||
<text text-anchor="start" x="260.5" y="-517.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-517.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-517.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="375.5" y="-517.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-504.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-504.6" font-family="Roboto" font-weight="bold" font-style="italic" font-size="8.00">polymorphic_ctype</text>
|
||||
<text text-anchor="start" x="290.5" y="-504.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-504.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-504.6" font-family="Roboto" font-weight="bold" font-style="italic" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="375.5" y="-504.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-491.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-491.6" font-family="Roboto" font-weight="bold" font-size="8.00">source</text>
|
||||
<text text-anchor="start" x="242.5" y="-491.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-491.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-491.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="375.5" y="-491.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-478.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-478.6" font-family="Roboto" font-size="8.00">amount</text>
|
||||
<text text-anchor="start" x="242.5" y="-478.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-478.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-478.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="384.5" y="-478.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-465.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-465.6" font-family="Roboto" font-size="8.00">created_at</text>
|
||||
<text text-anchor="start" x="252.5" y="-465.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-465.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-465.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="366.5" y="-465.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-452.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-452.6" font-family="Roboto" font-size="8.00">destination_alias</text>
|
||||
<text text-anchor="start" x="273.5" y="-452.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-452.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-452.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="349.5" y="-452.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-439.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-439.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">invalidity_reason</text>
|
||||
<text text-anchor="start" x="272.5" y="-439.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-439.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-439.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="349.5" y="-439.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-426.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-426.6" font-family="Roboto" font-size="8.00">quantity</text>
|
||||
<text text-anchor="start" x="242.5" y="-426.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-426.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-426.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="384.5" y="-426.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-413.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-413.6" font-family="Roboto" font-size="8.00">reason</text>
|
||||
<text text-anchor="start" x="239.5" y="-413.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-413.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-413.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="349.5" y="-413.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-400.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-400.6" font-family="Roboto" font-size="8.00">source_alias</text>
|
||||
<text text-anchor="start" x="259.5" y="-400.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-400.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-400.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="349.5" y="-400.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="204.5" y="-387.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214.5" y="-387.6" font-family="Roboto" font-size="8.00">valid</text>
|
||||
<text text-anchor="start" x="230.5" y="-387.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="304.5" y="-387.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="314.5" y="-387.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="360.5" y="-387.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="201.5,-381 201.5,-569 397.5,-569 397.5,-381 201.5,-381"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_Transaction->polymorphic_models_PolymorphicModel -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>note_models_transactions_Transaction->polymorphic_models_PolymorphicModel</title>
|
||||
<path fill="none" stroke="black" d="M291.68,-376.8C289.64,-318.52 291.7,-243.46 308.5,-179 312.59,-163.31 320.42,-147.19 327.99,-133.93"/>
|
||||
<polygon fill="none" stroke="black" points="331.05,-135.64 333.14,-125.26 325.03,-132.07 331.05,-135.64"/>
|
||||
<text text-anchor="middle" x="328.5" y="-253.6" font-family="Roboto" font-size="8.00"> abstract</text>
|
||||
<text text-anchor="middle" x="328.5" y="-244.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_Transaction->note_models_notes_Note -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>note_models_transactions_Transaction->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M407.37,-370.47C410.2,-366.68 412.92,-362.85 415.5,-359 422.87,-347.98 429.37,-335.54 434.95,-323.13"/>
|
||||
<ellipse fill="black" stroke="black" cx="404.93" cy="-373.64" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="445.5" y="-348.1" font-family="Roboto" font-size="8.00"> source (+)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_Transaction->note_models_notes_Note -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>note_models_transactions_Transaction->note_models_notes_Note</title>
|
||||
<path fill="none" stroke="black" d="M339.68,-369.46C345.09,-359.49 351.03,-349.83 357.5,-341 361.94,-334.93 366.87,-328.99 372.09,-323.24"/>
|
||||
<ellipse fill="black" stroke="black" cx="337.7" cy="-373.21" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="384.5" y="-348.1" font-family="Roboto" font-size="8.00"> destination (+)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_RecurrentTransaction -->
|
||||
<g id="node10" class="node">
|
||||
<title>note_models_transactions_RecurrentTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="39,-811 39,-860 226,-860 226,-811 39,-811"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="40.5,-837.5 40.5,-858.5 225.5,-858.5 225.5,-837.5 40.5,-837.5"/>
|
||||
<text text-anchor="start" x="66" y="-846.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="76" y="-846.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    RecurrentTransaction    </text>
|
||||
<text text-anchor="start" x="42.5" y="-830.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="52.5" y="-830.1" font-family="Roboto" font-weight="bold" font-size="8.00">transaction_ptr</text>
|
||||
<text text-anchor="start" x="113.5" y="-830.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="127.5" y="-830.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="137.5" y="-830.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="213.5" y="-830.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="42.5" y="-817.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="52.5" y="-817.1" font-family="Roboto" font-weight="bold" font-size="8.00">template</text>
|
||||
<text text-anchor="start" x="86.5" y="-817.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="127.5" y="-817.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="137.5" y="-817.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="198.5" y="-817.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="39,-811 39,-860 226,-860 226,-811 39,-811"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_RecurrentTransaction->note_models_transactions_TransactionTemplate -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>note_models_transactions_RecurrentTransaction->note_models_transactions_TransactionTemplate</title>
|
||||
<path fill="none" stroke="black" d="M242.27,-830.15C340.58,-823.38 487.34,-806.09 607.5,-762 610.6,-760.86 613.71,-759.63 616.81,-758.32"/>
|
||||
<ellipse fill="black" stroke="black" cx="238.09" cy="-830.43" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="601.5" y="-782.6" font-family="Roboto" font-size="8.00"> template (recurrenttransaction)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_RecurrentTransaction->note_models_transactions_Transaction -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>note_models_transactions_RecurrentTransaction->note_models_transactions_Transaction</title>
|
||||
<path fill="none" stroke="black" d="M124.08,-806.79C113.13,-765.51 98.42,-686.36 125.5,-627 127.55,-622.52 153.83,-599.09 185.37,-571.97"/>
|
||||
<polygon fill="none" stroke="black" points="187.73,-574.55 193.05,-565.39 183.18,-569.24 187.73,-574.55"/>
|
||||
<text text-anchor="middle" x="145.5" y="-697.1" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="145.5" y="-688.1" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_SpecialTransaction -->
|
||||
<g id="node11" class="node">
|
||||
<title>note_models_transactions_SpecialTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="183,-657 183,-732 370,-732 370,-657 183,-657"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="184.5,-709.5 184.5,-730.5 369.5,-730.5 369.5,-709.5 184.5,-709.5"/>
|
||||
<text text-anchor="start" x="215.5" y="-718.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="225.5" y="-718.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    SpecialTransaction    </text>
|
||||
<text text-anchor="start" x="186.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.5" y="-702.1" font-family="Roboto" font-weight="bold" font-size="8.00">transaction_ptr</text>
|
||||
<text text-anchor="start" x="257.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="271.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="281.5" y="-702.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="357.5" y="-702.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.5" y="-689.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">bank</text>
|
||||
<text text-anchor="start" x="214.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="271.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="281.5" y="-689.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="316.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.5" y="-676.1" font-family="Roboto" font-size="8.00">first_name</text>
|
||||
<text text-anchor="start" x="234.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="271.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="281.5" y="-676.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="316.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.5" y="-663.1" font-family="Roboto" font-size="8.00">last_name</text>
|
||||
<text text-anchor="start" x="233.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="271.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="281.5" y="-663.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="316.5" y="-663.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="183,-657 183,-732 370,-732 370,-657 183,-657"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_SpecialTransaction->note_models_transactions_Transaction -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>note_models_transactions_SpecialTransaction->note_models_transactions_Transaction</title>
|
||||
<path fill="none" stroke="black" d="M280.8,-652.85C282.89,-633.07 285.51,-608.27 288.13,-583.55"/>
|
||||
<polygon fill="none" stroke="black" points="291.63,-583.66 289.21,-573.35 284.67,-582.92 291.63,-583.66"/>
|
||||
<text text-anchor="middle" x="307.5" y="-602.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="307.5" y="-593.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_MembershipTransaction -->
|
||||
<g id="node12" class="node">
|
||||
<title>note_models_transactions_MembershipTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="404,-670 404,-719 591,-719 591,-670 404,-670"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="405.5,-696.5 405.5,-717.5 590.5,-717.5 590.5,-696.5 405.5,-696.5"/>
|
||||
<text text-anchor="start" x="425" y="-705.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="435" y="-705.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    MembershipTransaction    </text>
|
||||
<text text-anchor="start" x="407.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="417.5" y="-689.1" font-family="Roboto" font-weight="bold" font-size="8.00">transaction_ptr</text>
|
||||
<text text-anchor="start" x="478.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="492.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="502.5" y="-689.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="578.5" y="-689.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="407.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="417.5" y="-676.1" font-family="Roboto" font-weight="bold" font-size="8.00">membership</text>
|
||||
<text text-anchor="start" x="466.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="492.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="502.5" y="-676.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="578.5" y="-676.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="404,-670 404,-719 591,-719 591,-670 404,-670"/>
|
||||
</g>
|
||||
<!-- note_models_transactions_MembershipTransaction->note_models_transactions_Transaction -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>note_models_transactions_MembershipTransaction->note_models_transactions_Transaction</title>
|
||||
<path fill="none" stroke="black" d="M472.42,-665.95C452.58,-644.15 423.52,-612.24 394.88,-580.77"/>
|
||||
<polygon fill="none" stroke="black" points="397.32,-578.25 388,-573.21 392.14,-582.97 397.32,-578.25"/>
|
||||
<text text-anchor="middle" x="436.5" y="-602.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="436.5" y="-593.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- member_models_Membership -->
|
||||
<g id="node16" class="node">
|
||||
<title>member_models_Membership</title>
|
||||
<polygon fill="white" stroke="transparent" points="431.5,-464.5 431.5,-485.5 517.5,-485.5 517.5,-464.5 431.5,-464.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="431.5,-464 431.5,-485 517.5,-485 517.5,-464 431.5,-464"/>
|
||||
<text text-anchor="start" x="436" y="-472.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="441" y="-472.4" font-family="Roboto" font-size="12.00" fill="white">Membership</text>
|
||||
<text text-anchor="start" x="508" y="-472.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- note_models_transactions_MembershipTransaction->member_models_Membership -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>note_models_transactions_MembershipTransaction->member_models_Membership</title>
|
||||
<path fill="none" stroke="black" d="M494.59,-665.95C489.76,-620.34 480.26,-530.46 476.3,-493.01"/>
|
||||
<text text-anchor="middle" x="535.5" y="-598.1" font-family="Roboto" font-size="8.00"> membership (transaction)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 55 KiB |
171
docs/_static/img/graphs/permission.svg
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="352pt" height="373pt"
|
||||
viewBox="0.00 0.00 352.00 373.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 369)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-369 348,-369 348,4 -4,4"/>
|
||||
<!-- permission_models_PermissionMask -->
|
||||
<g id="node1" class="node">
|
||||
<title>permission_models_PermissionMask</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-4 8,-66 186,-66 186,-4 8,-4"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9,-44 9,-65 185,-65 185,-44 9,-44"/>
|
||||
<text text-anchor="start" x="41" y="-53" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="51" y="-53" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    PermissionMask    </text>
|
||||
<text text-anchor="start" x="11" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-36.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="29" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-36.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="122" y="-36.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-23.6" font-family="Roboto" font-size="8.00">description</text>
|
||||
<text text-anchor="start" x="60" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-23.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="119" y="-23.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-10.6" font-family="Roboto" font-size="8.00">rank</text>
|
||||
<text text-anchor="start" x="37" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-10.6" font-family="Roboto" font-size="8.00">PositiveSmallIntegerField</text>
|
||||
<text text-anchor="start" x="173" y="-10.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-4 8,-66 186,-66 186,-4 8,-4"/>
|
||||
</g>
|
||||
<!-- permission_models_Permission -->
|
||||
<g id="node2" class="node">
|
||||
<title>permission_models_Permission</title>
|
||||
<polygon fill="white" stroke="transparent" points="104.5,-119 104.5,-246 255.5,-246 255.5,-119 104.5,-119"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="106,-223.5 106,-244.5 255,-244.5 255,-223.5 106,-223.5"/>
|
||||
<text text-anchor="start" x="136.5" y="-232.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="146.5" y="-232.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Permission    </text>
|
||||
<text text-anchor="start" x="108" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-216.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="126" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-216.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="220" y="-216.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-203.1" font-family="Roboto" font-weight="bold" font-size="8.00">mask</text>
|
||||
<text text-anchor="start" x="139" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-203.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="243" y="-203.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-190.1" font-family="Roboto" font-weight="bold" font-size="8.00">model</text>
|
||||
<text text-anchor="start" x="143" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-190.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="243" y="-190.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-177.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">description</text>
|
||||
<text text-anchor="start" x="157" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-177.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="217" y="-177.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-164.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">field</text>
|
||||
<text text-anchor="start" x="133" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-164.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="217" y="-164.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-151.1" font-family="Roboto" font-size="8.00">permanent</text>
|
||||
<text text-anchor="start" x="158" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-151.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="228" y="-151.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-138.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-138.1" font-family="Roboto" font-size="8.00">query</text>
|
||||
<text text-anchor="start" x="139" y="-138.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-138.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-138.1" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="214" y="-138.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="108" y="-125.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="118" y="-125.1" font-family="Roboto" font-size="8.00">type</text>
|
||||
<text text-anchor="start" x="134" y="-125.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="172" y="-125.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="182" y="-125.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="217" y="-125.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="104.5,-119 104.5,-246 255.5,-246 255.5,-119 104.5,-119"/>
|
||||
</g>
|
||||
<!-- permission_models_Permission->permission_models_PermissionMask -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>permission_models_Permission->permission_models_PermissionMask</title>
|
||||
<path fill="none" stroke="black" d="M137.78,-107.48C130.35,-94.46 122.95,-81.49 116.54,-70.25"/>
|
||||
<ellipse fill="black" stroke="black" cx="139.92" cy="-111.24" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="168" y="-90.6" font-family="Roboto" font-size="8.00"> mask (permissions)</text>
|
||||
</g>
|
||||
<!-- django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="node4" class="node">
|
||||
<title>django_contrib_contenttypes_models_ContentType</title>
|
||||
<polygon fill="white" stroke="transparent" points="220,-24.5 220,-45.5 306,-45.5 306,-24.5 220,-24.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="220,-24 220,-45 306,-45 306,-24 220,-24"/>
|
||||
<text text-anchor="start" x="224" y="-32.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="229" y="-32.4" font-family="Roboto" font-size="12.00" fill="white">ContentType</text>
|
||||
<text text-anchor="start" x="297" y="-32.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- permission_models_Permission->django_contrib_contenttypes_models_ContentType -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>permission_models_Permission->django_contrib_contenttypes_models_ContentType</title>
|
||||
<path fill="none" stroke="black" d="M222.21,-107.51C233.83,-87.14 245.38,-66.89 253.2,-53.17"/>
|
||||
<ellipse fill="black" stroke="black" cx="220.08" cy="-111.24" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="249.5" y="-90.6" font-family="Roboto" font-size="8.00"> model (+)</text>
|
||||
</g>
|
||||
<!-- permission_models_Role -->
|
||||
<g id="node3" class="node">
|
||||
<title>permission_models_Role</title>
|
||||
<polygon fill="white" stroke="transparent" points="173,-299 173,-361 317,-361 317,-299 173,-299"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="174,-339 174,-360 316,-360 316,-339 174,-339"/>
|
||||
<text text-anchor="start" x="217" y="-348" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="227" y="-348" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Role    </text>
|
||||
<text text-anchor="start" x="176" y="-331.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186" y="-331.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="194" y="-331.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="233" y="-331.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="243" y="-331.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="281" y="-331.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="176" y="-318.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186" y="-318.6" font-family="Roboto" font-weight="bold" font-size="8.00">for_club</text>
|
||||
<text text-anchor="start" x="219" y="-318.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="233" y="-318.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="243" y="-318.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="304" y="-318.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="176" y="-305.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="186" y="-305.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="207" y="-305.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="233" y="-305.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="243" y="-305.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="278" y="-305.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="173,-299 173,-361 317,-361 317,-299 173,-299"/>
|
||||
</g>
|
||||
<!-- permission_models_Role->permission_models_Permission -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>permission_models_Role->permission_models_Permission</title>
|
||||
<path fill="none" stroke="black" d="M226.45,-287.47C222.27,-278.13 217.73,-267.95 213.16,-257.73"/>
|
||||
<ellipse fill="black" stroke="black" cx="228.13" cy="-291.23" rx="4" ry="4"/>
|
||||
<ellipse fill="black" stroke="black" cx="211.47" cy="-253.94" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="254" y="-270.6" font-family="Roboto" font-size="8.00"> permissions (role)</text>
|
||||
</g>
|
||||
<!-- member_models_Club -->
|
||||
<g id="node5" class="node">
|
||||
<title>member_models_Club</title>
|
||||
<polygon fill="white" stroke="transparent" points="289,-172 289,-193 333,-193 333,-172 289,-172"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="289,-171.5 289,-192.5 333,-192.5 333,-171.5 289,-171.5"/>
|
||||
<text text-anchor="start" x="293" y="-179.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="298" y="-179.9" font-family="Roboto" font-size="12.00" fill="white">Club</text>
|
||||
<text text-anchor="start" x="324" y="-179.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- permission_models_Role->member_models_Club -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>permission_models_Role->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M282.52,-288.02C284.92,-284.43 287.12,-280.73 289,-277 301.53,-252.13 307.07,-219.92 309.41,-200.53"/>
|
||||
<ellipse fill="black" stroke="black" cx="280.03" cy="-291.53" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="318" y="-270.6" font-family="Roboto" font-size="8.00"> for_club (role)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 15 KiB |
324
docs/_static/img/graphs/treasury.svg
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="970pt" height="493pt"
|
||||
viewBox="0.00 0.00 970.00 493.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 489)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-489 966,-489 966,4 -4,4"/>
|
||||
<!-- treasury_models_Invoice -->
|
||||
<g id="node1" class="node">
|
||||
<title>treasury_models_Invoice</title>
|
||||
<polygon fill="white" stroke="transparent" points="8,-187 8,-340 176,-340 176,-187 8,-187"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="9,-317.5 9,-338.5 175,-338.5 175,-317.5 9,-317.5"/>
|
||||
<text text-anchor="start" x="57.5" y="-326.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="67.5" y="-326.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Invoice    </text>
|
||||
<text text-anchor="start" x="11" y="-310.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-310.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="29" y="-310.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-310.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-310.1" font-family="Roboto" font-weight="bold" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="163" y="-310.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-297.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-297.1" font-family="Roboto" font-size="8.00">acquitted</text>
|
||||
<text text-anchor="start" x="54" y="-297.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-297.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-297.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="130" y="-297.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-284.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-284.1" font-family="Roboto" font-size="8.00">address</text>
|
||||
<text text-anchor="start" x="49" y="-284.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-284.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-284.1" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="116" y="-284.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-271.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-271.1" font-family="Roboto" font-size="8.00">bde</text>
|
||||
<text text-anchor="start" x="35" y="-271.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-271.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-271.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="119" y="-271.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-258.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-258.1" font-family="Roboto" font-size="8.00">date</text>
|
||||
<text text-anchor="start" x="37" y="-258.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-258.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-258.1" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="118" y="-258.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-245.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-245.1" font-family="Roboto" font-size="8.00">description</text>
|
||||
<text text-anchor="start" x="60" y="-245.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-245.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-245.1" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="116" y="-245.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-232.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-232.1" font-family="Roboto" font-size="8.00">locked</text>
|
||||
<text text-anchor="start" x="44" y="-232.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-232.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-232.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="130" y="-232.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-219.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-219.1" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="42" y="-219.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-219.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-219.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="119" y="-219.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-206.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-206.1" font-family="Roboto" font-size="8.00">object</text>
|
||||
<text text-anchor="start" x="43" y="-206.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-206.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-206.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="119" y="-206.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="11" y="-193.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="21" y="-193.1" font-family="Roboto" font-size="8.00">tex</text>
|
||||
<text text-anchor="start" x="32" y="-193.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="74" y="-193.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="84" y="-193.1" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="116" y="-193.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="8,-187 8,-340 176,-340 176,-187 8,-187"/>
|
||||
</g>
|
||||
<!-- treasury_models_Product -->
|
||||
<g id="node2" class="node">
|
||||
<title>treasury_models_Product</title>
|
||||
<polygon fill="white" stroke="transparent" points="11.5,-393 11.5,-481 172.5,-481 172.5,-393 11.5,-393"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="13,-459 13,-480 172,-480 172,-459 13,-459"/>
|
||||
<text text-anchor="start" x="57" y="-468" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="67" y="-468" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Product    </text>
|
||||
<text text-anchor="start" x="15" y="-451.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="25" y="-451.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="33" y="-451.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="80" y="-451.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="90" y="-451.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="128" y="-451.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="15" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="25" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">invoice</text>
|
||||
<text text-anchor="start" x="53" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="80" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="90" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="151" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="15" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="25" y="-425.6" font-family="Roboto" font-size="8.00">amount</text>
|
||||
<text text-anchor="start" x="53" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="80" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="90" y="-425.6" font-family="Roboto" font-size="8.00">IntegerField</text>
|
||||
<text text-anchor="start" x="133" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="15" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="25" y="-412.6" font-family="Roboto" font-size="8.00">designation</text>
|
||||
<text text-anchor="start" x="66" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="80" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="90" y="-412.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="125" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="15" y="-399.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="25" y="-399.6" font-family="Roboto" font-size="8.00">quantity</text>
|
||||
<text text-anchor="start" x="53" y="-399.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="80" y="-399.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="90" y="-399.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="160" y="-399.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="11.5,-393 11.5,-481 172.5,-481 172.5,-393 11.5,-393"/>
|
||||
</g>
|
||||
<!-- treasury_models_Product->treasury_models_Invoice -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>treasury_models_Product->treasury_models_Invoice</title>
|
||||
<path fill="none" stroke="black" d="M92,-380.43C92,-368.88 92,-356.48 92,-344.25"/>
|
||||
<ellipse fill="black" stroke="black" cx="92" cy="-384.68" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="124.5" y="-364.6" font-family="Roboto" font-size="8.00"> invoice (products)</text>
|
||||
</g>
|
||||
<!-- treasury_models_RemittanceType -->
|
||||
<g id="node3" class="node">
|
||||
<title>treasury_models_RemittanceType</title>
|
||||
<polygon fill="white" stroke="transparent" points="213.5,-85 213.5,-134 382.5,-134 382.5,-85 213.5,-85"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="215,-111.5 215,-132.5 382,-132.5 382,-111.5 215,-111.5"/>
|
||||
<text text-anchor="start" x="243" y="-120.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="253" y="-120.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    RemittanceType    </text>
|
||||
<text text-anchor="start" x="217" y="-104.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="227" y="-104.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="235" y="-104.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="259" y="-104.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="269" y="-104.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="307" y="-104.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="217" y="-91.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="227" y="-91.1" font-family="Roboto" font-weight="bold" font-size="8.00">note</text>
|
||||
<text text-anchor="start" x="245" y="-91.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="259" y="-91.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="269" y="-91.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (note_ptr)</text>
|
||||
<text text-anchor="start" x="370" y="-91.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="213.5,-85 213.5,-134 382.5,-134 382.5,-85 213.5,-85"/>
|
||||
</g>
|
||||
<!-- note_models_notes_NoteSpecial -->
|
||||
<g id="node7" class="node">
|
||||
<title>note_models_notes_NoteSpecial</title>
|
||||
<polygon fill="white" stroke="transparent" points="255,-7.5 255,-28.5 341,-28.5 341,-7.5 255,-7.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="255,-7 255,-28 341,-28 341,-7 255,-7"/>
|
||||
<text text-anchor="start" x="259.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="264.5" y="-15.4" font-family="Roboto" font-size="12.00" fill="white">NoteSpecial</text>
|
||||
<text text-anchor="start" x="331.5" y="-15.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- treasury_models_RemittanceType->note_models_notes_NoteSpecial -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>treasury_models_RemittanceType->note_models_notes_NoteSpecial</title>
|
||||
<path fill="none" stroke="black" d="M298,-80.67C298,-66.1 298,-48.72 298,-36.13"/>
|
||||
<text text-anchor="middle" x="337.5" y="-56.6" font-family="Roboto" font-size="8.00"> note (remittancetype)</text>
|
||||
</g>
|
||||
<!-- treasury_models_Remittance -->
|
||||
<g id="node4" class="node">
|
||||
<title>treasury_models_Remittance</title>
|
||||
<polygon fill="white" stroke="transparent" points="210.5,-219.5 210.5,-307.5 385.5,-307.5 385.5,-219.5 210.5,-219.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="212,-285.5 212,-306.5 385,-306.5 385,-285.5 212,-285.5"/>
|
||||
<text text-anchor="start" x="254" y="-294.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="264" y="-294.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Remittance    </text>
|
||||
<text text-anchor="start" x="214" y="-278.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="224" y="-278.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="232" y="-278.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="302" y="-278.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="312" y="-278.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="350" y="-278.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214" y="-265.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="224" y="-265.1" font-family="Roboto" font-weight="bold" font-size="8.00">remittance_type</text>
|
||||
<text text-anchor="start" x="288" y="-265.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="302" y="-265.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="312" y="-265.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="373" y="-265.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214" y="-252.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="224" y="-252.1" font-family="Roboto" font-size="8.00">closed</text>
|
||||
<text text-anchor="start" x="247" y="-252.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="302" y="-252.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="312" y="-252.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="358" y="-252.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214" y="-239.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="224" y="-239.1" font-family="Roboto" font-size="8.00">comment</text>
|
||||
<text text-anchor="start" x="258" y="-239.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="302" y="-239.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="312" y="-239.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="347" y="-239.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="214" y="-226.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="224" y="-226.1" font-family="Roboto" font-size="8.00">date</text>
|
||||
<text text-anchor="start" x="240" y="-226.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="302" y="-226.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="312" y="-226.1" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="364" y="-226.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="210.5,-219.5 210.5,-307.5 385.5,-307.5 385.5,-219.5 210.5,-219.5"/>
|
||||
</g>
|
||||
<!-- treasury_models_Remittance->treasury_models_RemittanceType -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>treasury_models_Remittance->treasury_models_RemittanceType</title>
|
||||
<path fill="none" stroke="black" d="M298,-207.09C298,-183.68 298,-157.46 298,-138.24"/>
|
||||
<ellipse fill="black" stroke="black" cx="298" cy="-211.24" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="350" y="-158.6" font-family="Roboto" font-size="8.00"> remittance_type (remittance)</text>
|
||||
</g>
|
||||
<!-- treasury_models_SpecialTransactionProxy -->
|
||||
<g id="node5" class="node">
|
||||
<title>treasury_models_SpecialTransactionProxy</title>
|
||||
<polygon fill="white" stroke="transparent" points="272.5,-406 272.5,-468 497.5,-468 497.5,-406 272.5,-406"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="274,-446 274,-467 497,-467 497,-446 274,-446"/>
|
||||
<text text-anchor="start" x="310" y="-455" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="320" y="-455" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    SpecialTransactionProxy    </text>
|
||||
<text text-anchor="start" x="276" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="286" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="294" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="346" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="356" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="394" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="276" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="286" y="-425.6" font-family="Roboto" font-weight="bold" font-size="8.00">remittance</text>
|
||||
<text text-anchor="start" x="328" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="346" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="356" y="-425.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="417" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="276" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="286" y="-412.6" font-family="Roboto" font-weight="bold" font-size="8.00">transaction</text>
|
||||
<text text-anchor="start" x="332" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="346" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="356" y="-412.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (transaction_ptr)</text>
|
||||
<text text-anchor="start" x="485" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="272.5,-406 272.5,-468 497.5,-468 497.5,-406 272.5,-406"/>
|
||||
</g>
|
||||
<!-- treasury_models_SpecialTransactionProxy->treasury_models_Remittance -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>treasury_models_SpecialTransactionProxy->treasury_models_Remittance</title>
|
||||
<path fill="none" stroke="black" d="M318.41,-396.34C310.62,-388.91 303.76,-380.46 299,-371 289.94,-353 288.41,-331.02 289.64,-311.71"/>
|
||||
<ellipse fill="black" stroke="black" cx="321.56" cy="-399.15" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="364" y="-364.6" font-family="Roboto" font-size="8.00"> remittance (specialtransactionproxy)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_SpecialTransaction -->
|
||||
<g id="node8" class="node">
|
||||
<title>note_models_transactions_SpecialTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="483,-253 483,-274 605,-274 605,-253 483,-253"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="483,-252.5 483,-273.5 605,-273.5 605,-252.5 483,-252.5"/>
|
||||
<text text-anchor="start" x="487.5" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="492.5" y="-260.9" font-family="Roboto" font-size="12.00" fill="white">SpecialTransaction</text>
|
||||
<text text-anchor="start" x="595.5" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- treasury_models_SpecialTransactionProxy->note_models_transactions_SpecialTransaction -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>treasury_models_SpecialTransactionProxy->note_models_transactions_SpecialTransaction</title>
|
||||
<path fill="none" stroke="black" d="M415.54,-401.97C426.93,-389.37 440,-374.98 452,-362 478.22,-333.63 509.12,-301.06 527.61,-281.66"/>
|
||||
<text text-anchor="middle" x="518" y="-364.6" font-family="Roboto" font-size="8.00"> transaction (specialtransactionproxy)</text>
|
||||
</g>
|
||||
<!-- treasury_models_SogeCredit -->
|
||||
<g id="node6" class="node">
|
||||
<title>treasury_models_SogeCredit</title>
|
||||
<polygon fill="white" stroke="transparent" points="612,-406 612,-468 864,-468 864,-406 612,-406"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="613,-446 613,-467 863,-467 863,-446 613,-446"/>
|
||||
<text text-anchor="start" x="694.5" y="-455" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="704.5" y="-455" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    SogeCredit    </text>
|
||||
<text text-anchor="start" x="615" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="625" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="633" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="712" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="722" y="-438.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="760" y="-438.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="615" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="625" y="-425.6" font-family="Roboto" font-weight="bold" font-size="8.00">credit_transaction</text>
|
||||
<text text-anchor="start" x="698" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="712" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="722" y="-425.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (transaction_ptr)</text>
|
||||
<text text-anchor="start" x="851" y="-425.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="615" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="625" y="-412.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="643" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="712" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="722" y="-412.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="798" y="-412.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="612,-406 612,-468 864,-468 864,-406 612,-406"/>
|
||||
</g>
|
||||
<!-- treasury_models_SogeCredit->note_models_transactions_SpecialTransaction -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>treasury_models_SogeCredit->note_models_transactions_SpecialTransaction</title>
|
||||
<path fill="none" stroke="black" d="M668.71,-401.98C653.52,-392.97 638.09,-382.51 625,-371 594.47,-344.16 567.83,-304.24 554.03,-281.68"/>
|
||||
<text text-anchor="middle" x="679.5" y="-364.6" font-family="Roboto" font-size="8.00"> credit_transaction (sogecredit)</text>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node9" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="716,-253 716,-274 760,-274 760,-253 716,-253"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="716,-252.5 716,-273.5 760,-273.5 760,-252.5 716,-252.5"/>
|
||||
<text text-anchor="start" x="720" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="725" y="-260.9" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="751" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- treasury_models_SogeCredit->django_contrib_auth_models_User -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>treasury_models_SogeCredit->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M738,-401.71C738,-365.34 738,-309.39 738,-281.51"/>
|
||||
<text text-anchor="middle" x="769.5" y="-364.6" font-family="Roboto" font-size="8.00"> user (sogecredit)</text>
|
||||
</g>
|
||||
<!-- note_models_transactions_MembershipTransaction -->
|
||||
<g id="node10" class="node">
|
||||
<title>note_models_transactions_MembershipTransaction</title>
|
||||
<polygon fill="white" stroke="transparent" points="794,-253 794,-274 940,-274 940,-253 794,-253"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="794,-252.5 794,-273.5 940,-273.5 940,-252.5 794,-252.5"/>
|
||||
<text text-anchor="start" x="798" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="803" y="-260.9" font-family="Roboto" font-size="12.00" fill="white">MembershipTransaction</text>
|
||||
<text text-anchor="start" x="931" y="-260.9" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- treasury_models_SogeCredit->note_models_transactions_MembershipTransaction -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>treasury_models_SogeCredit->note_models_transactions_MembershipTransaction</title>
|
||||
<path fill="none" stroke="black" d="M782.72,-395.99C790.55,-388.06 798.32,-379.54 805,-371 825.4,-344.92 843.79,-311.46 855.15,-289.06"/>
|
||||
<ellipse fill="black" stroke="black" cx="779.57" cy="-399.11" rx="4" ry="4"/>
|
||||
<ellipse fill="black" stroke="black" cx="857.08" cy="-285.21" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="886.5" y="-364.6" font-family="Roboto" font-size="8.00"> transactions (_sogecredit_transactions_+)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 29 KiB |
603
docs/_static/img/graphs/wei.svg
vendored
Normal file
@ -0,0 +1,603 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.44.1 (0)
|
||||
-->
|
||||
<!-- Title: model_graph Pages: 1 -->
|
||||
<svg width="1207pt" height="864pt"
|
||||
viewBox="0.00 0.00 1206.76 864.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 860)">
|
||||
<title>model_graph</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-860 1202.76,-860 1202.76,4 -4,4"/>
|
||||
<!-- wei_models_WEIClub -->
|
||||
<g id="node1" class="node">
|
||||
<title>wei_models_WEIClub</title>
|
||||
<polygon fill="white" stroke="transparent" points="357.76,-219 357.76,-294 520.76,-294 520.76,-219 357.76,-219"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="359.26,-271.5 359.26,-292.5 520.26,-292.5 520.26,-271.5 359.26,-271.5"/>
|
||||
<text text-anchor="start" x="401.76" y="-280.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="411.76" y="-280.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    WEIClub    </text>
|
||||
<text text-anchor="start" x="361.26" y="-264.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="371.26" y="-264.1" font-family="Roboto" font-weight="bold" font-size="8.00">club_ptr</text>
|
||||
<text text-anchor="start" x="404.26" y="-264.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="422.26" y="-264.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="432.26" y="-264.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="508.26" y="-264.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="361.26" y="-251.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="371.26" y="-251.1" font-family="Roboto" font-size="8.00">date_end</text>
|
||||
<text text-anchor="start" x="405.26" y="-251.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="422.26" y="-251.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="432.26" y="-251.1" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="466.26" y="-251.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="361.26" y="-238.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="371.26" y="-238.1" font-family="Roboto" font-size="8.00">date_start</text>
|
||||
<text text-anchor="start" x="408.26" y="-238.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="422.26" y="-238.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="432.26" y="-238.1" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="466.26" y="-238.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="361.26" y="-225.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="371.26" y="-225.1" font-family="Roboto" font-size="8.00">year</text>
|
||||
<text text-anchor="start" x="387.26" y="-225.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="422.26" y="-225.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="432.26" y="-225.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="502.26" y="-225.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="357.76,-219 357.76,-294 520.76,-294 520.76,-219 357.76,-219"/>
|
||||
</g>
|
||||
<!-- member_models_Club -->
|
||||
<g id="node8" class="node">
|
||||
<title>member_models_Club</title>
|
||||
<polygon fill="white" stroke="transparent" points="554.76,-4 554.76,-157 763.76,-157 763.76,-4 554.76,-4"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="556.26,-134.5 556.26,-155.5 763.26,-155.5 763.26,-134.5 556.26,-134.5"/>
|
||||
<text text-anchor="start" x="631.26" y="-143.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="641.26" y="-143.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Club    </text>
|
||||
<text text-anchor="start" x="558.26" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-127.1" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="576.26" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-127.1" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="719.26" y="-127.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-114.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">parent_club</text>
|
||||
<text text-anchor="start" x="616.26" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-114.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="742.26" y="-114.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-101.1" font-family="Roboto" font-size="8.00">email</text>
|
||||
<text text-anchor="start" x="587.26" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-101.1" font-family="Roboto" font-size="8.00">EmailField</text>
|
||||
<text text-anchor="start" x="718.26" y="-101.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-88.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_duration</text>
|
||||
<text text-anchor="start" x="647.26" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-88.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="751.26" y="-88.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-75.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_end</text>
|
||||
<text text-anchor="start" x="631.26" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-75.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">DateField</text>
|
||||
<text text-anchor="start" x="715.26" y="-75.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-62.1" font-family="Roboto" font-size="8.00">membership_fee_paid</text>
|
||||
<text text-anchor="start" x="648.26" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-62.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="751.26" y="-62.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-49.1" font-family="Roboto" font-size="8.00">membership_fee_unpaid</text>
|
||||
<text text-anchor="start" x="657.26" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-49.1" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="751.26" y="-49.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-36.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">membership_start</text>
|
||||
<text text-anchor="start" x="633.26" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-36.1" font-family="Roboto" font-size="8.00" fill="#7b7b7b">DateField</text>
|
||||
<text text-anchor="start" x="715.26" y="-36.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-23.1" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="589.26" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-23.1" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="716.26" y="-23.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="558.26" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="568.26" y="-10.1" font-family="Roboto" font-size="8.00">require_memberships</text>
|
||||
<text text-anchor="start" x="645.26" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="671.26" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="681.26" y="-10.1" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="727.26" y="-10.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="554.76,-4 554.76,-157 763.76,-157 763.76,-4 554.76,-4"/>
|
||||
</g>
|
||||
<!-- wei_models_WEIClub->member_models_Club -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>wei_models_WEIClub->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M490.56,-214.92C508.57,-200.68 529.56,-184.08 550.48,-167.53"/>
|
||||
<polygon fill="none" stroke="black" points="552.82,-170.14 558.49,-161.2 548.48,-164.65 552.82,-170.14"/>
|
||||
<text text-anchor="middle" x="550.26" y="-190.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="550.26" y="-181.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- wei_models_Bus -->
|
||||
<g id="node2" class="node">
|
||||
<title>wei_models_Bus</title>
|
||||
<polygon fill="white" stroke="transparent" points="112.26,-347 112.26,-435 306.26,-435 306.26,-347 112.26,-347"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="113.26,-413 113.26,-434 305.26,-434 305.26,-413 113.26,-413"/>
|
||||
<text text-anchor="start" x="182.76" y="-422" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="192.76" y="-422" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Bus    </text>
|
||||
<text text-anchor="start" x="115.26" y="-405.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.26" y="-405.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="133.26" y="-405.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="198.26" y="-405.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="208.26" y="-405.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="246.26" y="-405.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.26" y="-392.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.26" y="-392.6" font-family="Roboto" font-weight="bold" font-size="8.00">wei</text>
|
||||
<text text-anchor="start" x="138.26" y="-392.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="198.26" y="-392.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="208.26" y="-392.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (club_ptr)</text>
|
||||
<text text-anchor="start" x="293.26" y="-392.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.26" y="-379.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.26" y="-379.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">description</text>
|
||||
<text text-anchor="start" x="164.26" y="-379.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="198.26" y="-379.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="208.26" y="-379.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">TextField</text>
|
||||
<text text-anchor="start" x="240.26" y="-379.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.26" y="-366.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.26" y="-366.6" font-family="Roboto" font-size="8.00">information_json</text>
|
||||
<text text-anchor="start" x="184.26" y="-366.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="198.26" y="-366.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="208.26" y="-366.6" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="240.26" y="-366.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="115.26" y="-353.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="125.26" y="-353.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="146.26" y="-353.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="198.26" y="-353.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="208.26" y="-353.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="243.26" y="-353.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="112.26,-347 112.26,-435 306.26,-435 306.26,-347 112.26,-347"/>
|
||||
</g>
|
||||
<!-- wei_models_Bus->wei_models_WEIClub -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>wei_models_Bus->wei_models_WEIClub</title>
|
||||
<path fill="none" stroke="black" d="M298.21,-338.76C321.62,-325.27 346.55,-310.91 368.84,-298.07"/>
|
||||
<ellipse fill="black" stroke="black" cx="294.46" cy="-340.92" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="353.26" y="-318.6" font-family="Roboto" font-size="8.00"> wei (buses)</text>
|
||||
</g>
|
||||
<!-- wei_models_BusTeam -->
|
||||
<g id="node3" class="node">
|
||||
<title>wei_models_BusTeam</title>
|
||||
<polygon fill="white" stroke="transparent" points="129.76,-562 129.76,-650 288.76,-650 288.76,-562 129.76,-562"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="131.26,-628 131.26,-649 288.26,-649 288.26,-628 131.26,-628"/>
|
||||
<text text-anchor="start" x="170.76" y="-637" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="180.76" y="-637" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    BusTeam    </text>
|
||||
<text text-anchor="start" x="133.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="143.26" y="-620.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="151.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="206.26" y="-620.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="244.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="133.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="143.26" y="-607.6" font-family="Roboto" font-weight="bold" font-size="8.00">bus</text>
|
||||
<text text-anchor="start" x="159.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="206.26" y="-607.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="267.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="133.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="143.26" y="-594.6" font-family="Roboto" font-size="8.00">color</text>
|
||||
<text text-anchor="start" x="161.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="206.26" y="-594.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="276.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="133.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="143.26" y="-581.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">description</text>
|
||||
<text text-anchor="start" x="182.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="206.26" y="-581.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">TextField</text>
|
||||
<text text-anchor="start" x="238.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="133.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="143.26" y="-568.6" font-family="Roboto" font-size="8.00">name</text>
|
||||
<text text-anchor="start" x="164.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="196.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="206.26" y="-568.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="241.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="129.76,-562 129.76,-650 288.76,-650 288.76,-562 129.76,-562"/>
|
||||
</g>
|
||||
<!-- wei_models_BusTeam->wei_models_Bus -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>wei_models_BusTeam->wei_models_Bus</title>
|
||||
<path fill="none" stroke="black" d="M209.26,-549.93C209.26,-515.64 209.26,-471.98 209.26,-439.26"/>
|
||||
<ellipse fill="black" stroke="black" cx="209.26" cy="-553.99" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="232.26" y="-464.1" font-family="Roboto" font-size="8.00"> bus (teams)</text>
|
||||
</g>
|
||||
<!-- wei_models_WEIRole -->
|
||||
<g id="node4" class="node">
|
||||
<title>wei_models_WEIRole</title>
|
||||
<polygon fill="white" stroke="transparent" points="589.76,-588 589.76,-624 746.76,-624 746.76,-588 589.76,-588"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="591.26,-602 591.26,-623 746.26,-623 746.26,-602 591.26,-602"/>
|
||||
<text text-anchor="start" x="631.26" y="-611" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="641.26" y="-611" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    WEIRole    </text>
|
||||
<text text-anchor="start" x="593.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="603.26" y="-594.6" font-family="Roboto" font-weight="bold" font-size="8.00">role_ptr</text>
|
||||
<text text-anchor="start" x="634.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="648.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="658.26" y="-594.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="734.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="589.76,-588 589.76,-624 746.76,-624 746.76,-588 589.76,-588"/>
|
||||
</g>
|
||||
<!-- permission_models_Role -->
|
||||
<g id="node10" class="node">
|
||||
<title>permission_models_Role</title>
|
||||
<polygon fill="white" stroke="transparent" points="667.26,-380.5 667.26,-401.5 711.26,-401.5 711.26,-380.5 667.26,-380.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="667.26,-380 667.26,-401 711.26,-401 711.26,-380 667.26,-380"/>
|
||||
<text text-anchor="start" x="671.76" y="-388.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="676.76" y="-388.4" font-family="Roboto" font-size="12.00" fill="white">Role</text>
|
||||
<text text-anchor="start" x="701.76" y="-388.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- wei_models_WEIRole->permission_models_Role -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>wei_models_WEIRole->permission_models_Role</title>
|
||||
<path fill="none" stroke="black" d="M663.01,-583.63C656.73,-554.58 648.26,-501.21 658.26,-457 661.3,-443.57 667.46,-429.66 673.5,-418.17"/>
|
||||
<polygon fill="none" stroke="black" points="676.71,-419.6 678.47,-409.16 670.57,-416.23 676.71,-419.6"/>
|
||||
<text text-anchor="middle" x="678.26" y="-468.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="678.26" y="-459.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- wei_models_WEIRegistration -->
|
||||
<g id="node5" class="node">
|
||||
<title>wei_models_WEIRegistration</title>
|
||||
<polygon fill="white" stroke="transparent" points="323.26,-503.5 323.26,-708.5 555.26,-708.5 555.26,-503.5 323.26,-503.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="324.26,-686 324.26,-707 554.26,-707 554.26,-686 324.26,-686"/>
|
||||
<text text-anchor="start" x="384.26" y="-695" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="394.26" y="-695" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    WEIRegistration    </text>
|
||||
<text text-anchor="start" x="326.26" y="-678.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-678.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="344.26" y="-678.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-678.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-678.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="495.26" y="-678.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-665.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-665.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="354.26" y="-665.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-665.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-665.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="518.26" y="-665.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-652.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-652.6" font-family="Roboto" font-weight="bold" font-size="8.00">wei</text>
|
||||
<text text-anchor="start" x="349.26" y="-652.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-652.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-652.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (club_ptr)</text>
|
||||
<text text-anchor="start" x="542.26" y="-652.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-639.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-639.6" font-family="Roboto" font-size="8.00">birth_date</text>
|
||||
<text text-anchor="start" x="373.26" y="-639.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-639.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-639.6" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="491.26" y="-639.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-626.6" font-family="Roboto" font-size="8.00">caution_check</text>
|
||||
<text text-anchor="start" x="387.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-626.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="503.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-613.6" font-family="Roboto" font-size="8.00">clothing_cut</text>
|
||||
<text text-anchor="start" x="379.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-613.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="492.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-600.6" font-family="Roboto" font-size="8.00">clothing_size</text>
|
||||
<text text-anchor="start" x="382.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-600.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="492.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-587.6" font-family="Roboto" font-size="8.00">emergency_contact_name</text>
|
||||
<text text-anchor="start" x="431.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-587.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="492.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-574.6" font-family="Roboto" font-size="8.00">emergency_contact_phone</text>
|
||||
<text text-anchor="start" x="433.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-574.6" font-family="Roboto" font-size="8.00">PhoneNumberField</text>
|
||||
<text text-anchor="start" x="527.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-561.6" font-family="Roboto" font-size="8.00">first_year</text>
|
||||
<text text-anchor="start" x="370.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-561.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="503.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-548.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-548.6" font-family="Roboto" font-size="8.00">gender</text>
|
||||
<text text-anchor="start" x="362.26" y="-548.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-548.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-548.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="492.26" y="-548.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-535.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-535.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">health_issues</text>
|
||||
<text text-anchor="start" x="385.26" y="-535.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-535.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-535.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">TextField</text>
|
||||
<text text-anchor="start" x="489.26" y="-535.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-522.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-522.6" font-family="Roboto" font-size="8.00">information_json</text>
|
||||
<text text-anchor="start" x="395.26" y="-522.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-522.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-522.6" font-family="Roboto" font-size="8.00">TextField</text>
|
||||
<text text-anchor="start" x="489.26" y="-522.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="326.26" y="-509.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="336.26" y="-509.6" font-family="Roboto" font-size="8.00">soge_credit</text>
|
||||
<text text-anchor="start" x="377.26" y="-509.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="447.26" y="-509.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="457.26" y="-509.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="503.26" y="-509.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="323.26,-503.5 323.26,-708.5 555.26,-708.5 555.26,-503.5 323.26,-503.5"/>
|
||||
</g>
|
||||
<!-- wei_models_WEIRegistration->wei_models_WEIClub -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>wei_models_WEIRegistration->wei_models_WEIClub</title>
|
||||
<path fill="none" stroke="black" d="M439.26,-491.29C439.26,-424.89 439.26,-345.15 439.26,-298.05"/>
|
||||
<ellipse fill="black" stroke="black" cx="439.26" cy="-495.32" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="460.76" y="-389.1" font-family="Roboto" font-size="8.00"> wei (users)</text>
|
||||
</g>
|
||||
<!-- django_contrib_auth_models_User -->
|
||||
<g id="node11" class="node">
|
||||
<title>django_contrib_auth_models_User</title>
|
||||
<polygon fill="white" stroke="transparent" points="905.26,-380.5 905.26,-401.5 949.26,-401.5 949.26,-380.5 905.26,-380.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="905.26,-380 905.26,-401 949.26,-401 949.26,-380 905.26,-380"/>
|
||||
<text text-anchor="start" x="909.26" y="-388.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
<text text-anchor="start" x="914.26" y="-388.4" font-family="Roboto" font-size="12.00" fill="white">User</text>
|
||||
<text text-anchor="start" x="940.26" y="-388.4" font-family="Roboto" font-size="8.00">  </text>
|
||||
</g>
|
||||
<!-- wei_models_WEIRegistration->django_contrib_auth_models_User -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>wei_models_WEIRegistration->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M565.81,-495.81C567.95,-494.83 570.1,-493.89 572.26,-493 663.38,-455.48 699.65,-505.8 793.26,-475 837.19,-460.54 881.52,-429 906.57,-409.23"/>
|
||||
<ellipse fill="black" stroke="black" cx="562.18" cy="-497.57" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="852.76" y="-464.1" font-family="Roboto" font-size="8.00"> user (wei)</text>
|
||||
</g>
|
||||
<!-- wei_models_WEIMembership -->
|
||||
<g id="node6" class="node">
|
||||
<title>wei_models_WEIMembership</title>
|
||||
<polygon fill="white" stroke="transparent" points="196.26,-777 196.26,-852 386.26,-852 386.26,-777 196.26,-777"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="197.26,-829.5 197.26,-850.5 385.26,-850.5 385.26,-829.5 197.26,-829.5"/>
|
||||
<text text-anchor="start" x="235.76" y="-838.5" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="245.76" y="-838.5" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    WEIMembership    </text>
|
||||
<text text-anchor="start" x="199.26" y="-822.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="209.26" y="-822.1" font-family="Roboto" font-weight="bold" font-size="8.00">membership_ptr</text>
|
||||
<text text-anchor="start" x="273.26" y="-822.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287.26" y="-822.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297.26" y="-822.1" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="373.26" y="-822.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="199.26" y="-809.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="209.26" y="-809.1" font-family="Roboto" font-weight="bold" font-size="8.00">bus</text>
|
||||
<text text-anchor="start" x="225.26" y="-809.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287.26" y="-809.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297.26" y="-809.1" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="358.26" y="-809.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="199.26" y="-796.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="209.26" y="-796.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">registration</text>
|
||||
<text text-anchor="start" x="255.26" y="-796.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287.26" y="-796.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297.26" y="-796.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="373.26" y="-796.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="199.26" y="-783.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="209.26" y="-783.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">team</text>
|
||||
<text text-anchor="start" x="228.26" y="-783.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="287.26" y="-783.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="297.26" y="-783.1" font-family="Roboto" font-weight="bold" font-size="8.00" fill="#7b7b7b">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="358.26" y="-783.1" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="196.26,-777 196.26,-852 386.26,-852 386.26,-777 196.26,-777"/>
|
||||
</g>
|
||||
<!-- wei_models_WEIMembership->wei_models_Bus -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>wei_models_WEIMembership->wei_models_Bus</title>
|
||||
<path fill="none" stroke="black" d="M180.12,-800.54C128.6,-788.33 71.89,-764.71 40.26,-719 -16.9,-636.4 -9.31,-580.36 40.26,-493 54.76,-467.43 78.6,-447.65 103.85,-432.64"/>
|
||||
<ellipse fill="black" stroke="black" cx="184.11" cy="-801.44" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="76.26" y="-604.1" font-family="Roboto" font-size="8.00"> bus (memberships)</text>
|
||||
</g>
|
||||
<!-- wei_models_WEIMembership->wei_models_BusTeam -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>wei_models_WEIMembership->wei_models_BusTeam</title>
|
||||
<path fill="none" stroke="black" d="M271.95,-764.88C258.68,-731.47 241.16,-687.33 228.01,-654.22"/>
|
||||
<ellipse fill="black" stroke="black" cx="273.58" cy="-768.98" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="305.76" y="-744.1" font-family="Roboto" font-size="8.00"> team (memberships)</text>
|
||||
</g>
|
||||
<!-- wei_models_WEIMembership->wei_models_WEIRegistration -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>wei_models_WEIMembership->wei_models_WEIRegistration</title>
|
||||
<path fill="none" stroke="black" d="M332.93,-772.99C338.3,-767.14 343.55,-761.04 348.26,-755 358.55,-741.79 368.65,-727.36 378.17,-712.85"/>
|
||||
<text text-anchor="middle" x="407.26" y="-744.1" font-family="Roboto" font-size="8.00"> registration (membership)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership -->
|
||||
<g id="node9" class="node">
|
||||
<title>member_models_Membership</title>
|
||||
<polygon fill="white" stroke="transparent" points="780.76,-555.5 780.76,-656.5 937.76,-656.5 937.76,-555.5 780.76,-555.5"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="782.26,-634 782.26,-655 937.26,-655 937.26,-634 782.26,-634"/>
|
||||
<text text-anchor="start" x="813.76" y="-643" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="823.76" y="-643" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Membership    </text>
|
||||
<text text-anchor="start" x="784.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-626.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="802.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-626.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="893.26" y="-626.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="784.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-613.6" font-family="Roboto" font-weight="bold" font-size="8.00">club</text>
|
||||
<text text-anchor="start" x="812.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-613.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="916.26" y="-613.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="784.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-600.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="812.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-600.6" font-family="Roboto" font-weight="bold" font-size="8.00">ForeignKey (id)</text>
|
||||
<text text-anchor="start" x="916.26" y="-600.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="784.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-587.6" font-family="Roboto" font-size="8.00">date_end</text>
|
||||
<text text-anchor="start" x="828.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-587.6" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="889.26" y="-587.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="784.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-574.6" font-family="Roboto" font-size="8.00">date_start</text>
|
||||
<text text-anchor="start" x="831.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-574.6" font-family="Roboto" font-size="8.00">DateField</text>
|
||||
<text text-anchor="start" x="889.26" y="-574.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="784.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="794.26" y="-561.6" font-family="Roboto" font-size="8.00">fee</text>
|
||||
<text text-anchor="start" x="806.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="845.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="855.26" y="-561.6" font-family="Roboto" font-size="8.00">PositiveIntegerField</text>
|
||||
<text text-anchor="start" x="925.26" y="-561.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="780.76,-555.5 780.76,-656.5 937.76,-656.5 937.76,-555.5 780.76,-555.5"/>
|
||||
</g>
|
||||
<!-- wei_models_WEIMembership->member_models_Membership -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>wei_models_WEIMembership->member_models_Membership</title>
|
||||
<path fill="none" stroke="black" d="M394.29,-811.98C494.78,-806.29 648.78,-786.06 763.26,-719 784.62,-706.48 803.55,-687.45 818.82,-668.6"/>
|
||||
<polygon fill="none" stroke="black" points="821.72,-670.58 825.14,-660.55 816.21,-666.25 821.72,-670.58"/>
|
||||
<text text-anchor="middle" x="749.26" y="-748.6" font-family="Roboto" font-size="8.00"> multi-table</text>
|
||||
<text text-anchor="middle" x="749.26" y="-739.6" font-family="Roboto" font-size="8.00">inheritance</text>
|
||||
</g>
|
||||
<!-- member_models_Profile -->
|
||||
<g id="node7" class="node">
|
||||
<title>member_models_Profile</title>
|
||||
<polygon fill="white" stroke="transparent" points="971.76,-497 971.76,-715 1190.76,-715 1190.76,-497 971.76,-497"/>
|
||||
<polygon fill="#1b563f" stroke="transparent" points="973.26,-693 973.26,-714 1190.26,-714 1190.26,-693 973.26,-693"/>
|
||||
<text text-anchor="start" x="1049.26" y="-702" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1059.26" y="-702" font-family="Roboto" font-weight="bold" font-size="10.00" fill="white">    Profile    </text>
|
||||
<text text-anchor="start" x="975.26" y="-685.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-685.6" font-family="Roboto" font-weight="bold" font-size="8.00">id</text>
|
||||
<text text-anchor="start" x="993.26" y="-685.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-685.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-685.6" font-family="Roboto" font-weight="bold" font-size="8.00">AutoField</text>
|
||||
<text text-anchor="start" x="1127.26" y="-685.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-672.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-672.6" font-family="Roboto" font-weight="bold" font-size="8.00">user</text>
|
||||
<text text-anchor="start" x="1003.26" y="-672.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-672.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-672.6" font-family="Roboto" font-weight="bold" font-size="8.00">OneToOneField (id)</text>
|
||||
<text text-anchor="start" x="1165.26" y="-672.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-659.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-659.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">address</text>
|
||||
<text text-anchor="start" x="1013.26" y="-659.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-659.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-659.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="1124.26" y="-659.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-646.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-646.6" font-family="Roboto" font-size="8.00">department</text>
|
||||
<text text-anchor="start" x="1027.26" y="-646.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-646.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-646.6" font-family="Roboto" font-size="8.00">CharField</text>
|
||||
<text text-anchor="start" x="1124.26" y="-646.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-633.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-633.6" font-family="Roboto" font-size="8.00">email_confirmed</text>
|
||||
<text text-anchor="start" x="1044.26" y="-633.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-633.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-633.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="1135.26" y="-633.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-620.6" font-family="Roboto" font-size="8.00">last_report</text>
|
||||
<text text-anchor="start" x="1023.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-620.6" font-family="Roboto" font-size="8.00">DateTimeField</text>
|
||||
<text text-anchor="start" x="1141.26" y="-620.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-607.6" font-family="Roboto" font-size="8.00">ml_art_registration</text>
|
||||
<text text-anchor="start" x="1052.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-607.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="1135.26" y="-607.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-594.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">ml_events_registration</text>
|
||||
<text text-anchor="start" x="1065.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-594.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="1124.26" y="-594.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-581.6" font-family="Roboto" font-size="8.00">ml_sport_registration</text>
|
||||
<text text-anchor="start" x="1060.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-581.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="1135.26" y="-581.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-568.6" font-family="Roboto" font-size="8.00">paid</text>
|
||||
<text text-anchor="start" x="1001.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-568.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="1135.26" y="-568.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-555.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-555.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">phone_number</text>
|
||||
<text text-anchor="start" x="1040.26" y="-555.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-555.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-555.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">PhoneNumberField</text>
|
||||
<text text-anchor="start" x="1159.26" y="-555.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-542.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-542.6" font-family="Roboto" font-size="8.00">promotion</text>
|
||||
<text text-anchor="start" x="1021.26" y="-542.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-542.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-542.6" font-family="Roboto" font-size="8.00">PositiveSmallIntegerField</text>
|
||||
<text text-anchor="start" x="1178.26" y="-542.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-529.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-529.6" font-family="Roboto" font-size="8.00">registration_valid</text>
|
||||
<text text-anchor="start" x="1045.26" y="-529.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-529.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-529.6" font-family="Roboto" font-size="8.00">BooleanField</text>
|
||||
<text text-anchor="start" x="1135.26" y="-529.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-516.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-516.6" font-family="Roboto" font-size="8.00">report_frequency</text>
|
||||
<text text-anchor="start" x="1046.26" y="-516.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-516.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-516.6" font-family="Roboto" font-size="8.00">PositiveSmallIntegerField</text>
|
||||
<text text-anchor="start" x="1178.26" y="-516.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="975.26" y="-503.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="985.26" y="-503.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">section</text>
|
||||
<text text-anchor="start" x="1010.26" y="-503.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1079.26" y="-503.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<text text-anchor="start" x="1089.26" y="-503.6" font-family="Roboto" font-size="8.00" fill="#7b7b7b">CharField</text>
|
||||
<text text-anchor="start" x="1124.26" y="-503.6" font-family="Roboto" font-size="8.00">    </text>
|
||||
<polygon fill="none" stroke="black" points="971.76,-497 971.76,-715 1190.76,-715 1190.76,-497 971.76,-497"/>
|
||||
</g>
|
||||
<!-- member_models_Profile->django_contrib_auth_models_User -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>member_models_Profile->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M1010.97,-492.62C1002.57,-480.41 993.9,-468.32 985.26,-457 972.23,-439.93 955.81,-421.8 943.77,-409.03"/>
|
||||
<text text-anchor="middle" x="1020.26" y="-464.1" font-family="Roboto" font-size="8.00"> user (profile)</text>
|
||||
</g>
|
||||
<!-- member_models_Club->member_models_Club -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>member_models_Club->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M779.77,-93.53C786.05,-90.32 789.76,-85.97 789.76,-80.5 789.76,-73.05 782.88,-67.69 772.02,-64.43"/>
|
||||
<ellipse fill="black" stroke="black" cx="775.74" cy="-95.11" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="823.26" y="-78.6" font-family="Roboto" font-size="8.00"> parent_club (club)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership->member_models_Club -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>member_models_Membership->member_models_Club</title>
|
||||
<path fill="none" stroke="black" d="M863.76,-543.26C864.15,-516.37 862.46,-484.73 855.26,-457 826.89,-347.74 762.07,-234.51 714.72,-161.34"/>
|
||||
<ellipse fill="black" stroke="black" cx="863.67" cy="-547.45" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="841.26" y="-318.6" font-family="Roboto" font-size="8.00"> club (membership)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership->permission_models_Role -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>member_models_Membership->permission_models_Role</title>
|
||||
<path fill="none" stroke="black" d="M816.49,-545.05C801.23,-526.63 782.92,-507.44 763.26,-493 746.89,-480.98 735.35,-489.62 721.26,-475 705.92,-459.09 697.73,-435.03 693.49,-416.91"/>
|
||||
<ellipse fill="black" stroke="black" cx="819.01" cy="-548.15" rx="4" ry="4"/>
|
||||
<ellipse fill="black" stroke="black" cx="692.65" cy="-412.96" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="757.26" y="-464.1" font-family="Roboto" font-size="8.00"> roles (membership)</text>
|
||||
</g>
|
||||
<!-- member_models_Membership->django_contrib_auth_models_User -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>member_models_Membership->django_contrib_auth_models_User</title>
|
||||
<path fill="none" stroke="black" d="M878.91,-543.46C893.51,-497.7 912.45,-438.39 921.69,-409.44"/>
|
||||
<ellipse fill="black" stroke="black" cx="877.63" cy="-547.44" rx="4" ry="4"/>
|
||||
<text text-anchor="middle" x="943.76" y="-464.1" font-family="Roboto" font-size="8.00"> user (memberships)</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/_static/img/treasury_validate_sogecredit.png
vendored
Normal file
After Width: | Height: | Size: 51 KiB |
370
docs/api/activity.rst
Normal file
@ -0,0 +1,370 @@
|
||||
API Activités
|
||||
=============
|
||||
|
||||
Activité
|
||||
--------
|
||||
|
||||
**Chemin :** `/api/activity/activity/ <https://note.crans.org/api/activity/activity/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Activity List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Activity` objects, serialize it to JSON with the given serializer,\nthen render it on /api/activity/activity/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Description"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Lieu",
|
||||
"help_text": "Lieu o\u00f9 l'activit\u00e9 est organis\u00e9e, par exemple la Kfet.",
|
||||
"max_length": 255
|
||||
},
|
||||
"date_start": {
|
||||
"type": "datetime",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Date de d\u00e9but"
|
||||
},
|
||||
"date_end": {
|
||||
"type": "datetime",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Date de fin"
|
||||
},
|
||||
"valid": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Valide"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Ouvrir"
|
||||
},
|
||||
"activity_type": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Type"
|
||||
},
|
||||
"creater": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Utilisateur"
|
||||
},
|
||||
"organizer": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Organisateur",
|
||||
"help_text": "Le club qui organise l'activit\u00e9. Les co\u00fbts d'invitation iront pour ce club."
|
||||
},
|
||||
"attendees_club": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Club attendu",
|
||||
"help_text": "Club qui est autoris\u00e9 \u00e0 rejoindre l'activit\u00e9. Tr\u00e8s souvent le club Kfet."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``description``
|
||||
* ``activity_type``
|
||||
* ``location``
|
||||
* ``creater``
|
||||
* ``organizer``
|
||||
* ``attendees_club``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
* ``valid``
|
||||
* ``open``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``description`` (expression régulière)
|
||||
* ``location`` (expression régulière)
|
||||
* ``creater__last_name`` (expression régulière)
|
||||
* ``creater__first_name`` (expression régulière)
|
||||
* ``creater__email`` (expression régulière)
|
||||
* ``creater__note__alias__name`` (expression régulière)
|
||||
* ``creater__note__alias__normalized_name`` (expression régulière)
|
||||
* ``organizer__name`` (expression régulière)
|
||||
* ``organizer__email`` (expression régulière)
|
||||
* ``organizer__note__alias__name`` (expression régulière)
|
||||
* ``organizer__note__alias__normalized_name`` (expression régulière)
|
||||
* ``attendees_club__name`` (expression régulière)
|
||||
* ``attendees_club__email`` (expression régulière)
|
||||
* ``attendees_club__note__alias__name`` (expression régulière)
|
||||
* ``attendees_club__note__alias__normalized_name`` (expression régulière)
|
||||
|
||||
Type d'activité
|
||||
---------------
|
||||
|
||||
**Chemin :** `/api/activity/type/ <https://note.crans.org/api/activity/type/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Activity Type List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `ActivityType` objects, serialize it to JSON with the given serializer,\nthen render it on /api/activity/type/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"manage_entries": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "G\u00e9rer les entr\u00e9es",
|
||||
"help_text": "Activer le support des entr\u00e9es pour cette activit\u00e9."
|
||||
},
|
||||
"can_invite": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Peut inviter"
|
||||
},
|
||||
"guest_entry_fee": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cotisation de l'entr\u00e9e invit\u00e9",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``manage_entries``
|
||||
* ``can_invite``
|
||||
* ``guest_entry_fee``
|
||||
|
||||
Invité
|
||||
------
|
||||
|
||||
**Chemin :** `/api/activity/guest/ <https://note.crans.org/api/activity/guest/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Guest List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Guest` objects, serialize it to JSON with the given serializer,\nthen render it on /api/activity/guest/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom de famille",
|
||||
"max_length": 255
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Pr\u00e9nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"activity": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Activity"
|
||||
},
|
||||
"inviter": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "H\u00f4te"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``activity``
|
||||
* ``activity__name``
|
||||
* ``last_name``
|
||||
* ``first_name``
|
||||
* ``inviter``
|
||||
* ``inviter__alias__name``
|
||||
* ``inviter__alias__normalized_name``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``activity__name`` (expression régulière)
|
||||
* ``last_name`` (expression régulière)
|
||||
* ``first_name`` (expression régulière)
|
||||
* ``inviter__user__email`` (expression régulière)
|
||||
* ``inviter__alias__name`` (expression régulière)
|
||||
* ``inviter__alias__normalized_name`` (expression régulière)
|
||||
|
||||
Entrée
|
||||
------
|
||||
|
||||
**Chemin :** `/api/activity/entry/ <https://note.crans.org/api/activity/entry/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Entry List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Entry` objects, serialize it to JSON with the given serializer,\nthen render it on /api/activity/entry/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"time": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Heure d'entr\u00e9e"
|
||||
},
|
||||
"activity": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Activit\u00e9"
|
||||
},
|
||||
"note": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Note"
|
||||
},
|
||||
"guest": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``activity``
|
||||
* ``time``
|
||||
* ``note``
|
||||
* ``guest``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``activity__name`` (expression régulière)
|
||||
* ``note__user__email`` (expression régulière)
|
||||
* ``note__alias__name`` (expression régulière)
|
||||
* ``note__alias__normalized_name`` (expression régulière)
|
||||
* ``guest__last_name`` (expression régulière)
|
||||
* ``guest__first_name`` (expression régulière)
|
||||
|
157
docs/api/basic.rst
Normal file
@ -0,0 +1,157 @@
|
||||
API générale
|
||||
============
|
||||
|
||||
Utilisateur
|
||||
-----------
|
||||
|
||||
**Chemin :** `/api/user/ <https://note.crans.org/api/user/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "User List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,\nthen render it on /api/user/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"last_login": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Derni\u00e8re connexion"
|
||||
},
|
||||
"is_superuser": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Statut super-utilisateur",
|
||||
"help_text": "Pr\u00e9cise que l'utilisateur poss\u00e8de toutes les permissions sans les assigner explicitement."
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Pseudo",
|
||||
"help_text": "Requis. 150 caract\u00e8res maximum. Uniquement des lettres, nombres et les caract\u00e8res \u00ab\u00a0@\u00a0\u00bb, \u00ab\u00a0.\u00a0\u00bb, \u00ab\u00a0+\u00a0\u00bb, \u00ab\u00a0-\u00a0\u00bb et \u00ab\u00a0_\u00a0\u00bb.",
|
||||
"max_length": 150
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Pr\u00e9nom",
|
||||
"max_length": 30
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Nom de famille",
|
||||
"max_length": 150
|
||||
},
|
||||
"email": {
|
||||
"type": "email",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Adresse \u00e9lectronique",
|
||||
"max_length": 254
|
||||
},
|
||||
"is_staff": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Statut \u00e9quipe",
|
||||
"help_text": "Pr\u00e9cise si l'utilisateur peut se connecter \u00e0 ce site d'administration."
|
||||
},
|
||||
"is_active": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Actif",
|
||||
"help_text": "Pr\u00e9cise si l'utilisateur doit \u00eatre consid\u00e9r\u00e9 comme actif. D\u00e9cochez ceci plut\u00f4t que de supprimer le compte."
|
||||
},
|
||||
"date_joined": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Date d'inscription"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``id``
|
||||
* ``username``
|
||||
* ``first_name``
|
||||
* ``last_name``
|
||||
* ``email``
|
||||
* ``is_superuser``
|
||||
* ``is_staff``
|
||||
* ``is_active``
|
||||
* ``note__alias__name``
|
||||
* ``note__alias__normalized_name``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``note__alias`` (expression régulière, cherche en priorité les alias les plus proches, puis cherche les alias normalisés)
|
||||
* ``last_name`` (expression régulière)
|
||||
* ``first_name`` (expression régulière)
|
||||
|
||||
Type de contenu
|
||||
---------------
|
||||
|
||||
**Chemin :** `/api/models/ <https://note.crans.org/api/models/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Content Type List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,\nthen render it on /api/models/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
]
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``id``
|
||||
* ``app_label``
|
||||
* ``model``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``app_label`` (expression régulière)
|
||||
* ``model`` (expression régulière)
|
||||
|
214
docs/api/index.rst
Normal file
@ -0,0 +1,214 @@
|
||||
API
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Applications
|
||||
|
||||
activity
|
||||
basic
|
||||
logs
|
||||
member
|
||||
note
|
||||
permission
|
||||
treasury
|
||||
wei
|
||||
|
||||
La NoteKfet2020 dispose d'une API REST. Elle est accessible sur `/api/ <https://note.crans.org/api/>`_.
|
||||
Elle supporte les requêtes GET, POST, HEAD, PUT, PATCH et DELETE (peut varier selon les pages).
|
||||
|
||||
Pages de l'API
|
||||
--------------
|
||||
|
||||
Il suffit d'ajouter le préfixe ``/api/`` pour arriver sur ces pages.
|
||||
|
||||
* `models <basic#type-de-contenu>`_ : liste des différents modèles enregistrés en base de données
|
||||
* `user <basic#utilisateur>`_ : liste des différents utilisateurs enregistrés
|
||||
* `members/profile <member#profil-utilisateur>`_ : liste des différents profils associés à des utilisateurs
|
||||
* `members/club <member#club>`_ : liste des différents clubs enregistrés
|
||||
* `members/membership <member#adhesion>`_ : liste des adhésions enregistrées
|
||||
* `activity/activity <activity#activite>`_ : liste des activités recensées
|
||||
* `activity/type <activity#type-d-activite>`_ : liste des différents types d'activités : pots, soirées de club, ...
|
||||
* `activity/guest <activity#invite>`_ : liste des personnes invitées lors d'une activité
|
||||
* `activity/entry <activity#entree>`_ : liste des entrées effectuées lors des activités
|
||||
* `note/note <note#note>`_ : liste des notes enregistrées
|
||||
* `note/alias <note#alias>`_ : liste des alias enregistrés
|
||||
* `note/consumer <note#consommateur>`_ : liste des alias enregistrés avec leur note associée
|
||||
* `note/transaction/category <note#categorie-de-transaction>`_ : liste des différentes catégories de boutons : soft, alcool, ...
|
||||
* `note/transaction/transaction <note#transaction>`_ : liste des transactions effectuées
|
||||
* `note/transaction/template <note#modele-de-transaction>`_ : liste des boutons enregistrés
|
||||
* `treasury/invoice <treasury#facture>`_ : liste des factures générées
|
||||
* `treasury/product <treasury#produit>`_ : liste des produits associés à des factures
|
||||
* `treasury/remittance_type <treasury#type-de-remise>`_ : liste des types de remises supportés : chèque
|
||||
* `treasury/remittance <treasury#remise>`_ : liste des différentes remises enregistrées
|
||||
* `treasury/remittance <treasury#remise>`_ : liste des crédits de la Société générale enregistrés
|
||||
* `permission/permission <permission#permission>`_ : liste de toutes les permissions enregistrées
|
||||
* `permission/roles <permission#permissions-par-roles>`_ : liste des permissions octroyées pour chacun des rôles
|
||||
* `logs <logs#journal-de-modification>`_ : liste des modifications enregistrées en base de données
|
||||
* `wei/club <wei#wei>`_ : liste des WEI
|
||||
* `wei/bus <wei#bus>`_ : liste des bus de tous les WEI
|
||||
* `wei/team <wei#equipe-de-bus>`_ : liste des équipes de tous les WEI
|
||||
* `wei/role <wei#role-au-wei>`_ : liste des rôles possibles pour le WEI
|
||||
* `wei/registration <wei#participation-au-wei>`_ : liste de toutes les inscriptions à un WEI
|
||||
* `wei/membership <wei#adhesion-au-wei>`_ : liste des adhésions compètes à un WEI
|
||||
|
||||
Utilisation de l'API
|
||||
--------------------
|
||||
|
||||
La page ``/api/<model>/`` affiche la liste de tous les éléments enregistrés. La page ``/api/<model>/<pk>/`` affiche
|
||||
les attributs d'un objet uniquement.
|
||||
|
||||
L'affichage des données peut se faire sous deux formes : via une interface HTML propre ou directement en affichant
|
||||
le JSON brut. Le changement peut se faire en ajoutant en paramètre de l'URL ``format=json`` ou ``format=api``, ou bien
|
||||
en plaçant en en-tête de la requête ``Accept: application/json`` ou ``Accept: text/html``.
|
||||
|
||||
L'API Web propose des formulaires facilitant l'ajout et la modification d'éléments.
|
||||
|
||||
S'authentifier
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
L'authentification peut se faire soit par session en se connectant via la page de connexion classique,
|
||||
soit via un jeton d'authentification. Le jeton peut se récupérer via la page de son propre compte, en cliquant
|
||||
sur le bouton « `Accès API <https://note.crans.org/accounts/manage-auth-token/>`_ ». Il peut être révoqué et regénéré
|
||||
en un clic.
|
||||
|
||||
Pour s'authentifier via ce jeton, il faut ajouter l'en-tête ``Authorization: Token <TOKEN>`` aux paramètres HTTP.
|
||||
|
||||
En s'authentifiant par cette méthode, les masques de droit sont ignorés, les droits maximaux sont accordés.
|
||||
|
||||
GET
|
||||
~~~
|
||||
|
||||
Une requête GET affiche un ou des éléments. Si on veut la liste de tous les éléments d'un modèle, la réponse
|
||||
est de cette forme :
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"count": "<COUNT>",
|
||||
"next": "/api/<MODEL>/?page=<NEXT_PAGE>",
|
||||
"previous": "/api/<MODEL>/?page=<NEXT_PAGE>",
|
||||
"results": [ ]
|
||||
}
|
||||
|
||||
Où ``<COUNT>`` est le nombre d'éléments trouvés. La page n'affiche les informations que 20 par 20 pour ne pas
|
||||
augmenter inutilement la taille de la réponse. Les champs ``next`` et ``previous`` contiennent les URL des pages
|
||||
suivantes et précédentes (``null`` si première ou dernière page). Le champ ``results`` contient enfin l'ensemble des
|
||||
objets trouvés, au format JSON.
|
||||
|
||||
Certaines pages disposent de filtres, permettant de sélectionner les objets recherchés. Par exemple, il est possible
|
||||
de chercher une note d'un certain type matchant avec un certain alias.
|
||||
|
||||
Trois types de filtres sont implémentés :
|
||||
|
||||
* Les filtres Django, permettant d'ajouter ``?key=value`` dans l'URL pour filtrer les objets ayant ``value`` comme
|
||||
valeur pour la clé ``key`` ;
|
||||
* Les filtres de recherche, permettant une recherche plus souple notamment par expressions régulières ou contenance,
|
||||
et permet aussi de chercher parmi plusieurs clés à partir d'un champ ``search`` dans l'URL ;
|
||||
* Les filtres de tri, qui ne filtrent pas réellement mais changent l'ordre. En ajoutant ``?ordering=key`` dans l'URL,
|
||||
on trie les résultats selon la clé ``key`` dans l'ordre croissant, et ``?ordering=-key`` trie dans l'ordre
|
||||
décroissant.
|
||||
|
||||
Les filtres disponibles sont indiqués sur chacune des pages de documentation.
|
||||
|
||||
Le résultat est déjà par défaut filtré par droits : seuls les éléments que l'utilisateur à le droit de voir sont affichés.
|
||||
Cela est possible grâce à la structure des permissions, générant justement des filtres de requêtes de base de données.
|
||||
|
||||
Une requête à l'adresse ``/api/<model>/<pk>/`` affiche directement les informations du modèle demandé au format JSON.
|
||||
|
||||
POST
|
||||
~~~~
|
||||
|
||||
Une requête POST permet d'ajouter des éléments. Cette requête n'est possible que sur la page ``/api/<model>/``,
|
||||
la requête POST n'est pas supportée sur les pages de détails (car cette requête permet ... l'ajout).
|
||||
|
||||
Des exceptions sont faites sur certaines pages : les pages de logs et de contenttypes sont en lecture uniquement.
|
||||
|
||||
Les formats supportés sont multiples : ``application/json``, ``application/x-www-url-encoded``, ``multipart/form-data``.
|
||||
Cela facilite l'envoi de requêtes. Le module construit ensuite l'instance du modèle et le sauvegarde dans la base de
|
||||
données. L'application ``permission`` s'assure que l'utilisateur à le droit de faire ce type de modification. La réponse
|
||||
renvoyée est l'objet enregistré au format JSON si l'ajout s'est bien déroulé, sinon un message d'erreur au format JSON.
|
||||
|
||||
PATCH
|
||||
~~~~~
|
||||
|
||||
Une requête PATCH permet de modifier un élément. Ce type de requête n'est disponible que sur la page de détails d'un
|
||||
élément : ``/api/<model>/pk/``.
|
||||
|
||||
Comme pour la requête POST, les formats supportés sont multiples : ``application/json``,
|
||||
``application/x-www-url-encoded``, ``multipart/form-data``.
|
||||
|
||||
Il n'est pas utile d'indiquer tous les champs du modèle : seuls ceux qui sont à modifier suffisent.
|
||||
|
||||
Attention : pour les modèles polymorphiques (``Note``, ``Transaction``), il faut toujours spécifier le type de modèle
|
||||
pour que l'API arrive à s'y retrouver. Par exemple, si on veut rendre la transaction n°42 non valide, on effectue une
|
||||
requête ``PATCH`` sur ``/api/note/transaction/transaction/42/`` avec les données suivantes :
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"valid": false,
|
||||
"resourcetype": "RecurrentTransaction"
|
||||
}
|
||||
|
||||
PUT
|
||||
~~~
|
||||
|
||||
Une requête PUT permet de remplacer un élément. Ce type de requête n'est disponible que sur la page de détails d'un
|
||||
élément : ``/api/<model>/pk/``.
|
||||
|
||||
Comme pour les requêtes POST ou PATCH, les formats supportés sont multiples : ``application/json``,
|
||||
``application/x-www-url-encoded``, ``multipart/form-data``.
|
||||
|
||||
Contrairement à la requête PATCH, l'intégralité du modèle est remplacé. L'ancien modèle est détruit, on en recrée un
|
||||
nouveau avec la même clé primaire. Le fonctionnement est similaire à une requête POST.
|
||||
|
||||
DELETE
|
||||
~~~~~~
|
||||
|
||||
Une requête de type DELETE permet de supprimer un élément. Ce type de requête n'est disponible que sur la page de
|
||||
détails d'un élément : ``/api/<model>/pk/``.
|
||||
|
||||
Aucune donnée n'est nécessaire. Le module de permissions vérifiera que la suppression est possible. Une erreur
|
||||
est sinon renvoyée.
|
||||
|
||||
OPTIONS
|
||||
~~~~~~~
|
||||
|
||||
Une reqête OPTIONS affiche l'ensemble des opérations possibles sur un modèle ou une instance. Prototype d'une réponse :
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "<NAME>",
|
||||
"description": "<DESCRIPTION>",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"<METHOD>": {
|
||||
"<FIELD_NAME>": {
|
||||
"type": "<TYPE>",
|
||||
"required": "<REQUIRED>",
|
||||
"read_only": "<READ_ONLY>",
|
||||
"label": "<LABEL>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* ``<METHOD>`` est le type de requête HTTP supporté (pour modification, inclus dans {``POST``, ``PUT``, ``PATCH``}).
|
||||
* ``<FIELD_NAME>`` est le nom du champ dans le modèle concerné (exemple : ``id``)
|
||||
* ``<TYPE>`` représente le type de données : ``integer``, ``string``, ``date``, ``choice``, ``field`` (pour les clés étrangères), ...
|
||||
* ``<REQUIRED>`` est un booléen indiquant si le champ est requis dans le modèle ou s'il peut être nul/vide.
|
||||
* ``<READ_ONLY>`` est un booléen indiquant si le champ est accessible en lecture uniquement.
|
||||
* ``<LABEL>`` représente le label du champ, son nom traduit, qui s'affiche dans le formulaire accessible sur l'API Web.
|
||||
|
||||
Des contraintes peuvent s'ajouter à cela selon les champs : taille maximale de chaînes de caractères, valeurs minimales
|
||||
et maximales pour les entiers ...
|
42
docs/api/logs.rst
Normal file
@ -0,0 +1,42 @@
|
||||
API Logs
|
||||
========
|
||||
|
||||
Journal de modification
|
||||
-----------------------
|
||||
|
||||
**Chemin :** `/api/logs/ <https://note.crans.org/api/logs/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Changelog List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Changelog` objects, serialize it to JSON with the given serializer,\nthen render it on /api/logs/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
]
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``model``
|
||||
* ``action``
|
||||
* ``instance_pk``
|
||||
* ``user``
|
||||
* ``ip``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``timestamp``
|
||||
* ``id``
|
||||
|
476
docs/api/member.rst
Normal file
@ -0,0 +1,476 @@
|
||||
API Membres
|
||||
===========
|
||||
|
||||
Profil utilisateur
|
||||
------------------
|
||||
|
||||
**Chemin :** `/api/members/profile/ <https://note.crans.org/api/members/profile/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Profile List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Profile` objects, serialize it to JSON with the given serializer,\nthen render it on /api/members/profile/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Num\u00e9ro de t\u00e9l\u00e9phone",
|
||||
"max_length": 50
|
||||
},
|
||||
"section": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Section",
|
||||
"help_text": "e.g. \"1A0\", \"9A\u2665\", \"SAPHIRE\"",
|
||||
"max_length": 255
|
||||
},
|
||||
"department": {
|
||||
"type": "choice",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "D\u00e9partement",
|
||||
"choices": [
|
||||
{
|
||||
"value": "A0",
|
||||
"display_name": "Informatique (A0)"
|
||||
},
|
||||
{
|
||||
"value": "A1",
|
||||
"display_name": "Math\u00e9matiques (A1)"
|
||||
},
|
||||
{
|
||||
"value": "A2",
|
||||
"display_name": "Chimie (A''2)"
|
||||
},
|
||||
{
|
||||
"value": "A'2",
|
||||
"display_name": "Physique appliqu\u00e9e (A'2)"
|
||||
},
|
||||
{
|
||||
"value": "A3",
|
||||
"display_name": "Biologie (A3)"
|
||||
},
|
||||
{
|
||||
"value": "B1234",
|
||||
"display_name": "SAPHIRE (B1234)"
|
||||
},
|
||||
{
|
||||
"value": "B1",
|
||||
"display_name": "M\u00e9canique (B1)"
|
||||
},
|
||||
{
|
||||
"value": "B2",
|
||||
"display_name": "G\u00e9nie civil (B2)"
|
||||
},
|
||||
{
|
||||
"value": "B3",
|
||||
"display_name": "G\u00e9nie m\u00e9canique (B3)"
|
||||
},
|
||||
{
|
||||
"value": "B4",
|
||||
"display_name": "EEA (B4)"
|
||||
},
|
||||
{
|
||||
"value": "C",
|
||||
"display_name": "Design (C)"
|
||||
},
|
||||
{
|
||||
"value": "D2",
|
||||
"display_name": "\u00c9conomie-gestion (D2)"
|
||||
},
|
||||
{
|
||||
"value": "D3",
|
||||
"display_name": "Sciences sociales (D3)"
|
||||
},
|
||||
{
|
||||
"value": "E",
|
||||
"display_name": "Anglais (E)"
|
||||
},
|
||||
{
|
||||
"value": "EXT",
|
||||
"display_name": "Externe (EXT)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"promotion": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Promotion",
|
||||
"help_text": "Ann\u00e9e d'entr\u00e9e dans l'\u00e9cole (None si non-\u00e9tudiant\u00b7e de l'ENS)",
|
||||
"min_value": 0,
|
||||
"max_value": 32767
|
||||
},
|
||||
"address": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Adresse",
|
||||
"max_length": 255
|
||||
},
|
||||
"paid": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Pay\u00e9",
|
||||
"help_text": "Indique si l'utilisateur per\u00e7oit un salaire."
|
||||
},
|
||||
"ml_events_registration": {
|
||||
"type": "choice",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "S'inscrire sur la liste de diffusion pour rester inform\u00e9 des \u00e9v\u00e9nements sur le campus (1 mail par semaine)",
|
||||
"choices": [
|
||||
{
|
||||
"value": "",
|
||||
"display_name": "Non"
|
||||
},
|
||||
{
|
||||
"value": "fr",
|
||||
"display_name": "Oui (les recevoir en fran\u00e7ais)"
|
||||
},
|
||||
{
|
||||
"value": "en",
|
||||
"display_name": "Oui (les recevoir en anglais)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ml_sport_registration": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "S'inscrire sur la liste de diffusion pour rester inform\u00e9 des actualit\u00e9s sportives sur le campus (1 mail par semaine)"
|
||||
},
|
||||
"ml_art_registration": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "S'inscrire sur la liste de diffusion pour rester inform\u00e9 des actualit\u00e9s artistiques sur le campus (1 mail par semaine)"
|
||||
},
|
||||
"report_frequency": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Fr\u00e9quence des rapports (en jours)",
|
||||
"min_value": 0,
|
||||
"max_value": 32767
|
||||
},
|
||||
"last_report": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Date de dernier rapport"
|
||||
},
|
||||
"email_confirmed": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Adresse email confirm\u00e9e"
|
||||
},
|
||||
"registration_valid": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Inscription valide"
|
||||
},
|
||||
"user": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``user``
|
||||
* ``user__first_name``
|
||||
* ``user__last_name``
|
||||
* ``user__username``
|
||||
* ``user__email``
|
||||
* ``user__note__alias__name``
|
||||
* ``user__note__alias__normalized_name``
|
||||
* ``phone_number``
|
||||
* ``section``
|
||||
* ``department``
|
||||
* ``promotion``
|
||||
* ``address``
|
||||
* ``paid``
|
||||
* ``ml_events_registration``
|
||||
* ``ml_sport_registration``
|
||||
* ``ml_art_registration``
|
||||
* ``report_frequency``
|
||||
* ``email_confirmed``
|
||||
* ``registration_valid``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``user__first_name`` (expression régulière)
|
||||
* ``user__last_name`` (expression régulière)
|
||||
* ``user__username`` (expression régulière)
|
||||
* ``user__email`` (expression régulière)
|
||||
* ``user__note__alias__name`` (expression régulière)
|
||||
* ``user__note__alias__normalized_name`` (expression régulière)
|
||||
|
||||
Club
|
||||
----
|
||||
|
||||
**Chemin :** `/api/members/club/ <https://note.crans.org/api/members/club/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Club List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Club` objects, serialize it to JSON with the given serializer,\nthen render it on /api/members/club/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"email": {
|
||||
"type": "email",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Courriel",
|
||||
"max_length": 254
|
||||
},
|
||||
"require_memberships": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "N\u00e9cessite des adh\u00e9sions",
|
||||
"help_text": "D\u00e9cochez si ce club n'utilise pas d'adh\u00e9sions."
|
||||
},
|
||||
"membership_fee_paid": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cotisation pour adh\u00e9rer (normalien \u00e9l\u00e8ve)",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_fee_unpaid": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cotisation pour adh\u00e9rer (normalien \u00e9tudiant)",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_duration": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Dur\u00e9e de l'adh\u00e9sion",
|
||||
"help_text": "La dur\u00e9e maximale (en jours) d'une adh\u00e9sion (NULL = infinie).",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_start": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "D\u00e9but de l'adh\u00e9sion",
|
||||
"help_text": "Date \u00e0 partir de laquelle les adh\u00e9rents peuvent renouveler leur adh\u00e9sion."
|
||||
},
|
||||
"membership_end": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Fin de l'adh\u00e9sion",
|
||||
"help_text": "Date maximale d'une fin d'adh\u00e9sion, apr\u00e8s laquelle les adh\u00e9rents doivent la renouveler."
|
||||
},
|
||||
"parent_club": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Club parent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``email``
|
||||
* ``note__alias__name``
|
||||
* ``note__alias__normalized_name``
|
||||
* ``parent_club``
|
||||
* ``parent_club__name``
|
||||
* ``require_memberships``
|
||||
* ``membership_fee_paid``
|
||||
* ``membership_fee_unpaid``
|
||||
* ``membership_duration``
|
||||
* ``membership_start``
|
||||
* ``membership_end``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``email`` (expression régulière)
|
||||
* ``note__alias__name`` (expression régulière)
|
||||
* ``note__alias__normalized_name`` (expression régulière)
|
||||
|
||||
Adhésion
|
||||
--------
|
||||
|
||||
**Chemin :** `/api/members/membership/ <https://note.crans.org/api/members/membership/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Membership List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Membership` objects, serialize it to JSON with the given serializer,\nthen render it on /api/members/membership/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"date_start": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "L'adh\u00e9sion commence le"
|
||||
},
|
||||
"date_end": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "L'adh\u00e9sion finit le"
|
||||
},
|
||||
"fee": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Cotisation",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"user": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Utilisateur"
|
||||
},
|
||||
"club": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Club"
|
||||
},
|
||||
"roles": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "R\u00f4les"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``club__name``
|
||||
* ``club__email``
|
||||
* ``club__note__alias__name``
|
||||
* ``club__note__alias__normalized_name``
|
||||
* ``user__username``
|
||||
* ``user__last_name``
|
||||
* ``user__first_name``
|
||||
* ``user__email``
|
||||
* ``user__note__alias__name``
|
||||
* ``user__note__alias__normalized_name``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
* ``fee``
|
||||
* ``roles``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``id``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``club__name`` (expression régulière)
|
||||
* ``club__email`` (expression régulière)
|
||||
* ``club__note__alias__name`` (expression régulière)
|
||||
* ``club__note__alias__normalized_name`` (expression régulière)
|
||||
* ``user__username`` (expression régulière)
|
||||
* ``user__last_name`` (expression régulière)
|
||||
* ``user__first_name`` (expression régulière)
|
||||
* ``user__email`` (expression régulière)
|
||||
* ``user__note__alias__name`` (expression régulière)
|
||||
* ``user__note__alias__normalized_name`` (expression régulière)
|
||||
* ``roles__name`` (expression régulière)
|
||||
|
403
docs/api/note.rst
Normal file
@ -0,0 +1,403 @@
|
||||
API Note
|
||||
========
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
**Chemin :** `/api/note/note/ <https://note.crans.org/api/note/note/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Note Polymorphic List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Note` objects (with polymorhism),\nserialize it to JSON with the given serializer,\nthen render it on /api/note/note/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``alias__name``
|
||||
* ``polymorphic_ctype``
|
||||
* ``is_active``
|
||||
* ``balance``
|
||||
* ``last_negative``
|
||||
* ``created_at``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``alias__name``
|
||||
* ``alias__normalized_name``
|
||||
* ``balance``
|
||||
* ``created_at``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``alias__normalized_name`` (expression régulière)
|
||||
* ``alias__name`` (expression régulière)
|
||||
* ``polymorphic_ctype__model`` (expression régulière)
|
||||
* ``noteuser__user__last_name`` (expression régulière)
|
||||
* ``noteuser__user__first_name`` (expression régulière)
|
||||
* ``noteuser__user__email`` (expression régulière)
|
||||
* ``noteuser__user__email`` (expression régulière)
|
||||
* ``noteclub__club__email`` (expression régulière)
|
||||
|
||||
Alias
|
||||
-----
|
||||
|
||||
**Chemin :** `/api/note/alias/ <https://note.crans.org/api/note/alias/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Alias List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Alias` objects, serialize it to JSON with the given serializer,\nthen render it on /api/aliases/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"normalized_name": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "Normalized name"
|
||||
},
|
||||
"note": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Note"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``note``
|
||||
* ``note__noteuser__user``
|
||||
* ``note__noteclub__club``
|
||||
* ``note__polymorphic_ctype__model``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``normalized_name``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``alias`` (cherche en priorité les alias les plus proches, puis cherche les alias normalisés)
|
||||
* ``normalized_name`` (expression régulière)
|
||||
* ``name`` (expression régulière)
|
||||
* ``note__polymorphic_ctype__model`` (expression régulière)
|
||||
|
||||
Consommateur
|
||||
------------
|
||||
|
||||
**Chemin :** `/api/note/consumer/ <https://note.crans.org/api/note/consumer/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Consumer List",
|
||||
"description": "",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Cette page est en lecture seule. Elle offre l'avantage de fournir directement les informations sur la note associée
|
||||
à l'alias au lieu de l'identifiant uniquement, afin de minimiser les appels à l'API.
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``alias`` (expression régulière, cherche en priorité les alias les plus proches, puis cherche les alias normalisés)
|
||||
* ``note``
|
||||
* ``note__noteuser__user``
|
||||
* ``note__noteclub__club``
|
||||
* ``note__polymorphic_ctype__model``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``normalized_name``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``normalized_name`` (expression régulière)
|
||||
* ``name`` (expression régulière)
|
||||
* ``note__polymorphic_ctype__model`` (expression régulière)
|
||||
|
||||
Catégorie de transaction
|
||||
------------------------
|
||||
|
||||
**Chemin :** `/api/note/transaction/category/ <https://note.crans.org/api/note/transaction/category/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Template Category List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `TemplateCategory` objects, serialize it to JSON with the given serializer,\nthen render it on /api/note/transaction/category/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 31
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``templates``
|
||||
* ``templates__name``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``templates__name`` (expression régulière)
|
||||
|
||||
Modèle de transaction
|
||||
---------------------
|
||||
|
||||
**Chemin :** `/api/note/transaction/template/ <https://note.crans.org/api/note/transaction/template/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Transaction Template List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `TransactionTemplate` objects, serialize it to JSON with the given serializer,\nthen render it on /api/note/transaction/template/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"amount": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Montant",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"display": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Afficher"
|
||||
},
|
||||
"highlighted": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Mis en avant"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Description",
|
||||
"max_length": 255
|
||||
},
|
||||
"destination": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Destination"
|
||||
},
|
||||
"category": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Type"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``amount``
|
||||
* ``display``
|
||||
* ``category``
|
||||
* ``category__name``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``amount``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``category__name`` (expression régulière)
|
||||
|
||||
Transaction
|
||||
-----------
|
||||
|
||||
**Chemin :** `/api/note/transaction/transaction/ <https://note.crans.org/api/note/transaction/transaction/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Transaction List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Transaction` objects, serialize it to JSON with the given serializer,\nthen render it on /api/note/transaction/transaction/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``source``
|
||||
* ``source_alias``
|
||||
* ``source__alias__name``
|
||||
* ``source__alias__normalized_name``
|
||||
* ``destination``
|
||||
* ``destination_alias``
|
||||
* ``destination__alias__name``
|
||||
* ``destination__alias__normalized_name``
|
||||
* ``quantity``
|
||||
* ``polymorphic_ctype``
|
||||
* ``amount``
|
||||
* ``created_at``
|
||||
* ``valid``
|
||||
* ``invalidity_reason``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``created_at``
|
||||
* ``amount``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``reason`` (expression régulière)
|
||||
* ``source_alias`` (expression régulière)
|
||||
* ``source__alias__name`` (expression régulière)
|
||||
* ``source__alias__normalized_name`` (expression régulière)
|
||||
* ``destination_alias`` (expression régulière)
|
||||
* ``destination__alias__name`` (expression régulière)
|
||||
* ``destination__alias__normalized_name`` (expression régulière)
|
||||
* ``invalidity_reason`` (expression régulière)
|
||||
|
82
docs/api/permission.rst
Normal file
@ -0,0 +1,82 @@
|
||||
API Permissions
|
||||
===============
|
||||
|
||||
Permission
|
||||
----------
|
||||
|
||||
**Chemin :** `/api/permission/permission/ <https://note.crans.org/api/permission/permission/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Permission List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Permission` objects, serialize it to JSON with the given serializer,\nthen render it on /api/permission/permission/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
]
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``model``
|
||||
* ``type``
|
||||
* ``query``
|
||||
* ``mask``
|
||||
* ``field``
|
||||
* ``permanent``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``model__name`` (expression régulière)
|
||||
* ``query`` (expression régulière)
|
||||
* ``description`` (expression régulière)
|
||||
|
||||
Permissions par rôles
|
||||
---------------------
|
||||
|
||||
**Chemin :** `/api/permission/roles/ <https://note.crans.org/api/permission/roles/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Role List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `RolePermission` objects, serialize it to JSON with the given serializer\nthen render it on /api/permission/roles/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
]
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``permissions``
|
||||
* ``for_club``
|
||||
* ``memberships__user``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``for_club__name`` (expression régulière)
|
||||
|
402
docs/api/treasury.rst
Normal file
@ -0,0 +1,402 @@
|
||||
API Trésorerie
|
||||
==============
|
||||
|
||||
Facture
|
||||
-------
|
||||
|
||||
**Chemin :** `/api/treasury/invoice/ <https://note.crans.org/api/treasury/invoice/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Invoice List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Invoice` objects, serialize it to JSON with the given serializer,\nthen render it on /api/treasury/invoice/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Num\u00e9ro de facture",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"products": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "Products"
|
||||
},
|
||||
"bde": {
|
||||
"type": "choice",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "BDE"
|
||||
},
|
||||
"object": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Objet",
|
||||
"max_length": 255
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Description"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"address": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Adresse"
|
||||
},
|
||||
"date": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Date"
|
||||
},
|
||||
"acquitted": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Acquitt\u00e9e"
|
||||
},
|
||||
"locked": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Verrouill\u00e9e",
|
||||
"help_text": "Une facture ne peut plus \u00eatre modifi\u00e9e si elle est verrouill\u00e9e."
|
||||
},
|
||||
"tex": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Fichier TeX source"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``bde``
|
||||
* ``object``
|
||||
* ``description``
|
||||
* ``name``
|
||||
* ``address``
|
||||
* ``date``
|
||||
* ``acquitted``
|
||||
* ``locked``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``object`` (expression régulière)
|
||||
* ``description`` (expression régulière)
|
||||
* ``name`` (expression régulière)
|
||||
* ``address`` (expression régulière)
|
||||
|
||||
Produit
|
||||
-------
|
||||
|
||||
**Chemin :** `/api/treasury/product/ <https://note.crans.org/api/treasury/product/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Product List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Product` objects, serialize it to JSON with the given serializer,\nthen render it on /api/treasury/product/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"designation": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "D\u00e9signation",
|
||||
"max_length": 255
|
||||
},
|
||||
"quantity": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Quantit\u00e9",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"amount": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Prix unitaire",
|
||||
"min_value": -2147483648,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"invoice": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Facture"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``invoice``
|
||||
* ``designation``
|
||||
* ``quantity``
|
||||
* ``amount``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``designation`` (expression régulière)
|
||||
* ``invoice__object`` (expression régulière)
|
||||
|
||||
Type de remise
|
||||
--------------
|
||||
|
||||
**Chemin :** `/api/treasury/remittance_type/ <https://note.crans.org/api/treasury/remittance_type/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Remittance Type List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `RemittanceType` objects, serialize it to JSON with the given serializer\nthen render it on /api/treasury/remittance_type/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"note": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Note"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``note``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``note__special_type`` (expression régulière)
|
||||
|
||||
Remise
|
||||
------
|
||||
|
||||
**Chemin :** `/api/treasury/remittance/ <https://note.crans.org/api/treasury/remittance/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Remittance List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Remittance` objects, serialize it to JSON with the given serializer,\nthen render it on /api/treasury/remittance/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"transactions": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "Transactions"
|
||||
},
|
||||
"date": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Date"
|
||||
},
|
||||
"comment": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Commentaire",
|
||||
"max_length": 255
|
||||
},
|
||||
"closed": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Ferm\u00e9e"
|
||||
},
|
||||
"remittance_type": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Type"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``date``
|
||||
* ``remittance_type``
|
||||
* ``comment``
|
||||
* ``closed``
|
||||
* ``transaction_proxies__transaction``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``remittance_type__note__special_type`` (expression régulière)
|
||||
* ``comment`` (expression régulière)
|
||||
|
||||
Crédit de la société générale
|
||||
-----------------------------
|
||||
|
||||
**Chemin :** `/api/treasury/soge_credit/ <https://note.crans.org/api/treasury/soge_credit/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Soge Credit List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `SogeCredit` objects, serialize it to JSON with the given serializer,\nthen render it on /api/treasury/soge_credit/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"user": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Utilisateur"
|
||||
},
|
||||
"credit_transaction": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Transaction de cr\u00e9dit"
|
||||
},
|
||||
"transactions": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Transactions d'adh\u00e9sion"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``user``
|
||||
* ``user__last_name``
|
||||
* ``user__first_name``
|
||||
* ``user__email``
|
||||
* ``user__note__alias__name``
|
||||
* ``user__note__alias__normalized_name``
|
||||
* ``transactions``
|
||||
* ``credit_transaction``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``user__last_name`` (expression régulière)
|
||||
* ``user__first_name`` (expression régulière)
|
||||
* ``user__email`` (expression régulière)
|
||||
* ``user__note__alias__name`` (expression régulière)
|
||||
* ``user__note__alias__normalized_name`` (expression régulière)
|
||||
|
710
docs/api/wei.rst
Normal file
@ -0,0 +1,710 @@
|
||||
API WEI
|
||||
=======
|
||||
|
||||
Wei
|
||||
---
|
||||
|
||||
**Chemin :** `/api/wei/club/ <https://note.crans.org/api/wei/club/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Wei Club List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `WEIClub` objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/club/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"email": {
|
||||
"type": "email",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Courriel",
|
||||
"max_length": 254
|
||||
},
|
||||
"require_memberships": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "N\u00e9cessite des adh\u00e9sions",
|
||||
"help_text": "D\u00e9cochez si ce club n'utilise pas d'adh\u00e9sions."
|
||||
},
|
||||
"membership_fee_paid": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cotisation pour adh\u00e9rer (normalien \u00e9l\u00e8ve)",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_fee_unpaid": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cotisation pour adh\u00e9rer (normalien \u00e9tudiant)",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_duration": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Dur\u00e9e de l'adh\u00e9sion",
|
||||
"help_text": "La dur\u00e9e maximale (en jours) d'une adh\u00e9sion (NULL = infinie).",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"membership_start": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "D\u00e9but de l'adh\u00e9sion",
|
||||
"help_text": "Date \u00e0 partir de laquelle les adh\u00e9rents peuvent renouveler leur adh\u00e9sion."
|
||||
},
|
||||
"membership_end": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Fin de l'adh\u00e9sion",
|
||||
"help_text": "Date maximale d'une fin d'adh\u00e9sion, apr\u00e8s laquelle les adh\u00e9rents doivent la renouveler."
|
||||
},
|
||||
"year": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Ann\u00e9e",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"date_start": {
|
||||
"type": "date",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "D\u00e9but"
|
||||
},
|
||||
"date_end": {
|
||||
"type": "date",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Fin"
|
||||
},
|
||||
"parent_club": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Club parent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``year``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
* ``email``
|
||||
* ``note__alias__name``
|
||||
* ``note__alias__normalized_name``
|
||||
* ``parent_club``
|
||||
* ``parent_club__name``
|
||||
* ``require_memberships``
|
||||
* ``membership_fee_paid``
|
||||
* ``membership_fee_unpaid``
|
||||
* ``membership_duration``
|
||||
* ``membership_start``
|
||||
* ``membership_end``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``email`` (expression régulière)
|
||||
* ``note__alias__name`` (expression régulière)
|
||||
* ``note__alias__normalized_name`` (expression régulière)
|
||||
|
||||
Bus
|
||||
---
|
||||
|
||||
**Chemin :** `/api/wei/bus/ <https://note.crans.org/api/wei/bus/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Bus List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `Bus` objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/bus/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Description"
|
||||
},
|
||||
"information_json": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Informations sur le questionnaire",
|
||||
"help_text": "Informations sur le sondage pour les nouveaux membres, encod\u00e9es en JSON"
|
||||
},
|
||||
"wei": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "WEI"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``wei``
|
||||
* ``description``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``wei__name`` (expression régulière)
|
||||
* ``description`` (expression régulière)
|
||||
|
||||
Équipe de bus
|
||||
-------------
|
||||
|
||||
**Chemin :** `/api/wei/team/ <https://note.crans.org/api/wei/team/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Bus Team List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `BusTeam` objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/team/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"color": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Couleur",
|
||||
"help_text": "La couleur du T-Shirt, stock\u00e9 sous la forme de son \u00e9quivalent num\u00e9rique",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Description"
|
||||
},
|
||||
"bus": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Bus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``bus``
|
||||
* ``color``
|
||||
* ``description``
|
||||
* ``bus__wei``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
* ``bus__name`` (expression régulière)
|
||||
* ``bus__wei__name`` (expression régulière)
|
||||
* ``description`` (expression régulière)
|
||||
|
||||
Rôle au wei
|
||||
-----------
|
||||
|
||||
**Chemin :** `/api/wei/role/ <https://note.crans.org/api/wei/role/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Wei Role List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `WEIRole` objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/role/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom",
|
||||
"max_length": 255
|
||||
},
|
||||
"for_club": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "S'applique au club"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Permissions"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``name``
|
||||
* ``permissions``
|
||||
* ``memberships``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``name`` (expression régulière)
|
||||
|
||||
Participant au wei
|
||||
------------------
|
||||
|
||||
**Chemin :** `/api/wei/registration/ <https://note.crans.org/api/wei/registration/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Wei Registration List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all WEIRegistration objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/registration/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"soge_credit": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Cr\u00e9dit de la Soci\u00e9t\u00e9 g\u00e9n\u00e9rale"
|
||||
},
|
||||
"caution_check": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Ch\u00e8que de caution donn\u00e9"
|
||||
},
|
||||
"birth_date": {
|
||||
"type": "date",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Date de naissance"
|
||||
},
|
||||
"gender": {
|
||||
"type": "choice",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Genre",
|
||||
"choices": [
|
||||
{
|
||||
"value": "male",
|
||||
"display_name": "Homme"
|
||||
},
|
||||
{
|
||||
"value": "female",
|
||||
"display_name": "Femme"
|
||||
},
|
||||
{
|
||||
"value": "nonbinary",
|
||||
"display_name": "Non-binaire"
|
||||
}
|
||||
]
|
||||
},
|
||||
"clothing_cut": {
|
||||
"type": "choice",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Coupe de v\u00eatement",
|
||||
"choices": [
|
||||
{
|
||||
"value": "male",
|
||||
"display_name": "Homme"
|
||||
},
|
||||
{
|
||||
"value": "female",
|
||||
"display_name": "Femme"
|
||||
}
|
||||
]
|
||||
},
|
||||
"clothing_size": {
|
||||
"type": "choice",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Taille de v\u00eatement",
|
||||
"choices": [
|
||||
{
|
||||
"value": "XS",
|
||||
"display_name": "XS"
|
||||
},
|
||||
{
|
||||
"value": "S",
|
||||
"display_name": "S"
|
||||
},
|
||||
{
|
||||
"value": "M",
|
||||
"display_name": "M"
|
||||
},
|
||||
{
|
||||
"value": "L",
|
||||
"display_name": "L"
|
||||
},
|
||||
{
|
||||
"value": "XL",
|
||||
"display_name": "XL"
|
||||
},
|
||||
{
|
||||
"value": "XXL",
|
||||
"display_name": "XXL"
|
||||
}
|
||||
]
|
||||
},
|
||||
"health_issues": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Probl\u00e8mes de sant\u00e9"
|
||||
},
|
||||
"emergency_contact_name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Nom du contact en cas d'urgence",
|
||||
"max_length": 255
|
||||
},
|
||||
"emergency_contact_phone": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "T\u00e9l\u00e9phone du contact en cas d'urgence",
|
||||
"max_length": 32
|
||||
},
|
||||
"first_year": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Premi\u00e8re ann\u00e9e",
|
||||
"help_text": "Indique si l'utilisateur est nouveau dans l'\u00e9cole."
|
||||
},
|
||||
"information_json": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Informations sur l'inscription",
|
||||
"help_text": "Informations sur l'inscription (bus pour les 2A+, questionnaire pour les 1A), encod\u00e9es en JSON"
|
||||
},
|
||||
"user": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Utilisateur"
|
||||
},
|
||||
"wei": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "WEI"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``user``
|
||||
* ``user__username``
|
||||
* ``user__first_name``
|
||||
* ``user__last_name``
|
||||
* ``user__email``
|
||||
* ``user__note__alias__name``
|
||||
* ``user__note__alias__normalized_name``
|
||||
* ``wei``
|
||||
* ``wei__name``
|
||||
* ``wei__email``
|
||||
* ``wei__year``
|
||||
* ``soge_credit``
|
||||
* ``caution_check``
|
||||
* ``birth_date``
|
||||
* ``gender``
|
||||
* ``clothing_cut``
|
||||
* ``clothing_size``
|
||||
* ``first_year``
|
||||
* ``emergency_contact_name``
|
||||
* ``emergency_contact_phone``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``user__username`` (expression régulière)
|
||||
* ``user__first_name`` (expression régulière)
|
||||
* ``user__last_name`` (expression régulière)
|
||||
* ``user__email`` (expression régulière)
|
||||
* ``user__note__alias__name`` (expression régulière)
|
||||
* ``user__note__alias__normalized_name`` (expression régulière)
|
||||
* ``wei__name`` (expression régulière)
|
||||
* ``wei__email`` (expression régulière)
|
||||
* ``health_issues`` (expression régulière)
|
||||
* ``emergency_contact_name`` (expression régulière)
|
||||
* ``emergency_contact_phone`` (expression régulière)
|
||||
|
||||
Adhésion au wei
|
||||
---------------
|
||||
|
||||
**Chemin :** `/api/wei/membership/ <https://note.crans.org/api/wei/membership/>`_
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"name": "Wei Membership List",
|
||||
"description": "REST API View set.\nThe djangorestframework plugin will get all `BusTeam` objects, serialize it to JSON with the given serializer,\nthen render it on /api/wei/membership/",
|
||||
"renders": [
|
||||
"application/json",
|
||||
"text/html"
|
||||
],
|
||||
"parses": [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data"
|
||||
],
|
||||
"actions": {
|
||||
"POST": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"required": false,
|
||||
"read_only": true,
|
||||
"label": "ID"
|
||||
},
|
||||
"date_start": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "L'adh\u00e9sion commence le"
|
||||
},
|
||||
"date_end": {
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "L'adh\u00e9sion finit le"
|
||||
},
|
||||
"fee": {
|
||||
"type": "integer",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Cotisation",
|
||||
"min_value": 0,
|
||||
"max_value": 2147483647
|
||||
},
|
||||
"user": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Utilisateur"
|
||||
},
|
||||
"club": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "Club"
|
||||
},
|
||||
"bus": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Bus"
|
||||
},
|
||||
"team": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "\u00c9quipe"
|
||||
},
|
||||
"registration": {
|
||||
"type": "field",
|
||||
"required": false,
|
||||
"read_only": false,
|
||||
"label": "Inscription au WEI"
|
||||
},
|
||||
"roles": {
|
||||
"type": "field",
|
||||
"required": true,
|
||||
"read_only": false,
|
||||
"label": "R\u00f4les"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filtres Django
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* ``club__name``
|
||||
* ``club__email``
|
||||
* ``club__note__alias__name``
|
||||
* ``club__note__alias__normalized_name``
|
||||
* ``user__username``
|
||||
* ``user__last_name``
|
||||
* ``user__first_name``
|
||||
* ``user__email``
|
||||
* ``user__note__alias__name``
|
||||
* ``user__note__alias__normalized_name``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
* ``fee``
|
||||
* ``roles``
|
||||
* ``bus``
|
||||
* ``bus__name``
|
||||
* ``team``
|
||||
* ``team__name``
|
||||
* ``registration``
|
||||
|
||||
Tris possible
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* ``id``
|
||||
* ``date_start``
|
||||
* ``date_end``
|
||||
|
||||
Filtres de recherche
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``club__name`` (expression régulière)
|
||||
* ``club__email`` (expression régulière)
|
||||
* ``club__note__alias__name`` (expression régulière)
|
||||
* ``club__note__alias__normalized_name`` (expression régulière)
|
||||
* ``user__username`` (expression régulière)
|
||||
* ``user__last_name`` (expression régulière)
|
||||
* ``user__first_name`` (expression régulière)
|
||||
* ``user__email`` (expression régulière)
|
||||
* ``user__note__alias__name`` (expression régulière)
|
||||
* ``user__note__alias__normalized_name`` (expression régulière)
|
||||
* ``roles__name`` (expression régulière)
|
||||
* ``bus__name`` (expression régulière)
|
||||
* ``team__name`` (expression régulière)
|
||||
|
110
docs/apps/activity.rst
Normal file
@ -0,0 +1,110 @@
|
||||
Application Activités
|
||||
=====================
|
||||
|
||||
L'application activités gère les différentes activités liées au BDE. Elle permet entre autres de créer des activités qui
|
||||
peuvent être diffusées via des calendriers ou la mailing list d'événements. Elle permet aussi de réguler l'accès aux
|
||||
événements, en s'assurant que leur note est positive. Elle permet enfin de gérer les invités.
|
||||
|
||||
Modèles
|
||||
-------
|
||||
|
||||
L'application comporte 5 modèles : activités, types d'activité, invités, entrées et transactions d'invitation.
|
||||
|
||||
Types d'activité
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Les activités sont triées par type (pots, soirées de club, ...), et chaque type regroupe diverses informations :
|
||||
|
||||
* Nom du type
|
||||
* Possibilité d'inviter des non-adhérents (booléen)
|
||||
* Prix d'invitation (entier, centimes à débiter sur la note de l'hôte)
|
||||
|
||||
Activités
|
||||
~~~~~~~~~
|
||||
|
||||
Le modèle d'activité regroupe les informations liées à l'activité même :
|
||||
|
||||
* Nom de l'activité
|
||||
* Description de l'activité
|
||||
* Créateur, personne qui a proposé l'activité
|
||||
* Club ayant organisé l'activité
|
||||
* Note sur laquelle verser les crédits d'invitation (peut être nul si non concerné)
|
||||
* Club invité (généralement le club Kfet)
|
||||
* Date et heure de début
|
||||
* Date et heure de fin
|
||||
* Activité valide (booléen)
|
||||
* Activité ouverte (booléen)
|
||||
|
||||
Entrées
|
||||
~~~~~~~
|
||||
|
||||
Une instance de ce modèle est créé dès que quelqu'un est inscrit à l'activité. Sont stockées les informations suivantes :
|
||||
|
||||
* Activité concernée (clé étrangère)
|
||||
* Heure d'entrée
|
||||
* Note de la personne entrée, ou hôte s'il s'agit d'un invité (clé étrangère vers ``NoteUser``)
|
||||
* Invité (``OneToOneField`` vers ``Guest``, ``None`` si c'est la personne elle-même qui rentre et non son invité)
|
||||
|
||||
Il n'est pas possible de créer une entrée si la note est en négatif.
|
||||
|
||||
Invités
|
||||
~~~~~~~
|
||||
|
||||
Les adhérents ont la possibilité d'inviter des amis. Pour cela, les différentes informations sont enregistrées :
|
||||
|
||||
* Activité concernée (clé étrangère)
|
||||
* Nom de famille
|
||||
* Prénom
|
||||
* Note de la personne ayant invité
|
||||
|
||||
Certaines contraintes s'appliquent :
|
||||
|
||||
* Une personne ne peut pas être invitée plus de 5 fois par an (coupe nom/prénom)
|
||||
* Un adhérent ne peut pas inviter plus de 3 personnes par activité.
|
||||
|
||||
Transactions d'invitation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On étend le modèle ``Transaction`` de l'application note afin de supporter les transactions d'invitation. Elles ne
|
||||
comportent qu'un champ supplémentaire, de type ``OneToOneField`` vers ``Guest``.
|
||||
|
||||
Ce modèle aurait pu appartenir à l'application ``note``, mais afin de rester modulaire et que l'application ``note``
|
||||
ne dépende pas de cette application, on procède de cette manière.
|
||||
|
||||
Graphe
|
||||
~~~~~~
|
||||
|
||||
.. image:: ../_static/img/graphs/activity.svg
|
||||
:alt: Graphe de l'application activités
|
||||
|
||||
UI
|
||||
--
|
||||
|
||||
Création d'activités
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
N'importe quel adhérent Kfet peut suggérer l'ajout d'une activité via un formulaire.
|
||||
|
||||
Gestion des activités
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Les ayant-droit (Res[pot] et respos infos) peuvent valider les activités proposées. Ils peuvent également la modifier
|
||||
si besoin. Ils peuvent enfin la déclarer ouvertes pour lancer l'accès aux entrées.
|
||||
|
||||
N'importe qui peut inviter des amis non adhérents, tant que les contraintes de nombre (un adhérent n'invite pas plus de
|
||||
trois personnes par activité et une personne ne peut pas être invitée plus de 5 fois par an). L'invitation est
|
||||
facturée à l'entrée.
|
||||
|
||||
Entrées aux soirées
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
L'interface d'entrées est simple et ergonomique. Elle contient un champ de texte. À chaque fois que le champ est
|
||||
modifié, un tableau est affiché comprenant la liste des invités et des adhérents dont le prénom, le nom ou un alias
|
||||
de la note est acceptée par le texte entré.
|
||||
|
||||
En cliquant sur la ligne de la personne qui souhaite rentrée, s'il s'agit d'un adhérent, alors la personne est comptée
|
||||
comme entrée à l'activité, sous réserve que sa note soit positive. S'il s'agit d'un invité, alors 3 boutons
|
||||
apparaîssent, afin de régler la taxe d'invitation : l'un prélève directement depuis la note de l'hôte, les deux autres
|
||||
permettent un paiement par espèces ou par carte bancaire. En réalité, les deux derniers boutons enregistrent
|
||||
automatiquement un crédit sur la note de l'hôte, puis une transaction (de type ``GuestTransaction``) est faite depuis
|
||||
la note de l'hôte vers la note de l'organisateur de l'événement.
|
73
docs/apps/index.rst
Normal file
@ -0,0 +1,73 @@
|
||||
Applications de la NoteKfet2020
|
||||
===============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Applications
|
||||
|
||||
member
|
||||
note/index
|
||||
activity
|
||||
permission
|
||||
../api/index
|
||||
registration
|
||||
logs
|
||||
treasury
|
||||
wei
|
||||
|
||||
La NoteKfet est un projet Django, décomposé en applications.
|
||||
Certaines Applications sont développées uniquement pour ce projet, et sont indispensables,
|
||||
d'autres sont packagesé et sont installées comme dépendances.
|
||||
Enfin des fonctionnalités annexes ont été rajouté, mais ne sont pas essentiel au déploiement de la NoteKfet;
|
||||
leur usage est cependant recommandé.
|
||||
|
||||
Le front utilise le framework Bootstrap4 et quelques morceaux de javascript custom.
|
||||
|
||||
Applications indispensables
|
||||
---------------------------
|
||||
|
||||
* ``note_kfet`` :
|
||||
Application "projet" de django, c'est ici que la config de la note est gérée.
|
||||
* `Member <member>`_ :
|
||||
Gestion des profils d'utilisateurs, des clubs et de leur membres.
|
||||
* `Note <note>`_ :
|
||||
Les notes associés a des utilisateurs ou des clubs.
|
||||
* `Activity <activity>`_ :
|
||||
La gestion des Activités (créations, gestion, entrée...)
|
||||
* `Permission <permission>`_ :
|
||||
Backend de droits, limites les pouvoirs des utilisateurs
|
||||
* `API <../api>`_ :
|
||||
API REST de la note, est notamment utilisée pour rendre la note dynamique
|
||||
(notamment la page de conso)
|
||||
* `Registration <registration>`_ :
|
||||
Gestion des inscriptions à la Note Kfet
|
||||
|
||||
|
||||
Applications packagées
|
||||
----------------------
|
||||
* ``polymorphic``
|
||||
Utiliser pour la création de models polymorphiques (``Note`` et ``Transaction`` notamment) cf `Note <note>`_.
|
||||
|
||||
L'utilisation des models polymorphiques est détaillé sur la documentation du package:
|
||||
`<https://django-polymorphic.readthedocs.io/en/stable/>`_
|
||||
|
||||
* ``crispy_forms``
|
||||
Utiliser pour générer des forms avec bootstrap4
|
||||
* ``django_tables2``
|
||||
utiliser pour afficher des tables de données et les formater, en python plutôt qu'en HTML.
|
||||
* ``restframework``
|
||||
Base de l'`API <../api>`_.
|
||||
|
||||
Applications facultatives
|
||||
-------------------------
|
||||
* `Logs <logs>`_
|
||||
Enregistre toute les modifications effectuées en base de donnée.
|
||||
* ``cas-server``
|
||||
Serveur central d'authenfication, permet d'utiliser son compte de la NoteKfet2020 pour se connecter à d'autre application ayant intégrer un client.
|
||||
* `Script <https://gitlab.crans.org/bde/nk20-scripts>`_
|
||||
Ensemble de commande `./manage.py` pour la gestion de la note: import de données, verification d'intégrité, etc ...
|
||||
* `Treasury <treasury>`_ :
|
||||
Interface de gestion pour les trésoriers, émission de facture, remise de chèque, statistiques ...
|
||||
* `WEI <wei>`_ :
|
||||
Interface de gestion du WEI.
|
||||
|
57
docs/apps/logs.rst
Normal file
@ -0,0 +1,57 @@
|
||||
Logs
|
||||
====
|
||||
|
||||
Chaque modification effectuée sur un modèle est enregistrée dans la base dans données.
|
||||
|
||||
Dès qu'un modèle veut être sauvegardé, deux signaux sont envoyés dans ``logs.signals`` : un avant et un après
|
||||
la sauvegarde.
|
||||
En pré-sauvegarde, on récupère l'ancienne version du modèle, si elle existe.
|
||||
En post-sauvegarde, on récupère l'utilisateur et l'IP courants (voir ci-dessous), on convertit les modèles en JSON
|
||||
et on enregistre une entrée ``Changelog`` dans la base de données.
|
||||
|
||||
Pour récupérer l'utilisateur et son IP, le middleware ``logs.middlewares.LogsMiddlewares`` récupère à chaque requête
|
||||
l'utilisateur et l'adresse IP, et les stocke dans le processus courant, afin qu'ils puissent être
|
||||
récupérés par les signaux.
|
||||
|
||||
Si jamais la modification ne provient pas d'une requête Web, on suppose qu'elle vient d'une instruction
|
||||
lancée avec ``manage.py``.
|
||||
On récupère alors le nom de l'utilisateur dans l'interface de commandes, et si une note est associée à cet alias,
|
||||
alors on considère que c'est le détenteur de la note qui a effectué cette modification, sur l'adresse IP ``127.0.0.1``.
|
||||
Sinon, le champ est laissé à ``None``.
|
||||
|
||||
Une entrée de ``Changelog`` contient les informations suivantes :
|
||||
|
||||
* Utilisateur (``ForeignKey`` vers ``User``, nullable)
|
||||
* Adresse IP (``GenericIPAddressField``)
|
||||
* Type de modèle enregistré (``ForeignKey`` vers ``Model``)
|
||||
* Identifiant ``pk`` de l'instance enregistrée (``CharField``)
|
||||
* Anciennes données (au format JSON, ``None`` si création de données)
|
||||
* Nouvelles données (au format JSON, ``None`` si suppression de données)
|
||||
* Action (``CharField`` avec choix ``create``, ``edit``, ``delete``)
|
||||
* Date de modification (``DateTimeField``)
|
||||
|
||||
Exemple de Changelog, pour la création d'une transaction de 42424242 centimes d'une note vers une autre :
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"id": 42,
|
||||
"ip": "192.168.0.1",
|
||||
"instance_pk": "1",
|
||||
"previous": null,
|
||||
"data": "{\"id\":1,\"created_at\":\"2020-03-11T17:24:09.858651+01:00\",\"quantity\":1,\"amount\":42424242,\"reason\":\"Volons la Kfet\",\"valid\":true,\"polymorphic_ctype\":36,\"source\":6,\"destination\":7}",
|
||||
"action": "create",
|
||||
"timestamp": "2020-03-11T17:24:10.088151+01:00",
|
||||
"user": 1,
|
||||
"model": 36
|
||||
}
|
||||
|
||||
S'il est préférable de passer en console Postgresql pour parcourir les logs, ils sont trouvables via l'API dans
|
||||
``/api/logs``, sous réserve d'avoir les droits suffisants (ie. être respo info).
|
||||
|
||||
|
||||
Graphe
|
||||
~~~~~~
|
||||
|
||||
.. image:: ../_static/img/graphs/logs.svg
|
||||
:alt: Logs graphe
|
145
docs/apps/member.rst
Normal file
@ -0,0 +1,145 @@
|
||||
Application Member
|
||||
==================
|
||||
|
||||
L'application ``member`` s'occcupe de la gestion des utilisateurs enregistrés.
|
||||
|
||||
Le model d'utilisateur ``django.contrib.auth.model.User`` est complété par un ``Profile`` utilisateur.
|
||||
|
||||
Tous les utilisateurs peuvent être membre de ``Club``. Cela se traduit par une adhésion ``Membership``, dont les
|
||||
caractéristiques sont propres à chaque club.
|
||||
|
||||
En pratique, la NoteKfet possède au minimum deux Club: **Bde** et **Kfet** (instanciés via les fixtures). Et tous
|
||||
les personnes à jour de cotisation sont membre à minima de Bde.
|
||||
Être adhérent du club Kfet permet d'utiliser sa note pour consommer.
|
||||
|
||||
Modèles
|
||||
-------
|
||||
|
||||
Utilisateur
|
||||
~~~~~~~~~~~
|
||||
|
||||
Le modèle ``User`` est directement implémenté dans Django et n'appartient pas à l'application ``member``, mais il est
|
||||
bon de rappeler à quoi ressemble ce modèle.
|
||||
|
||||
* ``date_joined`` : ``DateTimeField``, date à laquelle l'utilisateur a été inscrit (*inutilisé dans la Note*)
|
||||
* ``email`` : ``EmailField``, adresse e-mail de l'utilisateur.
|
||||
* ``first_name`` : ``CharField``, prénom de l'utilisateur.
|
||||
* ``is_active`` : ``BooleanField``, indique si le compte est actif et peut se connecter.
|
||||
* ``is_staff`` : ``BooleanField``, indique si l'utilisateur peut se connecter à l'interface Django-admin.
|
||||
* ``is_superuser`` : ``BooleanField``, indique si l'utilisateur dispose de droits super-utilisateurs, permettant n'importe quelle action en base de donnée (lecture, ajout, modification, suppression).
|
||||
* ``last_login`` : ``DateTimeField``, date et heure de dernière connexion.
|
||||
* ``last_name`` : ``CharField``, nom de famille de l'utilisateur.
|
||||
* ``password`` : ``CharField``, contient le hash du mot de passe de l'utilisateur. L'algorithme utilisé est celui par défaut de Django : PBKDF2 + HMAC + SHA256 avec 150000 itérations.
|
||||
* ``username`` : ``CharField`` (unique), pseudo de l'utilisateur.
|
||||
|
||||
Profil
|
||||
~~~~~~
|
||||
|
||||
Le modèle ``Profile`` contient un champ ``user`` de type ``OneToOneField``, ce qui permet de voir ce modèle comme une
|
||||
extension du modèle ``User``, sans avoir à le réécrire. Il contient diverses informations personnelles sur
|
||||
l'utilisateur, utiles pour l'adhésion au BDE :
|
||||
|
||||
* ``user`` : ``OneToOneField(User)``, utilisateur lié à ce profil
|
||||
* ``address`` : ``CharField``, adresse physique de l'utilisateur
|
||||
* ``paid`` : ``BooleanField``, indique si l'utilisateur normalien est rémunéré ou non (utile pour différencier les montants d'adhésion aux clubs)
|
||||
* ``phone_number`` : ``CharField``, numéro de téléphone de l'utilisateur
|
||||
* ``section`` : ``CharField``, section de l'ENS à laquelle apartient l'utilisateur (exemple : 1A0, ...)
|
||||
|
||||
Clubs
|
||||
~~~~~
|
||||
|
||||
La gestion des clubs est une différence majeure avec la Note Kfet 2015. La Note gère ainsi les adhésions des
|
||||
utilisateurs aux différents clubs.
|
||||
|
||||
* ``parent_club`` : ``ForeignKey(Club)``. La présence d'un club parent force l'adhésion au club parent avant de pouvoir adhérer au dit club. Tout club qui n'est pas le club BDE doit avoir le club BDE dans son arborescence.
|
||||
* ``email`` : ``EmailField``, adresse e-mail sur laquelle contacter le bureau du club.
|
||||
* ``membership_start`` : ``DateField``, date à partir de laquelle il est possible d'adhérer à un club pour l'année suivante (si adhésions à l'année), en ignorant l'année. Par exemple, l'adhésion BDE est possible à partir du 31/08 par défaut, et c'est à cette date que les adhésions pour l'année future est possible.
|
||||
* ``membership_end`` : ``DateField``, date maximale de fin d'adhésion. Pour le club BDE, il s'agit du 30/09 de l'année suivante. Si cette valeur vaut ``null``, la fin d'adhésion n'est pas limitée.
|
||||
* ``membership_duration`` : ``PositiveIntegerField``, durée (en jours) maximale d'adhésion. Par exemple, le club BDE permet des adhésions maximales de 13 mois, soit 396 jours.
|
||||
* ``membership_fee_paid`` : ``PositiveIntegerField``, montant de la cotisation (en centimes) pour qu'un élève normalien (donc rémunéré) puisse adhérer.
|
||||
* ``membership_fee_unpaid`` : ``PositiveIntegerField``, montant de la cotisation (en centimes) pour qu'un étudiant normalien (donc non rémunéré) puisse adhérer.
|
||||
* ``name`` : ``CharField``, nom du club.
|
||||
* ``require_memberships`` : ``BooleanField``, indique si le club est un vrai club BDE qui nécessite des adhésions de club, ou s'il s'agit d'une note "pot commun" (organisation d'une activité, note de département, ...)
|
||||
|
||||
Adhésions
|
||||
~~~~~~~~~
|
||||
|
||||
Comme indiqué précédemment, la note gère les adhésions.
|
||||
|
||||
* ``club`` : ``ForeignKey(Club)``, club lié à l'adhésion.
|
||||
* ``user`` : ``ForeignKey(User)``, utilisateur adhéré.
|
||||
* ``date_start`` : ``DateField``, date de début d'adhésion.
|
||||
* ``date_end`` : ``DateField``, date de fin d'adhésion.
|
||||
* ``fee`` : ``PositiveIntegerField``, montant de la cotisation payée.
|
||||
* ``roles`` : ``ManyToManyField(Role)``, liste des rôles endossés par l'adhérent.
|
||||
|
||||
Rôles
|
||||
~~~~~
|
||||
|
||||
Comme indiqué le modèle des adhésions, les adhésions octroient des rôles aux adhérents, qui offrent des permissions
|
||||
(cf ``RolesPermissions`` dans la page des permissions). Le modèle ``RolesPermissions`` possède un
|
||||
``OneToOneField(Role)``, qui implémente les permissions des rôles. Le modèle ``Role`` à proprement parler ne contient
|
||||
que le champ de son nom (``CharField``).
|
||||
|
||||
Transactions d'adhésion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Si le modèle ``MembershipTransaction`` appartient à l'application ``note``, il est bon d'en parler ici.
|
||||
Le modèle ``MembershipTransaction`` est une extension du modèle ``Transaction`` (application ``note``) qui est de type
|
||||
polymorphique, et contient en plus des informations de base de la transaction un champ ``OneToOneField(Membership)``
|
||||
faisant le lien entre l'adhésion et la transaction liée. Une adhésion club, si elle n'est pas gratuite,
|
||||
génère en effet automatiquement une transaction de l'utilisateur vers le club (voir section adhésions).
|
||||
|
||||
Graphe
|
||||
------
|
||||
|
||||
.. image:: ../_static/img/graphs/member.svg
|
||||
:alt: Graphe de l'application member
|
||||
|
||||
Adhésions
|
||||
---------
|
||||
|
||||
La Note Kfet offre la possibilité aux clubs de gérer l'adhésion de leurs membres. En plus de réguler les cotisations
|
||||
des adhérents, des permissions sont octroyées sur la note en fonction des rôles au sein des clubs. Un rôle est une
|
||||
fonction occupée au sein d'un club (Trésorier de club, président de club, GCKfet, Res[pot], respo info, ...).
|
||||
Une adhésion attribue à un adhérent ses rôles. Les rôles fournissent les permissions. Par exemple, le trésorier d'un
|
||||
club a le droit de faire des transferts de et vers la note du club, tant que la source reste au-dessus de -50 €.
|
||||
Une adhésion est considérée comme valide si la date du jour est comprise (au sens large) entre les dates de début et
|
||||
de fin d'adhésion.
|
||||
|
||||
On peut ajouter une adhésion à un utilisateur dans un club à tout non adhérent de ce club. La personne en charge
|
||||
d'adhérer quelqu'un choisit l'utilisateur, les rôles au sein du club et la date de début d'adhésion. Cette date de
|
||||
début d'adhésion doit se situer entre les champs ``club.membership_start`` et ``club.membership_end``,
|
||||
si ces champs sont non nuls. Si ``club.parent_club`` n'est pas nul, l'utilisateur doit être membre de ce club.
|
||||
Le montant de la cotisation est fixé en fonction du statut normalien de l'utilisateur (``club.membership_fee_paid``
|
||||
centimes pour les élèves et ``club.membership_fee_unpaid`` centimes pour les étudiants). La date de fin est calculée
|
||||
comme ce qui suit :
|
||||
|
||||
* Si ``club.membership_duration`` est non nul, alors ``date_end`` = ``date_start`` + ``club.membership_duration``
|
||||
* Sinon ``club``, ``date_end`` = ``date_start`` + 424242 jours (suffisant pour tenir au moins une vie)
|
||||
* Si ``club.membership_end`` est non nul, alors ``date_end`` = min(``date_end``, ``club.membership_end``)
|
||||
|
||||
Si l'utilisateur n'est pas membre du club ``Kfet``, l'adhésion n'est pas possible si le solde disponible sur sa note est
|
||||
insuffisant. Une fois toute ces contraintes vérifiées, l'adhésion est créée. Une transaction de type
|
||||
``MembershipTransaction`` est automatiquement créée de la note de l'utilisateur vers la note du club, finalisant l'adhésion.
|
||||
|
||||
Réadhésions
|
||||
~~~~~~~~~~~
|
||||
|
||||
Pour les clubs nécessitant des adhésions (de durée limitée), il est possible de réadhérer au bout d'un an. Dès lors
|
||||
que le jour actuel est après ``club.membership_start`` + 1 an, ``club.membership_start`` et
|
||||
``club.membership_end`` sont incrémentés d'un an.
|
||||
|
||||
Il est possible de réadhérer si :
|
||||
|
||||
* ``membership.date_start`` <= ``today`` <= ``membership.date_end`` (l'adhésion en cours est valide)
|
||||
* ``membership.date_start`` < ``club.membership_start`` (si la date de début d'adhésion du club est postérieure à la date de début d'adhésion, qui a donc été mise à jour, on a changé d'année)
|
||||
* Il n'y a pas encore de réadhésion (pas d'adhésion au même club vérifiant ``new_membership.date_start`` >= ``club.membership_start``)
|
||||
|
||||
Un bouton ``Réadhérer`` apparaît dans la liste des adhésions si le droit est permis et si ces contraintes sont vérifiées.
|
||||
En réadhérant, une nouvelle adhésion est créée pour l'utilisateur avec les mêmes rôles, commençant le lendemain de la
|
||||
date d'expiration de la précédente adhésion. Si on réadhère le 16 août pour une adhésion finissant le 30 septembre,
|
||||
la nouvelle adhésion commencera le 1er octobre).
|
||||
|
||||
Pour supprimer une adhésion (en cas d'erreur), se rendre dans django admin et changer la date de fin d'adhésion,
|
||||
sans oublier d'invalider la transaction d'adhésion.
|