From d40dcdbb5dced95f58b2114b2a4c90108f2aaf4b Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:00:18 +0100 Subject: [PATCH 01/69] Create django.yml --- .github/workflows/django.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/django.yml diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml new file mode 100644 index 0000000..03499e9 --- /dev/null +++ b/.github/workflows/django.yml @@ -0,0 +1,22 @@ +name: AHC CI|CD + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + test: + runs-on: ubuntu-latest + env: + DB_USER: ${{ secrets.DB_USER }} + strategy: + max-parallel: 4 + + steps: + - name: Build docker images + run: docker-compose build + + - name: Run tests + run: docker-compose run test From 9cd33011266532da7ecf0ac992fdcface6feed97 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Thu, 29 Feb 2024 18:42:44 +0100 Subject: [PATCH 02/69] Features/medical notes fixes (#6) * Fix: couch port injection * FIX: upload file to couch and error handling * Fix: precommit hookup exclude .sh * Feature: A frame for discord bot notifications * Fix: relocate couchdb connector's initializer * Fix: discord bot task * Status update * Fix: filename by djangotag, order of forms, post method atomization, appendixes per note limit, * Feature: display more info about appendixes * Feature: edit attachment description * Fix: permission check * Fix: missing classes * Feature: delete appendix * Fix: download url and remove from couch * Fix: default server timezone * Fix: run collect static command when container is build * Fix: EditDietRecordView permission check * Fix: FeedingNoteListView permission check * Ref: ownership permissions as mixin * Ref: ownership permissions as mixin * Fix: FullTimelineOfNotes filters * Shortcut: Button url to MedicalRecord - CreateNoteFormView - medical_visit form type * Fix: FullTimelineOfNotes all timestamps in user timezone * Doc: .env template * Fix: setup for testing * Fix: unused import * Fix: field type * Test: an example test for searching empty urls * Refactor: homepage cleared * Synch: to CI_CD --- AHC_app/.env.template.txt | 39 ++ AHC_app/.pre-commit-config.yaml | 2 +- .../AHC_app/celery_notifications/config.py | 10 +- AHC_app/AHC_app/celery_notifications/cron.py | 58 ++- .../utils/discord_utils.py | 33 ++ AHC_app/AHC_app/settings.py | 47 +- AHC_app/AHC_app/urls.py | 3 +- AHC_app/Pipfile | 3 +- AHC_app/Pipfile.lock | 481 ++++++++++++++++-- AHC_app/animals/forms.py | 20 +- AHC_app/animals/mixins/__init__.py | 0 .../mixins/animal_owner_permissions.py | 10 + AHC_app/animals/models.py | 25 +- .../animals/templates/animals/profile.html | 17 +- AHC_app/animals/urls.py | 4 +- AHC_app/animals/utils_owner/views.py | 71 +-- AHC_app/animals/views.py | 44 +- AHC_app/docker-compose.yml | 1 + AHC_app/homepage/models.py | 15 +- AHC_app/homepage/templates/homepage/base.html | 2 +- .../homepage/templates/homepage/homepage.html | 78 +-- AHC_app/homepage/tests/test_homepage.py | 28 +- AHC_app/homepage/views.py | 47 +- .../medical_notes/forms/type_feeding_notes.py | 17 +- ...notification_last_modification_and_more.py | 30 ++ ...notification_last_modification_and_more.py | 28 + ...011_medicalrecordattachment_description.py | 18 + ...icalrecordattachment_file_name_and_more.py | 23 + ...ve_medicalrecordattachment_url_and_more.py | 22 + ...notification_last_modification_and_more.py | 28 + .../medical_notes/models/type_basic_note.py | 7 +- .../models/type_feeding_notes.py | 13 +- .../medical_notes/full_timeline_of_notes.html | 44 +- .../medical_notes/notification_list.html | 156 +++--- .../medical_notes/templatetags/__init__.py | 0 .../templatetags/custom_file_name.py | 15 + .../templatetags/custom_to_class_name.py | 14 + AHC_app/medical_notes/urls.py | 7 + .../medical_notes/views/type_basic_note.py | 160 +++++- .../medical_notes/views/type_feeding_notes.py | 86 ++-- AHC_app/static/css/hide_large_description.css | 19 + AHC_app/static/js/pin_animal.js | 47 ++ ...0002_profile_allow_recennt_animals_list.py | 18 + .../migrations/0003_profile_pinned_animals.py | 19 + AHC_app/users/models.py | 16 +- README.md | 5 +- 46 files changed, 1401 insertions(+), 429 deletions(-) create mode 100644 AHC_app/.env.template.txt create mode 100644 AHC_app/AHC_app/celery_notifications/utils/discord_utils.py create mode 100644 AHC_app/animals/mixins/__init__.py create mode 100644 AHC_app/animals/mixins/animal_owner_permissions.py create mode 100644 AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py create mode 100644 AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py create mode 100644 AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py create mode 100644 AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py create mode 100644 AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py create mode 100644 AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py create mode 100644 AHC_app/medical_notes/templatetags/__init__.py create mode 100644 AHC_app/medical_notes/templatetags/custom_file_name.py create mode 100644 AHC_app/medical_notes/templatetags/custom_to_class_name.py create mode 100644 AHC_app/static/css/hide_large_description.css create mode 100644 AHC_app/static/js/pin_animal.js create mode 100644 AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py create mode 100644 AHC_app/users/migrations/0003_profile_pinned_animals.py diff --git a/AHC_app/.env.template.txt b/AHC_app/.env.template.txt new file mode 100644 index 0000000..6fe5166 --- /dev/null +++ b/AHC_app/.env.template.txt @@ -0,0 +1,39 @@ +# PostgresDB for docker-compose.yml - dev +POSTGRES_DB= +POSTGRES_USER= +POSTGRES_PASSWORD= + +# Celery for docker-compose.yml - dev +CELERY_BROKER_URL= +FLOWER_PORT= +FLOWER_BASIC_AUTH= + +# PostgresDB for settings.py - dev +DB_NAME= +DB_USER= +DB_PASSWORD= +DB_HOST= +DB_PORT= + +# CouchDB for docker-compose - dev +COUCHDB_USER= +COUCHDB_PASSWORD= +COUCHDB_PORT= + +# CouchDB for settings.py - dev +COUCH_CONNECTOR=Server("") + +# Celery for settings.py - dev +CELERY_BROKER_URL= +CELERY_BACKEND= + +# SMTP for settings.py - dev +EMAIL_BACKEND = "" +EMAIL_HOST = "" +EMAIL_PORT = "" +EMAIL_USE_TLS = +EMAIL_HOST_USER = "" +EMAIL_HOST_PASSWORD = "" + +# Discord bot token +DISCORD_TOKEN = "" diff --git a/AHC_app/.pre-commit-config.yaml b/AHC_app/.pre-commit-config.yaml index b57775b..fa46aa4 100644 --- a/AHC_app/.pre-commit-config.yaml +++ b/AHC_app/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: types: [python] require_serial: true args: ['--in-place', '--remove-all-unused-imports'] - exclude: '^requirements\.txt$' + exclude: '^requirements\.txt$|^setup_couchdb\.sh$' - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: diff --git a/AHC_app/AHC_app/celery_notifications/config.py b/AHC_app/AHC_app/celery_notifications/config.py index 533e926..7a3c94e 100644 --- a/AHC_app/AHC_app/celery_notifications/config.py +++ b/AHC_app/AHC_app/celery_notifications/config.py @@ -4,6 +4,7 @@ from celery.utils.log import get_task_logger from django.conf import settings +from AHC_app.celery_notifications.utils.discord_utils import send_via_discord from AHC_app.celery_notifications.utils.sending_utils import ( send_via_email, # , send_via_sms, send_via_discord ) @@ -20,7 +21,7 @@ @celery_obj.task() -def send_email_notifications(**kwargs): +def send_email_notifications(*args, **kwargs): recipient_list = kwargs.get("email") subject = kwargs.get("subject") message = kwargs.get("message") @@ -42,6 +43,7 @@ def send_sms_notifications(**kwargs): @shared_task(bind=True) -def send_discord_notifications(**kwargs): - # send_via_discord(**kwargs) - pass +def send_discord_notifications(self, *args, **kwargs): + user_id = kwargs.get("user_id") + user_message = kwargs.get("user_message") + send_via_discord(user_id, user_message) diff --git a/AHC_app/AHC_app/celery_notifications/cron.py b/AHC_app/AHC_app/celery_notifications/cron.py index 955b468..a46ed4c 100644 --- a/AHC_app/AHC_app/celery_notifications/cron.py +++ b/AHC_app/AHC_app/celery_notifications/cron.py @@ -9,10 +9,12 @@ from django.db.models import Q, QuerySet from django.utils import timezone -from django_cron import CronJobBase, Schedule from medical_notes.models.type_feeding_notes import EmailNotification -from AHC_app.celery_notifications.config import send_email_notifications +from AHC_app.celery_notifications.config import ( + send_discord_notifications, + send_email_notifications, +) from AHC_app.celery_notifications.utils.example_task import send_mail_fnc logger = logging.getLogger("crons_logger") @@ -138,21 +140,37 @@ def send_sms(): @log_exceptions_and_notifications def send_discord_notes(): - pass - - -class SynchNotificationsCron(CronJobBase): - RUN_EVERY_MINS = 60 - - schedule = Schedule(run_every_mins=RUN_EVERY_MINS) - code = "AHC_app.SynchNotificationsCronJob" - - run_at_times = ["55"] - - @staticmethod - def cron_send_emails(): - from icecream import ic - - ic() - - send_emails() + # notifications_to_send = get_notifications_to_send() + # + # if not notifications_to_send: + # return None + + user_id: int = 422570242275934219 + # user_id: int = 530756049913905172 + user_message = "Test message" + delay: int = 0 + + send_discord_notifications.apply_async(kwargs={"user_id": user_id, "user_message": user_message}, countdown=delay) + + +# class SynchNotificationsCron(CronJobBase): +# RUN_EVERY_MINS = 60 +# +# schedule = Schedule(run_every_mins=RUN_EVERY_MINS) +# code = "AHC_app.SynchNotificationsCronJob" +# +# # run_at_times = ["55"] +# +# @staticmethod +# def cron_send_emails(): +# from icecream import ic +# ic() +# +# send_emails() +# +# @staticmethod +# def cron_send_discord(): +# from icecream import ic +# ic() +# +# send_discord_notes() diff --git a/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py b/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py new file mode 100644 index 0000000..7a0746d --- /dev/null +++ b/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py @@ -0,0 +1,33 @@ +import asyncio + +from discord import Client, Intents +from discord.ext import commands +from django.conf import settings + +TOKEN = settings.DISCORD_TOKEN + + +def send_via_discord(user_id: int, user_message: str) -> None: + def init_bot() -> Client: + intents = Intents.default() + intents.members = True + intents.message_content = True + + # discord_client = Client(intents=intents) + discord_client = commands.Bot(command_prefix="!", intents=intents) + + return discord_client + + client = init_bot() + + @client.event + async def on_ready(): + for guild in client.guilds: + user = guild.get_member(user_id) + if user: + await user.send(user_message) + + await asyncio.sleep(2) + await client.close() + + client.run(TOKEN) diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 9cf2ff9..37a7423 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -10,12 +10,13 @@ https://docs.djangoproject.com/en/4.2/ref/settings/ """ import os +import sys from pathlib import Path -from decouple import config +import pycouchdb -# from ibmcloudant.cloudant_v1 import CloudantV1 +from decouple import config # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -108,24 +109,18 @@ # COUCH_CONNECTOR = (config("COUCH_CONNECTOR"),) -COUCHDB_USER = config("COUCHDB_USER") -COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") -# COUCHDB_URL = f'http://{COUCHDB_USER}:{COUCHDB_PASSWORD}@localhost:5984' -# COUCHDB_DATABASE_NAME = 'your_database_name' # Dostosuj nazwę bazy danych -# -# client = Cloudant(COUCHDB_USER, COUCHDB_PASSWORD, url=COUCHDB_URL) -# client.connect() -# -# COUCHDB_ATTACHMENTS_DATABASES = { -# 'default': { -# 'ENGINE': 'django_couchdb.backends.Cloudant', -# 'NAME': COUCHDB_DATABASE_NAME, -# 'USER': COUCHDB_USER, -# 'PASSWORD': COUCHDB_PASSWORD, -# 'URL': COUCHDB_URL, -# }, -# } +if "test" not in sys.argv: + + COUCHDB_USER = config("COUCHDB_USER") + COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") + COUCHDB_PORT = config("COUCHDB_PORT") + + COUCH_SERVER = pycouchdb.Server( + f"http://{COUCHDB_USER}:{COUCHDB_PASSWORD}@appendixes-db:{COUCHDB_PORT}/", authmethod="basic" + ) + COUCH_DB = COUCH_SERVER.database("appendixes") + COUCH_DB_LIMIT_PER_NOTE = 5 # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators @@ -155,9 +150,7 @@ LANGUAGE_CODE = "en-us" TIME_ZONE = "Europe/Warsaw" - USE_I18N = True - USE_TZ = True @@ -201,10 +194,10 @@ CRONJOBS = [ - ("*/4 * * * *", "AHC_app.celery_notifications.cron.send_emails"), - ("*/2 * * * *", "AHC_app.celery_notifications.cron.send_email_example"), - # ('2 * * * *', 'AHC_app.celery_notifications.cron:send_emails'), - ("4 * * * *", "AHC_app.celery_notifications.cron.send_sms"), + # ("*/4 * * * *", "AHC_app.celery_notifications.cron.send_emails"), + # ("*/2 * * * *", "AHC_app.celery_notifications.cron.send_email_example"), + # # ('2 * * * *', 'AHC_app.celery_notifications.cron:send_emails'), + # ("4 * * * *", "AHC_app.celery_notifications.cron.send_sms"), ("6 * * * *", "AHC_app.celery_notifications.cron.send_discord_notes"), ] @@ -222,3 +215,7 @@ EMAIL_USE_TLS = config("EMAIL_USE_TLS", default=False, cast=bool) EMAIL_HOST_USER = config("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD") + +DISCORD_TOKEN = config("DISCORD_TOKEN") + +TEST_RUNNER = "django.test.runner.DiscoverRunner" diff --git a/AHC_app/AHC_app/urls.py b/AHC_app/AHC_app/urls.py index 62bcce9..4ef5aef 100644 --- a/AHC_app/AHC_app/urls.py +++ b/AHC_app/AHC_app/urls.py @@ -17,7 +17,6 @@ from django.conf import settings from django.conf.urls.static import static from django.contrib import admin -from django.contrib.staticfiles.storage import staticfiles_storage from django.urls import include, path from django.views.generic.base import RedirectView @@ -29,6 +28,6 @@ path("note/", include("medical_notes.urls")), path( "favicon.ico", - RedirectView.as_view(url=staticfiles_storage.url("media/icons/chinchilla.png")), + RedirectView.as_view(url=static("media/icons/chinchilla.png")), ), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/AHC_app/Pipfile b/AHC_app/Pipfile index 85c4d47..f59df3a 100644 --- a/AHC_app/Pipfile +++ b/AHC_app/Pipfile @@ -5,7 +5,6 @@ name = "pypi" [packages] amqp = "==5.1.1" -asgiref = "==3.7.2" billiard = "==4.1.0" cachetools = "==5.3.2" celery = "==5.3.4" @@ -71,6 +70,8 @@ flower = "*" django-cron = "*" python-decouple = "*" pycouchdb = "*" +discord = "*" +django-ajax = "*" [dev-packages] pre-commit = "*" diff --git a/AHC_app/Pipfile.lock b/AHC_app/Pipfile.lock index b26b517..e5002eb 100644 --- a/AHC_app/Pipfile.lock +++ b/AHC_app/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ecf8fa89e82a273f325213bea86e78e6aa6270de50a3eb1530c5dc72e423b18e" + "sha256": "63c00ccdc99125bde692176d6cc5aaafa2510c0d0a6b067c88982da05cf36624" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,96 @@ ] }, "default": { + "aiohttp": { + "hashes": [ + "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168", + "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb", + "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5", + "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f", + "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc", + "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c", + "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29", + "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4", + "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc", + "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc", + "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63", + "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e", + "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d", + "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a", + "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60", + "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38", + "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b", + "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2", + "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53", + "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5", + "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4", + "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96", + "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58", + "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa", + "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321", + "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae", + "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce", + "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8", + "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194", + "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c", + "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf", + "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d", + "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869", + "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b", + "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52", + "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528", + "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5", + "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1", + "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4", + "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8", + "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d", + "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7", + "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5", + "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54", + "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3", + "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5", + "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c", + "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29", + "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3", + "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747", + "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672", + "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5", + "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11", + "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca", + "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768", + "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6", + "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2", + "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533", + "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6", + "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266", + "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d", + "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec", + "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5", + "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1", + "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b", + "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679", + "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283", + "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb", + "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b", + "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3", + "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051", + "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511", + "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e", + "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d", + "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542", + "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f" + ], + "markers": "python_version >= '3.8'", + "version": "==3.9.3" + }, + "aiosignal": { + "hashes": [ + "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", + "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" + }, "amqp": { "hashes": [ "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2", @@ -30,7 +120,6 @@ "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed" ], - "index": "pypi", "markers": "python_version >= '3.7'", "version": "==3.7.2" }, @@ -41,6 +130,14 @@ ], "version": "==2.4.1" }, + "attrs": { + "hashes": [ + "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2.0" + }, "billiard": { "hashes": [ "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a", @@ -404,6 +501,22 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.7.1" }, + "discord": { + "hashes": [ + "sha256:cc1ee2dbe6df218ca51519af355b97e87309f8230f58c7f34885feb8e8a76145", + "sha256:d7959418799dd3b1e896685812d880169c193468b061b3431fa2a4664febd3da" + ], + "index": "pypi", + "version": "==2.3.2" + }, + "discord.py": { + "hashes": [ + "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c", + "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==2.3.2" + }, "django": { "hashes": [ "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", @@ -413,6 +526,13 @@ "markers": "python_version >= '3.8'", "version": "==4.2.1" }, + "django-ajax": { + "hashes": [ + "sha256:f45a4c5b0240a0c5e0e088e056e71ec3b381bbd8fa2e6b2bc307c9ff6b64bf33" + ], + "index": "pypi", + "version": "==0.3.0" + }, "django-appconf": { "hashes": [ "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d", @@ -523,6 +643,89 @@ "markers": "python_version >= '3.8'", "version": "==4.39.4" }, + "frozenlist": { + "hashes": [ + "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", + "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", + "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", + "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", + "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", + "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", + "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", + "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", + "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", + "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", + "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", + "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", + "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", + "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", + "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", + "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", + "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", + "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", + "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", + "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", + "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", + "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", + "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", + "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", + "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", + "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", + "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", + "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", + "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", + "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", + "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", + "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", + "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", + "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", + "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", + "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", + "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", + "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", + "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", + "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", + "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", + "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", + "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", + "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", + "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", + "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", + "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", + "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", + "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", + "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", + "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", + "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", + "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", + "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", + "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", + "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", + "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", + "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", + "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", + "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", + "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", + "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", + "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", + "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", + "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", + "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", + "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", + "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", + "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", + "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", + "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", + "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", + "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", + "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", + "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", + "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", + "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" + ], + "markers": "python_version >= '3.8'", + "version": "==1.4.1" + }, "httplib2": { "hashes": [ "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", @@ -702,6 +905,102 @@ "markers": "python_version >= '3.8'", "version": "==3.7.1" }, + "multidict": { + "hashes": [ + "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", + "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c", + "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", + "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b", + "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8", + "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", + "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd", + "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40", + "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", + "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3", + "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c", + "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9", + "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5", + "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae", + "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442", + "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9", + "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", + "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", + "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", + "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", + "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50", + "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182", + "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453", + "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e", + "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600", + "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", + "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", + "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241", + "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461", + "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", + "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", + "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b", + "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e", + "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7", + "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386", + "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", + "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9", + "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf", + "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee", + "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5", + "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a", + "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", + "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54", + "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", + "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", + "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", + "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319", + "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", + "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f", + "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527", + "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", + "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604", + "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", + "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8", + "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", + "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5", + "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626", + "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c", + "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d", + "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c", + "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc", + "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc", + "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", + "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38", + "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", + "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1", + "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", + "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3", + "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755", + "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", + "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a", + "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046", + "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", + "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479", + "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", + "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", + "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", + "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83", + "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929", + "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93", + "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a", + "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c", + "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44", + "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89", + "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", + "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e", + "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", + "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", + "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423", + "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef" + ], + "markers": "python_version >= '3.7'", + "version": "==6.0.5" + }, "numpy": { "hashes": [ "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187", @@ -830,11 +1129,11 @@ }, "prometheus-client": { "hashes": [ - "sha256:4585b0d1223148c27a225b10dbec5ae9bc4c81a99a3fa80774fa6209935324e1", - "sha256:c88b1e6ecf6b41cd8fb5731c7ae919bf66df6ec6fafa555cd6c0e16ca169ae92" + "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89", + "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7" ], "markers": "python_version >= '3.8'", - "version": "==0.19.0" + "version": "==0.20.0" }, "prompt-toolkit": { "hashes": [ @@ -1122,37 +1421,133 @@ ], "index": "pypi", "version": "==0.2.8" + }, + "yarl": { + "hashes": [ + "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", + "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", + "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", + "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", + "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", + "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", + "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", + "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", + "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", + "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", + "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", + "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", + "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", + "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", + "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", + "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", + "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", + "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", + "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", + "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", + "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", + "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", + "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", + "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", + "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", + "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", + "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", + "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", + "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", + "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", + "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", + "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", + "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", + "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", + "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", + "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", + "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", + "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", + "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", + "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", + "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", + "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", + "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", + "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", + "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", + "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", + "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", + "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", + "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", + "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", + "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", + "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", + "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", + "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", + "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", + "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", + "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", + "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", + "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", + "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", + "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", + "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", + "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", + "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", + "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", + "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", + "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", + "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", + "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", + "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", + "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", + "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", + "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", + "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", + "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", + "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", + "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", + "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", + "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", + "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", + "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", + "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", + "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", + "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", + "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", + "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", + "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", + "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", + "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", + "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" + ], + "markers": "python_version >= '3.7'", + "version": "==1.9.4" } }, "develop": { "black": { "hashes": [ - "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50", - "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f", - "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e", - "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec", - "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055", - "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3", - "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5", - "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54", - "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b", - "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e", - "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e", - "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba", - "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea", - "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59", - "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d", - "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0", - "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9", - "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a", - "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e", - "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba", - "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2", - "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2" + "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8", + "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8", + "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd", + "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9", + "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31", + "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92", + "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f", + "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29", + "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4", + "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693", + "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218", + "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a", + "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23", + "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0", + "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982", + "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894", + "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540", + "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430", + "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b", + "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2", + "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6", + "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.12.1" + "version": "==24.2.0" }, "cfgv": { "hashes": [ @@ -1197,11 +1592,11 @@ }, "identify": { "hashes": [ - "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d", - "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34" + "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791", + "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e" ], "markers": "python_version >= '3.8'", - "version": "==2.5.33" + "version": "==2.5.35" }, "isort": { "hashes": [ @@ -1247,20 +1642,20 @@ }, "platformdirs": { "hashes": [ - "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380", - "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420" + "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", + "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" ], "markers": "python_version >= '3.8'", - "version": "==4.1.0" + "version": "==4.2.0" }, "pre-commit": { "hashes": [ - "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376", - "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d" + "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c", + "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==3.6.0" + "version": "==3.6.2" }, "pyyaml": { "hashes": [ @@ -1321,19 +1716,19 @@ }, "setuptools": { "hashes": [ - "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05", - "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78" + "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56", + "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" ], "markers": "python_version >= '3.8'", - "version": "==69.0.3" + "version": "==69.1.1" }, "virtualenv": { "hashes": [ - "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3", - "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b" + "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a", + "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197" ], "markers": "python_version >= '3.7'", - "version": "==20.25.0" + "version": "==20.25.1" } } } diff --git a/AHC_app/animals/forms.py b/AHC_app/animals/forms.py index a2b802d..fa3d045 100644 --- a/AHC_app/animals/forms.py +++ b/AHC_app/animals/forms.py @@ -1,9 +1,8 @@ +from animals.models import Animal from django import forms from django.core.validators import MaxLengthValidator, MinLengthValidator from django.db.models import Q -from animals.models import Animal - class AnimalRegisterForm(forms.ModelForm): class Meta: @@ -13,9 +12,7 @@ class Meta: full_name = forms.CharField( validators=[ MinLengthValidator(limit_value=3, message="Minimum 3 characters required."), - MaxLengthValidator( - limit_value=50, message="Maximum 50 characters allowed." - ), + MaxLengthValidator(limit_value=50, message="Maximum 50 characters allowed."), ] ) @@ -26,11 +23,12 @@ def __init__(self, *args, **kwargs): def clean_full_name(self): full_name = self.cleaned_data.get("full_name") - if Animal.objects.filter( - Q(full_name=full_name) & (Q(owner=self.user) | Q(allowed_users=self.user)) - ).exists(): - raise forms.ValidationError( - "An animal with that name is already in your care." - ) + if Animal.objects.filter(Q(full_name=full_name) & (Q(owner=self.user) | Q(allowed_users=self.user))).exists(): + raise forms.ValidationError("An animal with that name is already in your care.") return full_name + + +class PinAnimalForm(forms.Form): + animal_id = forms.CharField(widget=forms.HiddenInput) + action = forms.CharField(widget=forms.HiddenInput) diff --git a/AHC_app/animals/mixins/__init__.py b/AHC_app/animals/mixins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/AHC_app/animals/mixins/animal_owner_permissions.py b/AHC_app/animals/mixins/animal_owner_permissions.py new file mode 100644 index 0000000..378f8c0 --- /dev/null +++ b/AHC_app/animals/mixins/animal_owner_permissions.py @@ -0,0 +1,10 @@ +from animals.models import Animal +from django.contrib.auth.mixins import UserPassesTestMixin + + +class UserPassesOwnershipTestMixin(UserPassesTestMixin): + def test_func(self): + owner = Animal.objects.get(pk=self.kwargs["pk"]).owner + user = self.request.user.profile + + return user == owner diff --git a/AHC_app/animals/models.py b/AHC_app/animals/models.py index 7254cd2..d64a8d1 100644 --- a/AHC_app/animals/models.py +++ b/AHC_app/animals/models.py @@ -7,17 +7,11 @@ class Animal(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) full_name = models.CharField(max_length=50, null=False, blank=False) - short_description = models.CharField( - max_length=250, default=None, blank=True, null=True - ) - long_description = models.CharField( - max_length=2500, default=None, blank=True, null=True - ) + short_description = models.CharField(max_length=250, default=None, blank=True, null=True) + long_description = models.CharField(max_length=2500, default=None, blank=True, null=True) birthdate = models.DateField(null=True, default=None) - profile_image = models.ImageField( - default="profile_pics/pet-care.png", upload_to="profile_pics/animals" - ) + profile_image = models.ImageField(default="profile_pics/pet-care.png", upload_to="profile_pics/animals") creation_date = models.DateTimeField(auto_now_add=True, editable=False) owner = models.ForeignKey( @@ -25,18 +19,9 @@ class Animal(models.Model): ) # dodac okresową notyfikację o braku ownera - przypisuje admin z panelu allowed_users = models.ManyToManyField(UserProfile, related_name="keepers") - first_contact_vet = models.CharField( - max_length=250, default=None, blank=True, null=True - ) + first_contact_vet = models.CharField(max_length=250, default=None, blank=True, null=True) # first_contact_vet = models.ForeignKey(Vet_pofile) - first_contact_medical_place = models.CharField( - max_length=250, default=None, blank=True, null=True - ) + first_contact_medical_place = models.CharField(max_length=250, default=None, blank=True, null=True) # first_contact_medical_place = models.ForeignKey(Place_profile) last_control_visit = models.DateTimeField(null=True, default=None) - - -# biometric_records_history = # relacja jeden do jeden w notatce -# medical_records_history = None # relacja jeden do jeden do notatki -# feeding_records_history = None # relacja jeden do jeden w notatce diff --git a/AHC_app/animals/templates/animals/profile.html b/AHC_app/animals/templates/animals/profile.html index 529136d..79394db 100644 --- a/AHC_app/animals/templates/animals/profile.html +++ b/AHC_app/animals/templates/animals/profile.html @@ -62,7 +62,7 @@

Last records:

- Plan a new visit + Plan a new visit Add a new note View the full timeline
@@ -90,6 +90,21 @@

Common options:

Add biometric record Manage notifications + + {% if not is_pinned %} + Add to Pinned + {% else %} + Remove from Pinned + {% endif %} + +
{% if is_owner %} diff --git a/AHC_app/animals/urls.py b/AHC_app/animals/urls.py index 803bad8..08f4d72 100644 --- a/AHC_app/animals/urls.py +++ b/AHC_app/animals/urls.py @@ -1,6 +1,5 @@ -from django.urls import path - from animals import views as animal_views +from django.urls import path urlpatterns = [ path("create/", animal_views.CreateAnimalView.as_view(), name="animal_create"), @@ -12,4 +11,5 @@ path("/upload-image/", animal_views.ImageUploadView.as_view(), name="upload_image"), path("/manage_keepers/", animal_views.ManageKeepersView.as_view(), name="manage_keepers"), path("animals/", animal_views.StableView.as_view(), name="animals_stable"), + path("pinned-animals/", animal_views.ToPinAnimalsView.as_view(), name="pinned_animals"), ] diff --git a/AHC_app/animals/utils_owner/views.py b/AHC_app/animals/utils_owner/views.py index 06a3a61..963d817 100644 --- a/AHC_app/animals/utils_owner/views.py +++ b/AHC_app/animals/utils_owner/views.py @@ -1,16 +1,21 @@ -from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from animals.mixins.animal_owner_permissions import UserPassesOwnershipTestMixin +from animals.models import Animal +from animals.utils_owner.forms import ( + ChangeBirthdayForm, + ChangeFirstContactForm, + ChangeOwnerForm, + ImageUploadForm, + ManageKeepersForm, +) +from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy from django.views.generic import DeleteView from django.views.generic.edit import FormView from PIL import Image -from animals.models import Animal -from animals.utils_owner.forms import (ChangeBirthdayForm, ChangeFirstContactForm, - ChangeOwnerForm, ImageUploadForm, ManageKeepersForm) - -class AnimalDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): +class AnimalDeleteView(LoginRequiredMixin, UserPassesOwnershipTestMixin, DeleteView): model = Animal template_name = "animals/animal_confirm_delete.html" success_url = reverse_lazy("Homepage") @@ -20,14 +25,8 @@ def get_context_data(self, **kwargs): context["animal_id"] = self.kwargs["pk"] return context - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - return user == owner - - -class ImageUploadView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class ImageUploadView(LoginRequiredMixin, UserPassesOwnershipTestMixin, FormView): template_name = "animals/image.html" form_class = ImageUploadForm @@ -56,14 +55,8 @@ def form_valid(self, form): success_url = reverse("animal_profile", kwargs={"pk": animal_instance.pk}) return redirect(success_url) - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - - return user == owner - -class ChangeOwnerView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class ChangeOwnerView(LoginRequiredMixin, UserPassesOwnershipTestMixin, FormView): template_name = "animals/change_owner.html" form_class = ChangeOwnerForm @@ -71,9 +64,7 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) animal = Animal.objects.get(pk=self.kwargs["pk"]) context["full_name"] = animal.full_name - context["animal_url"] = reverse( - "animal_profile", kwargs={"pk": self.get_form().instance.id} - ) + context["animal_url"] = reverse("animal_profile", kwargs={"pk": self.get_form().instance.id}) return context def get_form_kwargs(self): @@ -96,13 +87,8 @@ def form_valid(self, form): def get_success_url(self): return reverse("animals_stable") - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - return user == owner - -class ManageKeepersView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class ManageKeepersView(LoginRequiredMixin, UserPassesOwnershipTestMixin, FormView): template_name = "animals/manage_keepers.html" form_class = ManageKeepersForm @@ -111,9 +97,7 @@ def get_context_data(self, **kwargs): animal = Animal.objects.get(pk=self.kwargs["pk"]) context["full_name"] = animal.full_name context["allowed_users"] = animal.allowed_users.all() - context["animal_url"] = reverse( - "animal_profile", kwargs={"pk": self.get_form().instance.id} - ) + context["animal_url"] = reverse("animal_profile", kwargs={"pk": self.get_form().instance.id}) return context def get_form_kwargs(self): @@ -131,13 +115,8 @@ def form_valid(self, form): def get_success_url(self): return self.request.path - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - return user == owner - -class ChangeBirthdayView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class ChangeBirthdayView(LoginRequiredMixin, UserPassesOwnershipTestMixin, FormView): form_class = ChangeBirthdayForm template_name = "animals/change_birthday.html" @@ -160,13 +139,8 @@ def form_valid(self, form): success_url = reverse("animal_profile", kwargs={"pk": self.kwargs["pk"]}) return redirect(success_url) - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - return user == owner - -class ChangeFirstContactView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class ChangeFirstContactView(LoginRequiredMixin, UserPassesOwnershipTestMixin, FormView): form_class = ChangeFirstContactForm template_name = "animals/change_first_contact.html" @@ -181,16 +155,9 @@ def get_context_data(self, **kwargs): def form_valid(self, form): animal = get_object_or_404(Animal, pk=self.kwargs["pk"]) animal.first_contact_vet = form.cleaned_data["first_contact_vet"] - animal.first_contact_medical_place = form.cleaned_data[ - "first_contact_medical_place" - ] + animal.first_contact_medical_place = form.cleaned_data["first_contact_medical_place"] animal.save() return super().form_valid(form) def get_success_url(self): return self.request.path - - def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - return user == owner diff --git a/AHC_app/animals/views.py b/AHC_app/animals/views.py index a2d6339..84f1c0b 100644 --- a/AHC_app/animals/views.py +++ b/AHC_app/animals/views.py @@ -1,15 +1,16 @@ +from animals.forms import AnimalRegisterForm, PinAnimalForm +from animals.models import Animal from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db.models import Q +from django.http import JsonResponse from django.urls import reverse from django.utils import timezone -from django.views.generic import TemplateView +from django.views.generic import TemplateView, View from django.views.generic.detail import DetailView from django.views.generic.edit import FormView from medical_notes.models.type_basic_note import MedicalRecord -from animals.forms import AnimalRegisterForm -from animals.models import Animal -from animals.utils_owner.views import * +# from users.models import Profile as UserProfile class CreateAnimalView(LoginRequiredMixin, FormView): @@ -39,9 +40,11 @@ def get_context_data(self, **kwargs): # only for visibility of buttons, do not use as authentication context["is_owner"] = self.object.owner == self.request.user.profile - recent_records = MedicalRecord.objects.filter(animal=self.object).order_by( - "-date_creation" - )[:5] + context["is_pinned"] = context["is_pinned"] = self.request.user.profile.pinned_animals.filter( + pk=self.object.pk + ).exists() + + recent_records = MedicalRecord.objects.filter(animal=self.object).order_by("-date_creation")[:5] context["recent_records"] = recent_records @@ -63,10 +66,33 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) query = Animal.objects.filter( - Q(owner=self.request.user.profile) - | Q(allowed_users=self.request.user.profile) + Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile) ).order_by("-creation_date") context["animals"] = query return context + + +class ToPinAnimalsView(View): + def post(self, request, *args, **kwargs): + print("DUPA1") + + form = PinAnimalForm(request.POST) + print(form) + + current_user = request.user + + if form.is_valid(): + animal_id = form.cleaned_data["animal_id"] + action = form.cleaned_data["action"] + + if action == "add": + animal = Animal.objects.get(id=animal_id) + current_user.pinned_animals.add(animal) + current_user.save() + elif action == "remove": + current_user.pinned_animals.remove(animal_id) + current_user.save() + + return JsonResponse({"status": "success"}) diff --git a/AHC_app/docker-compose.yml b/AHC_app/docker-compose.yml index 0b346e5..7bf9ce5 100644 --- a/AHC_app/docker-compose.yml +++ b/AHC_app/docker-compose.yml @@ -18,6 +18,7 @@ services: - | python manage.py makemigrations python manage.py migrate + python manage.py collectstatic --noinput python manage.py crontab remove python manage.py crontab add python manage.py sync_cronjobs diff --git a/AHC_app/homepage/models.py b/AHC_app/homepage/models.py index 9cc68a4..fab7fc2 100644 --- a/AHC_app/homepage/models.py +++ b/AHC_app/homepage/models.py @@ -1,7 +1,6 @@ from django.contrib.auth.models import User from django.db import models from django.urls import reverse - from homepage.utils import ImageGenerator @@ -9,17 +8,26 @@ class Privilege(models.Model): title = models.CharField(max_length=30) privilege_to_delete_animal = models.BooleanField(default=False) + # TODO: reconsider usage to simplyfy priveliges test mixins + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + raise NotImplementedError + class ProfileBackground(models.Model): title = models.CharField(max_length=30) - # content = models.ImageField(default='AHC_app/static/media/background/background-1169534_1920.png', - # upload_to='AHC_app/static/media/background') content = models.ImageField( default=ImageGenerator.default_profile_image, upload_to="static/media/background", ) + # TODO: reconsider usage + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + raise NotImplementedError + +# TODO: reconsider usage, currently Animal Profile is enough but can be used to manipulate images class AnimalTitle(models.Model): title = models.CharField(max_length=30) content = models.ImageField( @@ -35,6 +43,7 @@ def get_absolute_url(self): return reverse("article-detail", kwargs={"pk": self.pk}) +# TODO: implement to allow set cronjobs from admin panel, must run shell command class CronJob(models.Model): name = models.CharField(max_length=255) command = models.CharField(max_length=255) diff --git a/AHC_app/homepage/templates/homepage/base.html b/AHC_app/homepage/templates/homepage/base.html index 688df28..a1d1cb3 100644 --- a/AHC_app/homepage/templates/homepage/base.html +++ b/AHC_app/homepage/templates/homepage/base.html @@ -53,7 +53,7 @@ {% endif %}