From 37534b31bf3f7963d06e1ee2842f79870445c97c Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Mon, 1 Jan 2024 18:21:27 +0000 Subject: [PATCH] Add CRUD for Drug --- app/urls.py | 23 +- core/__init__.py | 12 +- core/admin.py | 25 +- core/forms.py | 33 +- ...e_drug_entry_sei_source_timing_and_more.py | 162 ++++++ ...ng_comeup_remove_timing_offset_and_more.py | 146 ++++++ ...ame_common_dosage_common_lower_and_more.py | 68 +++ ...roa_timing_roa_alter_experiencedose_roa.py | 30 ++ core/models.py | 183 +++++-- core/templates/base.html | 14 +- core/templates/partials/drug-list.html | 73 +++ core/views/drugs.py | 52 ++ docker-compose.yml | 16 +- mxs/restrictions.py | 109 +++++ mxs/views.py | 461 ++++++++++++++++++ requirements.txt | 2 + 16 files changed, 1355 insertions(+), 54 deletions(-) create mode 100644 core/migrations/0002_action_dosage_drug_entry_sei_source_timing_and_more.py create mode 100644 core/migrations/0003_remove_timing_comeup_remove_timing_offset_and_more.py create mode 100644 core/migrations/0004_rename_common_dosage_common_lower_and_more.py create mode 100644 core/migrations/0005_dosage_roa_timing_roa_alter_experiencedose_roa.py create mode 100644 core/templates/partials/drug-list.html create mode 100644 core/views/drugs.py create mode 100644 mxs/restrictions.py create mode 100644 mxs/views.py diff --git a/app/urls.py b/app/urls.py index b885f25..9ec6016 100644 --- a/app/urls.py +++ b/app/urls.py @@ -20,7 +20,7 @@ from django.contrib.auth.views import LogoutView from django.urls import include, path from two_factor.urls import urlpatterns as tf_urls -from core.views import base, demo, notifications +from core.views import base, demo, drugs, notifications urlpatterns = [ path("__debug__/", include("debug_toolbar.urls")), @@ -40,4 +40,25 @@ urlpatterns = [ notifications.NotificationsUpdate.as_view(), name="notifications_update", ), + path("drugs//", drugs.DrugList.as_view(), name="drugs"), + path( + "drugs//create/", + drugs.DrugCreate.as_view(), + name="drug_create", + ), + path( + "drugs//update//", + drugs.DrugUpdate.as_view(), + name="drug_update", + ), + path( + "drugs//delete//", + drugs.DrugDelete.as_view(), + name="drug_delete", + ), + path( + "drugs/clear/all/", + drugs.DrugClear.as_view(), + name="drug_clear", + ), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/core/__init__.py b/core/__init__.py index d1d0e50..6b9f007 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,14 +1,14 @@ import os -import stripe -from django.conf import settings +# import stripe +# from django.conf import settings os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" # from redis import StrictRedis # r = StrictRedis(unix_socket_path="/var/run/redis/redis.sock", db=0) -if settings.STRIPE_TEST: - stripe.api_key = settings.STRIPE_API_KEY_TEST -else: - stripe.api_key = settings.STRIPE_API_KEY_PROD +# if settings.STRIPE_TEST: +# stripe.api_key = settings.STRIPE_API_KEY_TEST +# else: +# stripe.api_key = settings.STRIPE_API_KEY_PROD diff --git a/core/admin.py b/core/admin.py index 8923aa5..831f8f0 100644 --- a/core/admin.py +++ b/core/admin.py @@ -2,7 +2,20 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .forms import CustomUserCreationForm -from .models import NotificationSettings, User +from .models import ( + SEI, + Action, + Dosage, + Drug, + Effect, + Entry, + Experience, + ExperienceDose, + NotificationSettings, + Source, + Timing, + User, +) # Register your models here. @@ -34,3 +47,13 @@ class NotificationSettingsAdmin(admin.ModelAdmin): admin.site.register(User, CustomUserAdmin) admin.site.register(NotificationSettings, NotificationSettingsAdmin) +admin.site.register(Drug) +admin.site.register(Entry) +admin.site.register(Dosage) +admin.site.register(Timing) +admin.site.register(Effect) +admin.site.register(Action) +admin.site.register(Experience) +admin.site.register(Source) +admin.site.register(SEI) +admin.site.register(ExperienceDose) diff --git a/core/forms.py b/core/forms.py index 32eb5ca..0241385 100644 --- a/core/forms.py +++ b/core/forms.py @@ -3,7 +3,9 @@ from django.contrib.auth.forms import UserCreationForm from django.forms import ModelForm from mixins.restrictions import RestrictedFormMixin -from .models import NotificationSettings, User +from mxs.restrictions import RestrictedFormMixinStaff + +from .models import Drug, NotificationSettings, User # Create your forms here. @@ -47,3 +49,32 @@ class CustomUserCreationForm(UserCreationForm): class Meta: model = User fields = "__all__" + + +class DrugForm(RestrictedFormMixinStaff, ModelForm): + class Meta: + model = Drug + fields = ( + "name", + "drug_class", + "common_name", + "links", + "dosages", + "timings", + "effects", + "actions", + "experiences", + ) + help_texts = { + "name": "Lysergic acid diethylamide, Phenibut", + "drug_class": "Psychedelic, Sedative, Stimulant", + "common_name": "LSD", + "links": "Factsheets, posts", + "dosages": "Dosages, how much to take to get a certain effect", + "timings": ( + "Timings, how long to wait to reach maximum intensity (and " "others)" + ), + "effects": "Effects, what does it do on a subjective level?", + "actions": "Actions, what does it do on an objective level?", + "experiences": "Experiences, what do people experience?", + } diff --git a/core/migrations/0002_action_dosage_drug_entry_sei_source_timing_and_more.py b/core/migrations/0002_action_dosage_drug_entry_sei_source_timing_and_more.py new file mode 100644 index 0000000..73e9b50 --- /dev/null +++ b/core/migrations/0002_action_dosage_drug_entry_sei_source_timing_and_more.py @@ -0,0 +1,162 @@ +# Generated by Django 4.2.8 on 2024-01-01 17:37 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Action', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('site', models.CharField(choices=[('5HT2C', '5-HT2C'), ('5HT2A', '5-HT2A'), ('GABAB', 'GABAB'), ('NMDA', 'NMDA')], max_length=255)), + ('mechanism', models.CharField(choices=[('AGONISM', 'Agonism'), ('ANTAGONISM', 'Antagomism'), ('MODULATION', 'Modulation')], max_length=255)), + ('affinity', models.IntegerField(blank=True)), + ('reversible', models.BooleanField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Dosage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('unit', models.CharField(choices=[('mg', 'mg'), ('g', 'g'), ('ug', 'ug')], max_length=255)), + ('threshold', models.IntegerField()), + ('light', models.IntegerField()), + ('common', models.IntegerField()), + ('strong', models.IntegerField()), + ('heavy', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='Drug', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('drug_class', models.CharField(max_length=255)), + ('common_name', models.CharField(max_length=1024, unique=True)), + ('actions', models.ManyToManyField(to='core.action')), + ('dosages', models.ManyToManyField(to='core.dosage')), + ], + ), + migrations.CreateModel( + name='Entry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.CharField(blank=True, max_length=1024, null=True)), + ('author', models.CharField(blank=True, max_length=255, null=True)), + ('description', models.CharField(blank=True, max_length=1024, null=True)), + ], + ), + migrations.CreateModel( + name='SEI', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(choices=[('PHYSICAL', 'Physical'), ('COGNITIVE', 'Cognitive'), ('VISUAL', 'Visual'), ('AUDITORY', 'Auditory'), ('MULTISENSORY', 'Multi-sensory'), ('TRANSPERSONAL', 'Transpersonal')], default='PHYSICAL', max_length=255)), + ('subtype', models.CharField(choices=[('ENTACTOGENIC', 'Entactogenic (touch-enhancing)'), ('ENTHEOGENIC', 'Entheogenic (spirituality-enhancing)'), ('PSYCHEDELIC', 'Psychedelic (mind-manifesting)'), ('DISSOCIATIVE', 'Dissociative'), ('HALLUCINOGENIC', 'Hallucinogenic (hallucination-inducing)'), ('DELIRIANT', 'Deliriant'), ('PSYCHOTOMIMETIC', 'Psychotomimetic (psychosis-inducing)'), ('STIMULATING', 'Stimulating'), ('SEDATING', 'Sedating'), ('DEPRESSANT', 'Depressant'), ('EUPHORIC', 'Euphoric'), ('ANXIOLYTIC', 'Anxiolytic'), ('ANTIPSYCHOTIC', 'Antipsychotic'), ('PSYCHOSTIMULANT', 'Psychostimulant'), ('EUGEROIC', 'Eugeroic (wakefulness-promoting)'), ('NOOTROPIC', 'Nootropic')], max_length=255)), + ('description', models.CharField(blank=True, max_length=4096, null=True)), + ], + ), + migrations.CreateModel( + name='Source', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('type', models.CharField(choices=[('PSITE', 'Professional pharmaceutical data repository'), ('DWIKI', 'Dedicated peer-reviewed community wiki'), ('CWIKI', 'Peer-reviewed community wiki'), ('WIKI', 'Private wiki'), ('DFORUM', 'Dedicated community forum'), ('FORUM', 'Community forum')], max_length=255)), + ('endpoint', models.CharField(blank=True, max_length=1024, null=True)), + ('score', models.IntegerField(blank=True)), + ], + ), + migrations.CreateModel( + name='Timing', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('unit', models.CharField(choices=[('SECONDS', 'Seconds'), ('MINUTES', 'Minutes'), ('HOURS', 'Hours'), ('DAYS', 'Days :D'), ('WEEKS', 'Weeks :O'), ('MONTHS', 'Months :-|'), ('YEARS', 'Years x_X')], default='HOURS', max_length=255)), + ('onset', models.IntegerField()), + ('comeup', models.IntegerField()), + ('peak', models.IntegerField()), + ('offset', models.IntegerField(blank=True, null=True)), + ('total', models.IntegerField()), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.entry')), + ], + ), + migrations.CreateModel( + name='ExperienceDose', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('seconds_since_start', models.IntegerField()), + ('dose', models.IntegerField()), + ('dose_per_kg', models.IntegerField()), + ('unit', models.CharField(choices=[('mg', 'mg'), ('g', 'g'), ('ug', 'ug')], max_length=255)), + ('roa', models.CharField(max_length=255)), + ('form', models.CharField(max_length=255)), + ('drug', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.drug')), + ], + ), + migrations.CreateModel( + name='Experience', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('body_weight_kg', models.IntegerField(blank=True, null=True)), + ('year_of_experience', models.IntegerField(blank=True, null=True)), + ('gender', models.CharField(blank=True, max_length=255, null=True)), + ('age_at_experience', models.IntegerField(blank=True, null=True)), + ('date_published', models.DateTimeField(blank=True, null=True)), + ('views', models.IntegerField(blank=True, null=True)), + ('date_crawled', models.DateTimeField(blank=True, null=True)), + ('tags', models.CharField(blank=True, max_length=255, null=True)), + ('experience_id', models.IntegerField(blank=True, null=True)), + ('text', models.TextField()), + ('doses', models.ManyToManyField(to='core.experiencedose')), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.entry')), + ], + ), + migrations.AddField( + model_name='entry', + name='source', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.source'), + ), + migrations.CreateModel( + name='Effect', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.entry')), + ('subjective_effects', models.ManyToManyField(to='core.sei')), + ], + ), + migrations.AddField( + model_name='drug', + name='effects', + field=models.ManyToManyField(to='core.effect'), + ), + migrations.AddField( + model_name='drug', + name='experiences', + field=models.ManyToManyField(to='core.experience'), + ), + migrations.AddField( + model_name='drug', + name='links', + field=models.ManyToManyField(to='core.entry'), + ), + migrations.AddField( + model_name='drug', + name='timings', + field=models.ManyToManyField(to='core.timing'), + ), + migrations.AddField( + model_name='dosage', + name='entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.entry'), + ), + migrations.AddField( + model_name='action', + name='entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.entry'), + ), + ] diff --git a/core/migrations/0003_remove_timing_comeup_remove_timing_offset_and_more.py b/core/migrations/0003_remove_timing_comeup_remove_timing_offset_and_more.py new file mode 100644 index 0000000..45ff08c --- /dev/null +++ b/core/migrations/0003_remove_timing_comeup_remove_timing_offset_and_more.py @@ -0,0 +1,146 @@ +# Generated by Django 4.2.8 on 2024-01-01 17:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_action_dosage_drug_entry_sei_source_timing_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='timing', + name='comeup', + ), + migrations.RemoveField( + model_name='timing', + name='offset', + ), + migrations.RemoveField( + model_name='timing', + name='onset', + ), + migrations.RemoveField( + model_name='timing', + name='peak', + ), + migrations.RemoveField( + model_name='timing', + name='total', + ), + migrations.AddField( + model_name='timing', + name='comeup_lower', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='comeup_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='offset_lower', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='timing', + name='offset_upper', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='timing', + name='onset_lower', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='onset_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='peak_lower', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='peak_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='total_lower', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='total_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AlterField( + model_name='dosage', + name='common', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='dosage', + name='heavy', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='dosage', + name='light', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='dosage', + name='strong', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='dosage', + name='threshold', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='drug', + name='actions', + field=models.ManyToManyField(blank=True, to='core.action'), + ), + migrations.AlterField( + model_name='drug', + name='dosages', + field=models.ManyToManyField(blank=True, to='core.dosage'), + ), + migrations.AlterField( + model_name='drug', + name='effects', + field=models.ManyToManyField(blank=True, to='core.effect'), + ), + migrations.AlterField( + model_name='drug', + name='experiences', + field=models.ManyToManyField(blank=True, to='core.experience'), + ), + migrations.AlterField( + model_name='drug', + name='links', + field=models.ManyToManyField(blank=True, to='core.entry'), + ), + migrations.AlterField( + model_name='drug', + name='timings', + field=models.ManyToManyField(blank=True, to='core.timing'), + ), + ] diff --git a/core/migrations/0004_rename_common_dosage_common_lower_and_more.py b/core/migrations/0004_rename_common_dosage_common_lower_and_more.py new file mode 100644 index 0000000..a188bd8 --- /dev/null +++ b/core/migrations/0004_rename_common_dosage_common_lower_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.8 on 2024-01-01 17:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_remove_timing_comeup_remove_timing_offset_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='dosage', + old_name='common', + new_name='common_lower', + ), + migrations.RenameField( + model_name='dosage', + old_name='heavy', + new_name='heavy_lower', + ), + migrations.RenameField( + model_name='dosage', + old_name='light', + new_name='light_lower', + ), + migrations.RenameField( + model_name='dosage', + old_name='strong', + new_name='strong_lower', + ), + migrations.RenameField( + model_name='dosage', + old_name='threshold', + new_name='threshold_lower', + ), + migrations.AddField( + model_name='dosage', + name='common_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='dosage', + name='heavy_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='dosage', + name='light_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='dosage', + name='strong_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + migrations.AddField( + model_name='dosage', + name='threshold_upper', + field=models.FloatField(default=0), + preserve_default=False, + ), + ] diff --git a/core/migrations/0005_dosage_roa_timing_roa_alter_experiencedose_roa.py b/core/migrations/0005_dosage_roa_timing_roa_alter_experiencedose_roa.py new file mode 100644 index 0000000..70d9333 --- /dev/null +++ b/core/migrations/0005_dosage_roa_timing_roa_alter_experiencedose_roa.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.8 on 2024-01-01 18:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_rename_common_dosage_common_lower_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='dosage', + name='roa', + field=models.CharField(choices=[('ORAL', 'Oral'), ('SMOKED', 'Smoked/vaped'), ('INSUFFLATED', 'Insufflated'), ('INJECTED', 'Injected'), ('SUBLINGUAL', 'Sublingual'), ('TRANSDERMAL', 'Transdermal'), ('RECTAL', 'Rectal')], default='ORAL', max_length=255), + preserve_default=False, + ), + migrations.AddField( + model_name='timing', + name='roa', + field=models.CharField(choices=[('ORAL', 'Oral'), ('SMOKED', 'Smoked/vaped'), ('INSUFFLATED', 'Insufflated'), ('INJECTED', 'Injected'), ('SUBLINGUAL', 'Sublingual'), ('TRANSDERMAL', 'Transdermal'), ('RECTAL', 'Rectal')], default='ORAL', max_length=255), + preserve_default=False, + ), + migrations.AlterField( + model_name='experiencedose', + name='roa', + field=models.CharField(choices=[('ORAL', 'Oral'), ('SMOKED', 'Smoked/vaped'), ('INSUFFLATED', 'Insufflated'), ('INJECTED', 'Injected'), ('SUBLINGUAL', 'Sublingual'), ('TRANSDERMAL', 'Transdermal'), ('RECTAL', 'Rectal')], max_length=255), + ), + ] diff --git a/core/models.py b/core/models.py index f036ac1..f38681e 100644 --- a/core/models.py +++ b/core/models.py @@ -19,6 +19,16 @@ MECHANISM_CHOICES = ( ("MODULATION", "Modulation"), ) +ROA_CHOICES = ( + ("ORAL", "Oral"), + ("SMOKED", "Smoked/vaped"), + ("INSUFFLATED", "Insufflated"), + ("INJECTED", "Injected"), + ("SUBLINGUAL", "Sublingual"), + ("TRANSDERMAL", "Transdermal"), + ("RECTAL", "Rectal"), +) + SOURCE_TYPE_CHOICES = ( ("PSITE", "Professional pharmaceutical data repository"), ("DWIKI", "Dedicated peer-reviewed community wiki"), @@ -53,6 +63,30 @@ SEI_TYPE_CHOICES = ( ("TRANSPERSONAL", "Transpersonal"), ) +SEI_SUBTYPE_CHOICES = ( + # Common psychedelic subtypes + ("ENTACTOGENIC", "Entactogenic (touch-enhancing)"), + ("ENTHEOGENIC", "Entheogenic (spirituality-enhancing)"), + ("PSYCHEDELIC", "Psychedelic (mind-manifesting)"), + ("DISSOCIATIVE", "Dissociative"), + ("HALLUCINOGENIC", "Hallucinogenic (hallucination-inducing)"), + # Unpleasant psychedelic-esque subtypes + ("DELIRIANT", "Deliriant"), + ("PSYCHOTOMIMETIC", "Psychotomimetic (psychosis-inducing)"), + # Common recreational drug subtypes + ("STIMULATING", "Stimulating"), + ("SEDATING", "Sedating"), + ("DEPRESSANT", "Depressant"), + ("EUPHORIC", "Euphoric"), + # Common pharmaceutical subtypes + ("ANXIOLYTIC", "Anxiolytic"), + ("ANTIPSYCHOTIC", "Antipsychotic"), + # Common nootropic subtypes + ("PSYCHOSTIMULANT", "Psychostimulant"), + ("EUGEROIC", "Eugeroic (wakefulness-promoting)"), + ("NOOTROPIC", "Nootropic"), +) + class User(AbstractUser): # Stripe customer ID @@ -99,11 +133,14 @@ class Entry(models.Model): Used to gather conflicting information and store it coherently. """ - source = models.ForeignKey(Source) + source = models.ForeignKey(Source, on_delete=models.CASCADE) # Slug of the article on the Source slug = models.CharField(max_length=1024, null=True, blank=True) + # Authorship information, if present + author = models.CharField(max_length=255, null=True, blank=True) + # Extra information can be added description = models.CharField(max_length=1024, null=True, blank=True) @@ -114,26 +151,33 @@ class Dosage(models.Model): Linked to Entry to analyse conflicting records. """ - entry = models.ForeignKey("core.Entry") + entry = models.ForeignKey("core.Entry", on_delete=models.CASCADE) + + roa = models.CharField(max_length=255, choices=ROA_CHOICES) # Unit of mass as drugs are diverse # Dosages varying between micrograms and grams unit = models.CharField(max_length=255, choices=DOSAGE_UNIT_CHOICES) # I can no longer say I am sober, but it is slight - threshold = models.IntegerField() + threshold_lower = models.FloatField() + threshold_upper = models.FloatField() # Light - light = models.IntegerField() + light_lower = models.FloatField() + light_upper = models.FloatField() # Average dose for a user - common = models.IntegerField() + common_lower = models.FloatField() + common_upper = models.FloatField() # Strong intensity, many sober activities may become impossible - strong = models.IntegerField() + strong_lower = models.FloatField() + strong_upper = models.FloatField() # Highest intensity - heavy = models.IntegerField() + heavy_lower = models.FloatField() + heavy_upper = models.FloatField() class Timing(models.Model): @@ -142,7 +186,9 @@ class Timing(models.Model): Linked to Entry to analyse conflicting records. """ - entry = models.ForeignKey("core.Entry") + entry = models.ForeignKey("core.Entry", on_delete=models.CASCADE) + + roa = models.CharField(max_length=255, choices=ROA_CHOICES) # Unit of time as drugs are diverse # Half-lives varying between seconds and months @@ -151,19 +197,24 @@ class Timing(models.Model): ) # It has just now begin, I can no longer say I am sober - onset = models.IntegerField() + onset_lower = models.FloatField() + onset_upper = models.FloatField() # The intensity is accelerating - comeup = models.IntegerField() + comeup_lower = models.FloatField() + comeup_upper = models.FloatField() # The maximum intensity has been reached # How long this state occurs - peak = models.IntegerField() + peak_lower = models.FloatField() + peak_upper = models.FloatField() # How long it takes to get back to baseline - offset = models.IntegerField(null=True, blank=True) + offset_lower = models.FloatField(null=True, blank=True) + offset_upper = models.FloatField(null=True, blank=True) - total = models.IntegerField() + total_lower = models.FloatField() + total_upper = models.FloatField() class SEI(models.Model): @@ -178,6 +229,11 @@ class SEI(models.Model): max_length=255, choices=SEI_TYPE_CHOICES, default="PHYSICAL" ) + subtype = models.CharField( + max_length=255, + choices=SEI_SUBTYPE_CHOICES, + ) + # WIP: consider euphoric, depressant, relaxant # Specify how @@ -191,7 +247,7 @@ class Effect(models.Model): Linked to Entry to analyse conflicting records. """ - entry = models.ForeignKey("core.Entry") + entry = models.ForeignKey("core.Entry", on_delete=models.CASCADE) # List of subjective effects, since they would likely be from the same entry subjective_effects = models.ManyToManyField(SEI) @@ -205,7 +261,7 @@ class Action(models.Model): Linked to Entry to analyse conflicting records. """ - entry = models.ForeignKey("core.Entry") + entry = models.ForeignKey("core.Entry", on_delete=models.CASCADE) # Site - like 5HT2A for LSD site = models.CharField(max_length=255, choices=SITE_CHOICES) @@ -216,6 +272,73 @@ class Action(models.Model): # Free integer for binding affinity affinity = models.IntegerField(blank=True) + # For MAOI, SSRI + reversible = models.BooleanField(blank=True, null=True) + + # MAOI A (I) + # Site: Monoamine Oxidase A + # Mechanism: Inhibition + # Affinity: Permanent (999999) + + # Moclobemide + # Site: Monoamine Oxidase A + # Mechanism: Inhibition + # Affinity: Reversible (1) + + +class ExperienceDose(models.Model): + # Seconds since the start of the experiment + # To be converted to T+00:22 format + seconds_since_start = models.IntegerField() + + # Dosage, integer + dose = models.IntegerField() + + # Dose per kg of body weight + dose_per_kg = models.IntegerField() + + # Unit of mass as drugs are diverse + unit = models.CharField(max_length=255, choices=DOSAGE_UNIT_CHOICES) + + # Route of administration, oral, smoked, injected, etc + roa = models.CharField(max_length=255, choices=ROA_CHOICES) + + # The drug that was taken, linked to our database + drug = models.ForeignKey("core.Drug", on_delete=models.CASCADE) + + # The form of the drug: pills, powder, crystals, tabs, etc + form = models.CharField(max_length=255) + + # TODO: Subjective effects (use AI/ML) + # TODO: Sentiment analysis (use AI/ML) + # TODO: Time-based effects (use AI/ML) + + +class Experience(models.Model): + """ + Registers a subjective experience from a user. + """ + + # Where did the data come from? + entry = models.ForeignKey("core.Entry", on_delete=models.CASCADE) + + # List of the doses and drugs taken + doses = models.ManyToManyField(ExperienceDose) + + # Erowid-specific fields + body_weight_kg = models.IntegerField(blank=True, null=True) + year_of_experience = models.IntegerField(blank=True, null=True) + gender = models.CharField(max_length=255, blank=True, null=True) + age_at_experience = models.IntegerField(blank=True, null=True) + date_published = models.DateTimeField(blank=True, null=True) + views = models.IntegerField(blank=True, null=True) + date_crawled = models.DateTimeField(blank=True, null=True) + tags = models.CharField(max_length=255, blank=True, null=True) + experience_id = models.IntegerField(blank=True, null=True) + + # Description of the experience + text = models.TextField() + class Drug(models.Model): """ @@ -225,42 +348,34 @@ class Drug(models.Model): # Lysergic acid diethylamide, Phenibut name = models.CharField(max_length=255, unique=True) - # Psychedelic, Sedative + # Psychedelic, Sedative, Stimulant drug_class = models.CharField(max_length=255) # LSD common_name = models.CharField(max_length=1024, unique=True) # Factsheets, posts - links = models.ManyToManyField(Entry) + links = models.ManyToManyField(Entry, blank=True) # Dosages, how much to take to get a certain effect - dosages = models.ManyToManyField(Dosage) + dosages = models.ManyToManyField(Dosage, blank=True) # Timings, how long to wait to reach maximum intensity (and others) - timings = models.ManyToManyField(Timing) + timings = models.ManyToManyField(Timing, blank=True) # Effects, what does it do on a subjective level? - effects = models.ManyToManyField(Effect) + effects = models.ManyToManyField(Effect, blank=True) # Actions, what does it do on an objective level? - actions = models.ManyToManyField(Action) + actions = models.ManyToManyField(Action, blank=True) + + # Experiences, what do people experience? + experiences = models.ManyToManyField(Experience, blank=True) # class Perms(models.Model): # class Meta: # permissions = ( -# ("bypass_hashing", "Can bypass field hashing"), # -# ("bypass_blacklist", "Can bypass the blacklist"), # -# ("bypass_encryption", "Can bypass field encryption"), # -# ("bypass_obfuscation", "Can bypass field obfuscation"), # -# ("bypass_delay", "Can bypass data delay"), # -# ("bypass_randomisation", "Can bypass data randomisation"), # -# ("post_irc", "Can post to IRC"), -# ("post_discord", "Can post to Discord"), -# ("query_search", "Can search with query strings"), # -# ("use_insights", "Can use the Insights page"), -# ("index_int", "Can use the internal index"), -# ("index_meta", "Can use the meta index"), -# ("restricted_sources", "Can access restricted sources"), +# ("permission_name", "Permission description"), + # ) diff --git a/core/templates/base.html b/core/templates/base.html index 07ef8d0..c8319d2 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -208,8 +208,8 @@