Upgrade Bulma and begin drug info screen and favourites
This commit is contained in:
parent
4c8411b863
commit
46b1858897
|
@ -61,4 +61,5 @@ DRUGS_DEFAULT_PARAMS = {
|
||||||
"size": "15",
|
"size": "15",
|
||||||
"sorting": "desc",
|
"sorting": "desc",
|
||||||
"source": "substances",
|
"source": "substances",
|
||||||
|
"index": "main",
|
||||||
}
|
}
|
||||||
|
|
31
app/urls.py
31
app/urls.py
|
@ -20,7 +20,7 @@ from django.contrib.auth.views import LogoutView
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from two_factor.urls import urlpatterns as tf_urls
|
from two_factor.urls import urlpatterns as tf_urls
|
||||||
|
|
||||||
from core.views import base, demo, drugs, notifications, search
|
from core.views import base, demo, drugs, favourites, notifications, search
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("__debug__/", include("debug_toolbar.urls")),
|
path("__debug__/", include("debug_toolbar.urls")),
|
||||||
|
@ -57,6 +57,11 @@ urlpatterns = [
|
||||||
drugs.DrugDelete.as_view(),
|
drugs.DrugDelete.as_view(),
|
||||||
name="drug_delete",
|
name="drug_delete",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"drugs/<str:type>/detail/<str:pk>/",
|
||||||
|
drugs.DrugDetail.as_view(),
|
||||||
|
name="drug_detail",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"drugs/clear/all/",
|
"drugs/clear/all/",
|
||||||
drugs.DrugClear.as_view(),
|
drugs.DrugClear.as_view(),
|
||||||
|
@ -70,4 +75,28 @@ urlpatterns = [
|
||||||
# Drug search
|
# Drug search
|
||||||
path("search/", search.DrugsTableView.as_view(), name="search"),
|
path("search/", search.DrugsTableView.as_view(), name="search"),
|
||||||
path("search/partial/", search.DrugsTableView.as_view(), name="search_partial"),
|
path("search/partial/", search.DrugsTableView.as_view(), name="search_partial"),
|
||||||
|
# Favourites
|
||||||
|
path(
|
||||||
|
"favourites/<str:type>/", favourites.FavouriteList.as_view(), name="favourites"
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"favourites/<str:type>/create/",
|
||||||
|
favourites.FavouriteCreate.as_view(),
|
||||||
|
name="favourite_create",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"favourites/<str:type>/update/<str:pk>/",
|
||||||
|
favourites.FavouriteUpdate.as_view(),
|
||||||
|
name="favourite_update",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"favourites/<str:type>/delete/<str:pk>/",
|
||||||
|
favourites.FavouriteDelete.as_view(),
|
||||||
|
name="favourite_delete",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"favourites/<str:type>/detail/<str:pk>/",
|
||||||
|
favourites.FavouriteDetail.as_view(),
|
||||||
|
name="favourite_detail",
|
||||||
|
),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
|
@ -11,6 +11,7 @@ from .models import (
|
||||||
Entry,
|
Entry,
|
||||||
Experience,
|
Experience,
|
||||||
ExperienceDose,
|
ExperienceDose,
|
||||||
|
Favourite,
|
||||||
NotificationSettings,
|
NotificationSettings,
|
||||||
Source,
|
Source,
|
||||||
Timing,
|
Timing,
|
||||||
|
@ -57,3 +58,4 @@ admin.site.register(Experience)
|
||||||
admin.site.register(Source)
|
admin.site.register(Source)
|
||||||
admin.site.register(SEI)
|
admin.site.register(SEI)
|
||||||
admin.site.register(ExperienceDose)
|
admin.site.register(ExperienceDose)
|
||||||
|
admin.site.register(Favourite)
|
||||||
|
|
|
@ -21,6 +21,7 @@ class PsychWikiClient(GraphQLClient, BaseClient):
|
||||||
Store the data in the database.
|
Store the data in the database.
|
||||||
"""
|
"""
|
||||||
for drug in data["substances"]:
|
for drug in data["substances"]:
|
||||||
|
print("DRUG ITER", drug)
|
||||||
try:
|
try:
|
||||||
drug_obj = Drug.objects.get(name=drug["name"])
|
drug_obj = Drug.objects.get(name=drug["name"])
|
||||||
except Drug.DoesNotExist:
|
except Drug.DoesNotExist:
|
||||||
|
@ -89,6 +90,7 @@ class PsychWikiClient(GraphQLClient, BaseClient):
|
||||||
)
|
)
|
||||||
if created or dosage not in drug_obj.dosages.all():
|
if created or dosage not in drug_obj.dosages.all():
|
||||||
drug_obj.dosages.add(dosage)
|
drug_obj.dosages.add(dosage)
|
||||||
|
print("YES DOSAGE", drug_obj.dosages)
|
||||||
|
|
||||||
# Parsing timing information
|
# Parsing timing information
|
||||||
timing = roa["duration"]
|
timing = roa["duration"]
|
||||||
|
|
|
@ -89,6 +89,8 @@ def drug_query(request, query_params, size=None, tags=None):
|
||||||
|
|
||||||
# Q/T - Query/Tags
|
# Q/T - Query/Tags
|
||||||
result = run_query(query_params, tags, size, sources, ranges, sort)
|
result = run_query(query_params, tags, size, sources, ranges, sort)
|
||||||
|
for x in result:
|
||||||
|
print(x.dosages)
|
||||||
rtrn = {
|
rtrn = {
|
||||||
"object_list": result,
|
"object_list": result,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ from mixins.restrictions import RestrictedFormMixin
|
||||||
|
|
||||||
from mxs.restrictions import RestrictedFormMixinStaff
|
from mxs.restrictions import RestrictedFormMixinStaff
|
||||||
|
|
||||||
from .models import Drug, NotificationSettings, User
|
from .models import Drug, Favourite, NotificationSettings, User
|
||||||
|
|
||||||
# Create your forms here.
|
# Create your forms here.
|
||||||
|
|
||||||
|
@ -78,3 +78,20 @@ class DrugForm(RestrictedFormMixinStaff, ModelForm):
|
||||||
"actions": "Actions, what does it do on an objective level?",
|
"actions": "Actions, what does it do on an objective level?",
|
||||||
"experiences": "Experiences, what do people experience?",
|
"experiences": "Experiences, what do people experience?",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteForm(RestrictedFormMixin, ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Favourite
|
||||||
|
fields = (
|
||||||
|
"nickname",
|
||||||
|
"name",
|
||||||
|
"drug_class",
|
||||||
|
"common_name",
|
||||||
|
)
|
||||||
|
help_texts = {
|
||||||
|
"nickname": "Call it whatever you like.",
|
||||||
|
"name": "Lysergic acid diethylamide, Phenibut",
|
||||||
|
"drug_class": "Psychedelic, Sedative, Stimulant",
|
||||||
|
"common_name": "LSD",
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-05-17 19:01
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0009_alter_drug_common_name_alter_drug_drug_class'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Price',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('amount_mg', models.IntegerField()),
|
||||||
|
('price_gbp', models.FloatField()),
|
||||||
|
('note', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FavouriteDrug',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('nickname', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('drug_class', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('common_name', models.CharField(blank=True, max_length=1024, null=True)),
|
||||||
|
('actions', models.ManyToManyField(blank=True, to='core.action')),
|
||||||
|
('dosages', models.ManyToManyField(blank=True, to='core.dosage')),
|
||||||
|
('effects', models.ManyToManyField(blank=True, to='core.effect')),
|
||||||
|
('experiences', models.ManyToManyField(blank=True, to='core.experience')),
|
||||||
|
('links', models.ManyToManyField(blank=True, to='core.entry')),
|
||||||
|
('original', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.drug')),
|
||||||
|
('timings', models.ManyToManyField(blank=True, to='core.timing')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('prices', models.ManyToManyField(blank=True, null=True, to='core.price')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-05-17 19:02
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0010_price_favouritedrug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='FavouriteDrug',
|
||||||
|
new_name='Favourite',
|
||||||
|
),
|
||||||
|
]
|
|
@ -188,7 +188,7 @@ class Dosage(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
text = (
|
text = (
|
||||||
f"{self.threshold_lower} {self.light_lower} {self.common_lower} "
|
f"{self.threshold_lower} {self.light_lower} {self.common_lower} "
|
||||||
"{self.strong_lower} {self.heavy_lower}"
|
f"{self.strong_lower} {self.heavy_lower}"
|
||||||
)
|
)
|
||||||
return f"{self.roa} {text} ({self.unit})"
|
return f"{self.roa} {text} ({self.unit})"
|
||||||
|
|
||||||
|
@ -409,6 +409,69 @@ class Drug(models.Model):
|
||||||
return f"{self.name} ({self.common_name})"
|
return f"{self.name} ({self.common_name})"
|
||||||
|
|
||||||
|
|
||||||
|
class Price(models.Model):
|
||||||
|
"""
|
||||||
|
Price of a drug.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
amount_mg = models.IntegerField()
|
||||||
|
price_gbp = models.FloatField()
|
||||||
|
note = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
# class Stack: references, times
|
||||||
|
# class StackUnit: reference, times, dose_mg
|
||||||
|
|
||||||
|
|
||||||
|
class Favourite(models.Model):
|
||||||
|
"""
|
||||||
|
Model of a drug. Owned by a user and customisable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
# Nickname
|
||||||
|
nickname = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
# Prices, how much certain mass of this substance costs
|
||||||
|
prices = models.ManyToManyField(Price, blank=True, null=True)
|
||||||
|
|
||||||
|
# Internals
|
||||||
|
original = models.ForeignKey(Drug, on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
|
||||||
|
# Below duplicates Drug
|
||||||
|
# Lysergic acid diethylamide, Phenibut
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
# Psychedelic, Sedative, Stimulant
|
||||||
|
drug_class = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
# LSD
|
||||||
|
common_name = models.CharField(max_length=1024, blank=True, null=True)
|
||||||
|
|
||||||
|
# Factsheets, posts
|
||||||
|
links = models.ManyToManyField(Entry, blank=True)
|
||||||
|
|
||||||
|
# Dosages, how much to take to get a certain effect
|
||||||
|
dosages = models.ManyToManyField(Dosage, blank=True)
|
||||||
|
|
||||||
|
# Timings, how long to wait to reach maximum intensity (and others)
|
||||||
|
timings = models.ManyToManyField(Timing, blank=True)
|
||||||
|
|
||||||
|
# Effects, what does it do on a subjective level?
|
||||||
|
effects = models.ManyToManyField(Effect, blank=True)
|
||||||
|
|
||||||
|
# Actions, what does it do on an objective level?
|
||||||
|
actions = models.ManyToManyField(Action, blank=True)
|
||||||
|
|
||||||
|
# Experiences, what do people experience?
|
||||||
|
experiences = models.ManyToManyField(Experience, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} ({self.common_name})"
|
||||||
|
|
||||||
|
|
||||||
# class Perms(models.Model):
|
# class Perms(models.Model):
|
||||||
# class Meta:
|
# class Meta:
|
||||||
# permissions = (
|
# permissions = (
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -209,6 +209,12 @@
|
||||||
<a class="navbar-item" href="{% url 'home' %}">
|
<a class="navbar-item" href="{% url 'home' %}">
|
||||||
Home
|
Home
|
||||||
</a>
|
</a>
|
||||||
|
<a class="navbar-item" href="{% url 'home' %}">
|
||||||
|
Search
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="{% url 'favourites' type='page' %}">
|
||||||
|
Favourites
|
||||||
|
</a>
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser %}
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a class="navbar-link">
|
<a class="navbar-link">
|
||||||
|
@ -225,9 +231,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="navbar-item" href="{% url 'home' %}">
|
|
||||||
Search
|
|
||||||
</a>
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
{% load pretty %}
|
||||||
|
|
||||||
|
{% load cache %}
|
||||||
|
|
||||||
|
{% cache 600 favourite_detail request.user.id object %}
|
||||||
|
{% include 'mixins/partials/notify.html' %}
|
||||||
|
|
||||||
|
{% if object is not None %}
|
||||||
|
<h1 class="title">{{ object.name }} - {{ object.nickname }} - {{ object.common_name }}</h1>
|
||||||
|
<p class="subtitle"><strong>{{ object.drug_class }}</strong></p>
|
||||||
|
<div class="block">
|
||||||
|
<a class="button is-info" href="#">Prices</a>
|
||||||
|
<a class="button is-info" href="#">More info</a>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="cell">
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Dosage</h2>
|
||||||
|
<ul>
|
||||||
|
{% for dose in object.dosages.all %}
|
||||||
|
<li>{{ dose }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Timing</h2>
|
||||||
|
<ul>
|
||||||
|
{% for timing in object.timings.all %}
|
||||||
|
<li>{{ timing }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Links</h2>
|
||||||
|
<ul>
|
||||||
|
{% for link in object.links.all %}
|
||||||
|
<li>{{ link }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="cell">
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Actions</h2>
|
||||||
|
<ul>
|
||||||
|
{% for action in object.actions.all %}
|
||||||
|
<li>{{ action }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Effects</h2>
|
||||||
|
<ul>
|
||||||
|
{% for effect in object.effects.all %}
|
||||||
|
<li>{{ effect }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">Experiences</h2>
|
||||||
|
<ul>
|
||||||
|
{% for exp in object.experiences.all %}
|
||||||
|
<li>{{ exp }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endcache %}
|
|
@ -64,6 +64,15 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<a href="{% url 'drug_detail' type='page' pk=item.id %}"><button
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-eye"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Favourite' as last %}
|
||||||
|
{% include 'mixins/partials/notify.html' %}
|
||||||
|
{% cache 600 objects_favourittes request.user.id object_list last %}
|
||||||
|
<table
|
||||||
|
class="table is-fullwidth is-hoverable"
|
||||||
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
id="{{ context_object_name }}-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-trigger="{{ context_object_name_singular }}Event from:body"
|
||||||
|
hx-get="{{ list_url }}">
|
||||||
|
<thead>
|
||||||
|
<th>id</th>
|
||||||
|
<th>nickname</th>
|
||||||
|
<th>prices</th>
|
||||||
|
<th>name</th>
|
||||||
|
<th>drug class</th>
|
||||||
|
<th>common name</th>
|
||||||
|
<th>links</th>
|
||||||
|
<th>dosages</th>
|
||||||
|
<th>timings</th>
|
||||||
|
<th>effects</th>
|
||||||
|
<th>actions</th>
|
||||||
|
<th>experiences</th>
|
||||||
|
<th>actions</th>
|
||||||
|
</thead>
|
||||||
|
{% for item in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ item.id }}</td>
|
||||||
|
<td>{{ item.nickname }}</td>
|
||||||
|
<td>{{ item.prices.count }}</td>
|
||||||
|
<td>{{ item.name }}</td>
|
||||||
|
<td>{{ item.drug_class }}</td>
|
||||||
|
<td>{{ item.common_name }}</td>
|
||||||
|
<td>{{ item.links.count }}</td>
|
||||||
|
<td>{{ item.dosages.count }}</td>
|
||||||
|
<td>{{ item.timings.count }}</td>
|
||||||
|
<td>{{ item.effects.count }}</td>
|
||||||
|
<td>{{ item.actions.count }}</td>
|
||||||
|
<td>{{ item.experiences.count }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="buttons">
|
||||||
|
<button
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-get="{% url 'favourite_update' type=type pk=item.id %}"
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#{{ type }}s-here"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-pencil"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-delete="{% url 'favourite_delete' type=type pk=item.id %}"
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#modals-here"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-confirm="Are you sure you wish to delete {{ item.name }}?"
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a href="{% url 'favourite_detail' type='page' pk=item.id %}"><button
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-eye"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
{% endcache %}
|
|
@ -5,523 +5,289 @@
|
||||||
{% load urlsafe %}
|
{% load urlsafe %}
|
||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
|
||||||
{% cache 3600 results_table_full request.user.id table %}
|
{# cache 3600 results_table_full request.user.id table #}
|
||||||
{% block table-wrapper %}
|
{% block table-wrapper %}
|
||||||
<script src="{% static 'js/column-shifter.js' %}"></script>
|
<script src="{% static 'js/column-shifter.js' %}"></script>
|
||||||
<div id="drilldown-table" class="column-shifter-container" style="position:relative; z-index:1;">
|
<div id="drilldown-table" class="column-shifter-container" style="position:relative; z-index:1;">
|
||||||
{% block table %}
|
{% block table %}
|
||||||
<div class="nowrap-parent">
|
<div class="nowrap-parent">
|
||||||
<div class="nowrap-child">
|
<div class="nowrap-child">
|
||||||
<div class="dropdown" id="dropdown">
|
<div class="dropdown" id="dropdown">
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<button id="dropdown-trigger" class="button dropdown-toggle" aria-haspopup="true" aria-controls="dropdown-menu">
|
<button id="dropdown-trigger" class="button dropdown-toggle" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
<span>Show/hide fields</span>
|
<span>Show/hide fields</span>
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||||
<div class="dropdown-content" style="position:absolute; z-index:2;">
|
<div class="dropdown-content" style="position:absolute; z-index:2;">
|
||||||
{% for column in table.columns %}
|
{% for column in table.columns %}
|
||||||
{% if column.name in show %}
|
{% if column.name in show %}
|
||||||
<a class="btn-shift-column dropdown-item"
|
<a class="btn-shift-column dropdown-item"
|
||||||
data-td-class="{{ column.name }}"
|
data-td-class="{{ column.name }}"
|
||||||
data-state="on"
|
data-state="on"
|
||||||
{% if not forloop.last %} style="border-bottom:1px solid #ccc;" {%endif %}
|
{% if not forloop.last %} style="border-bottom:1px solid #ccc;" {%endif %}
|
||||||
data-table-class-container="drilldown-table">
|
data-table-class-container="drilldown-table">
|
||||||
<span class="check icon" data-tooltip="Visible" style="display:none;">
|
<span class="check icon" data-tooltip="Visible" style="display:none;">
|
||||||
<i class="fa-solid fa-check"></i>
|
<i class="fa-solid fa-check"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="uncheck icon" data-tooltip="Hidden" style="display:none;">
|
<span class="uncheck icon" data-tooltip="Hidden" style="display:none;">
|
||||||
<i class="fa-solid fa-xmark"></i>
|
<i class="fa-solid fa-xmark"></i>
|
||||||
</span>
|
</span>
|
||||||
{{ column.header }}
|
{{ column.header }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nowrap-child">
|
|
||||||
<span id="loader" class="button is-light has-text-link is-loading">Static</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<div class="nowrap-child">
|
||||||
var dropdown_button = document.getElementById("dropdown-trigger");
|
<span id="loader" class="button is-light has-text-link is-loading">Static</span>
|
||||||
var dropdown = document.getElementById("dropdown");
|
</div>
|
||||||
dropdown_button.addEventListener('click', function(e) {
|
</div>
|
||||||
// elements[i].preventDefault();
|
<script>
|
||||||
dropdown.classList.toggle('is-active');
|
var dropdown_button = document.getElementById("dropdown-trigger");
|
||||||
});
|
var dropdown = document.getElementById("dropdown");
|
||||||
|
dropdown_button.addEventListener('click', function(e) {
|
||||||
|
// elements[i].preventDefault();
|
||||||
|
dropdown.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<div id="table-container" style="display:none;">
|
<div id="table-container" style="display:none;">
|
||||||
<table {% render_attrs table.attrs class="table drilldown-results-table is-fullwidth" %}>
|
<table {% render_attrs table.attrs class="table drilldown-results-table is-fullwidth" %}>
|
||||||
{% block table.thead %}
|
{% block table.thead %}
|
||||||
{% if table.show_header %}
|
{% if table.show_header %}
|
||||||
<thead {% render_attrs table.attrs.thead class="" %}>
|
<thead {% render_attrs table.attrs.thead class="" %}>
|
||||||
{% block table.thead.row %}
|
{% block table.thead.row %}
|
||||||
<tr>
|
<tr>
|
||||||
{% for column in table.columns %}
|
{% for column in table.columns %}
|
||||||
{% if column.name in show %}
|
{% if column.name in show %}
|
||||||
{% block table.thead.th %}
|
{% block table.thead.th %}
|
||||||
<th class="orderable {{ column.name }}">
|
<th class="orderable {{ column.name }}">
|
||||||
<div class="nowrap-parent">
|
<div class="nowrap-parent">
|
||||||
{% if column.orderable %}
|
{% if column.orderable %}
|
||||||
<div class="nowrap-child">
|
<div class="nowrap-child">
|
||||||
{% if column.is_ordered %}
|
{% if column.is_ordered %}
|
||||||
{% is_descending column.order_by as descending %}
|
{% is_descending column.order_by as descending %}
|
||||||
{% if descending %}
|
{% if descending %}
|
||||||
<span class="icon" aria-hidden="true">{% block table.desc_icon %}<i class="fa-solid fa-sort-down"></i>{% endblock table.desc_icon %}</span>
|
<span class="icon" aria-hidden="true">{% block table.desc_icon %}<i class="fa-solid fa-sort-down"></i>{% endblock table.desc_icon %}</span>
|
||||||
{% else %}
|
|
||||||
<span class="icon" aria-hidden="true">{% block table.asc_icon %}<i class="fa-solid fa-sort-up"></i>{% endblock table.asc_icon %}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="icon" aria-hidden="true">{% block table.orderable_icon %}<i class="fa-solid fa-sort"></i>{% endblock table.orderable_icon %}</span>
|
<span class="icon" aria-hidden="true">{% block table.asc_icon %}<i class="fa-solid fa-sort-up"></i>{% endblock table.asc_icon %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
{% else %}
|
||||||
<div class="nowrap-child">
|
<span class="icon" aria-hidden="true">{% block table.orderable_icon %}<i class="fa-solid fa-sort"></i>{% endblock table.orderable_icon %}</span>
|
||||||
<a
|
{% endif %}
|
||||||
hx-get="search/partial/{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}&{{ uri }}"
|
</div>
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
<div class="nowrap-child">
|
||||||
hx-trigger="click"
|
|
||||||
hx-target="#drilldown-table"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
hx-indicator="#spinner"
|
|
||||||
style="cursor: pointer;">
|
|
||||||
{{ column.header }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="nowrap-child">
|
|
||||||
{{ column.header }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
{% endblock table.thead.th %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% endblock table.thead.row %}
|
|
||||||
</thead>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock table.thead %}
|
|
||||||
{% block table.tbody %}
|
|
||||||
<tbody {{ table.attrs.tbody.as_html }}>
|
|
||||||
{% for row in table.paginated_rows %}
|
|
||||||
{% block table.tbody.row %}
|
|
||||||
{% if row.cells.type == 'control' %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<span class="icon has-text-grey" data-tooltip="Hidden">
|
|
||||||
<i class="fa-solid fa-file-slash"></i>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p class="has-text-grey">Hidden {{ row.cells.hidden }} similar result{% if row.cells.hidden > 1%}s{% endif %}</p>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr class="
|
|
||||||
{% if row.cells.exemption == True %}has-background-grey-lighter
|
|
||||||
{% elif cell == 'join' %}has-background-success-light
|
|
||||||
{% elif cell == 'quit' %}has-background-danger-light
|
|
||||||
{% elif cell == 'kick' %}has-background-danger-light
|
|
||||||
{% elif cell == 'part' %}has-background-warning-light
|
|
||||||
{% elif cell == 'mode' %}has-background-info-light
|
|
||||||
{% endif %}">
|
|
||||||
{% for column, cell in row.items %}
|
|
||||||
{% if column.name in show %}
|
|
||||||
{% block table.tbody.td %}
|
|
||||||
{% if cell == '—' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fa-solid fa-file-slash"></i>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'src' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<a
|
<a
|
||||||
class="has-text-grey"
|
hx-get="search/partial/{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}&{{ uri }}"
|
||||||
onclick="populateSearch('src', '{{ cell|escapejs }}')">
|
|
||||||
{% if row.cells.src == 'irc' %}
|
|
||||||
<span class="icon" data-tooltip="IRC">
|
|
||||||
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
{% elif row.cells.src == 'dis' %}
|
|
||||||
<span class="icon" data-tooltip="Discord">
|
|
||||||
<i class="fa-brands fa-discord" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
{% elif row.cells.src == '4ch' %}
|
|
||||||
<span class="icon" data-tooltip="4chan">
|
|
||||||
<i class="fa-solid fa-leaf" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'ts' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<p>{{ row.cells.date }}</p>
|
|
||||||
<p>{{ row.cells.time }}</p>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'type' or column.name == 'mtype' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<a
|
|
||||||
class="has-text-grey"
|
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ cell|escapejs }}')">
|
|
||||||
{% if cell == 'msg' %}
|
|
||||||
<span class="icon" data-tooltip="Message">
|
|
||||||
<i class="fa-solid fa-message"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'join' %}
|
|
||||||
<span class="icon" data-tooltip="Join">
|
|
||||||
<i class="fa-solid fa-person-to-portal"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'part' %}
|
|
||||||
<span class="icon" data-tooltip="Part">
|
|
||||||
<i class="fa-solid fa-person-from-portal"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'quit' %}
|
|
||||||
<span class="icon" data-tooltip="Quit">
|
|
||||||
<i class="fa-solid fa-circle-xmark"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'kick' %}
|
|
||||||
<span class="icon" data-tooltip="Kick">
|
|
||||||
<i class="fa-solid fa-user-slash"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'nick' %}
|
|
||||||
<span class="icon" data-tooltip="Nick">
|
|
||||||
<i class="fa-solid fa-signature"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'mode' %}
|
|
||||||
<span class="icon" data-tooltip="Mode">
|
|
||||||
<i class="fa-solid fa-gear"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'action' %}
|
|
||||||
<span class="icon" data-tooltip="Action">
|
|
||||||
<i class="fa-solid fa-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'notice' %}
|
|
||||||
<span class="icon" data-tooltip="Notice">
|
|
||||||
<i class="fa-solid fa-message-code"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'conn' %}
|
|
||||||
<span class="icon" data-tooltip="Connection">
|
|
||||||
<i class="fa-solid fa-cloud-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'znc' %}
|
|
||||||
<span class="icon" data-tooltip="ZNC">
|
|
||||||
<i class="fa-brands fa-unity"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'query' %}
|
|
||||||
<span class="icon" data-tooltip="Query">
|
|
||||||
<i class="fa-solid fa-message"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'highlight' %}
|
|
||||||
<span class="icon" data-tooltip="Highlight">
|
|
||||||
<i class="fa-solid fa-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'who' %}
|
|
||||||
<span class="icon" data-tooltip="Who">
|
|
||||||
<i class="fa-solid fa-passport"></i>
|
|
||||||
</span>
|
|
||||||
{% elif cell == 'topic' %}
|
|
||||||
<span class="icon" data-tooltip="Topic">
|
|
||||||
<i class="fa-solid fa-sign"></i>
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
{{ cell }}
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'msg' %}
|
|
||||||
<td class="{{ column.name }} wrap">
|
|
||||||
<a
|
|
||||||
class="has-text-grey is-underlined"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
hx-post="{% url 'modal_context' %}"
|
|
||||||
hx-vals='{"net": "{{ row.cells.net|escapejs }}",
|
|
||||||
"num": "{{ row.cells.num|escapejs }}",
|
|
||||||
"source": "{{ row.cells.src|escapejs }}",
|
|
||||||
"channel": "{{ row.cells.channel|escapejs }}",
|
|
||||||
"time": "{{ row.cells.time|escapejs }}",
|
|
||||||
"date": "{{ row.cells.date|escapejs }}",
|
|
||||||
"index": "{% if row.cells.index != '—' %}{{row.cells.index}}{% else %}{{ params.index }}{% endif %}",
|
|
||||||
"type": "{{ row.cells.type }}",
|
|
||||||
"mtype": "{{ row.cells.mtype }}",
|
|
||||||
"nick": "{{ row.cells.nick|escapejs }}",
|
|
||||||
"dedup": "{{ params.dedup }}"}'
|
|
||||||
hx-target="#modals-here"
|
|
||||||
hx-trigger="click"
|
hx-trigger="click"
|
||||||
href="/?modal=context&net={{row.cells.net|escapejs}}&num={{row.cells.num|escapejs}}&source={{row.cells.src|escapejs}}&channel={{row.cells.channel|urlsafe}}&time={{row.cells.time|escapejs}}&date={{row.cells.date|escapejs}}&index={{params.index}}&type={{row.cells.type}}&mtype={{row.cells.mtype}}&nick={{row.cells.mtype|escapejs}}">
|
hx-target="#drilldown-table"
|
||||||
{{ row.cells.msg }}
|
hx-swap="outerHTML"
|
||||||
|
hx-indicator="#spinner"
|
||||||
|
style="cursor: pointer;">
|
||||||
|
{{ column.header }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</div>
|
||||||
{% elif column.name == 'nick' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<div class="nowrap-parent">
|
|
||||||
<div class="nowrap-child">
|
|
||||||
{% if row.cells.online is True %}
|
|
||||||
<span class="icon has-text-success has-tooltip-success" data-tooltip="Online">
|
|
||||||
<i class="fa-solid fa-circle"></i>
|
|
||||||
</span>
|
|
||||||
{% elif row.cells.online is False %}
|
|
||||||
<span class="icon has-text-danger has-tooltip-danger" data-tooltip="Offline">
|
|
||||||
<i class="fa-solid fa-circle"></i>
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="icon has-text-warning has-tooltip-warning" data-tooltip="Unknown">
|
|
||||||
<i class="fa-solid fa-circle"></i>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<a class="nowrap-child has-text-grey" onclick="populateSearch('nick', '{{ cell|escapejs }}')">
|
|
||||||
{{ cell }}
|
|
||||||
</a>
|
|
||||||
<div class="nowrap-child">
|
|
||||||
{% if row.cells.src == 'irc' %}
|
|
||||||
<a
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_drilldown' %}"
|
|
||||||
hx-vals='{"net": "{{ row.cells.net }}", "nick": "{{ row.cells.nick }}", "channel": "{{ row.cells.channel }}"}'
|
|
||||||
hx-target="#modals-here"
|
|
||||||
hx-trigger="click"
|
|
||||||
class="has-text-black">
|
|
||||||
<span class="icon" data-tooltip="Open drilldown modal">
|
|
||||||
<i class="fa-solid fa-album"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_drilldown' type='window' %}"
|
|
||||||
hx-vals='{"net": "{{ row.cells.net }}", "nick": "{{ row.cells.nick }}", "channel": "{{ row.cells.channel }}"}'
|
|
||||||
hx-target="#windows-here"
|
|
||||||
hx-swap="afterend"
|
|
||||||
hx-trigger="click"
|
|
||||||
class="has-text-black">
|
|
||||||
<span class="icon" data-tooltip="Open drilldown window">
|
|
||||||
<i class="fa-solid fa-album"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_drilldown' type='widget' %}"
|
|
||||||
hx-vals='{"net": "{{ row.cells.net }}", "nick": "{{ row.cells.nick }}", "channel": "{{ row.cells.channel }}"}'
|
|
||||||
hx-target="#widgets-here"
|
|
||||||
hx-trigger="click"
|
|
||||||
class="has-text-black">
|
|
||||||
<span class="icon" data-tooltip="Open drilldown widget">
|
|
||||||
<i class="fa-solid fa-album"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if row.cells.num_chans != '—' %}
|
|
||||||
<div class="nowrap-child">
|
|
||||||
<span class="tag">
|
|
||||||
{{ row.cells.num_chans }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'channel' %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
{% if cell != '—' %}
|
|
||||||
<div class="nowrap-parent">
|
|
||||||
<a
|
|
||||||
class="nowrap-child has-text-grey"
|
|
||||||
onclick="populateSearch('channel', '{{ cell|escapejs }}')">
|
|
||||||
{{ cell }}
|
|
||||||
</a>
|
|
||||||
{% if row.cells.num_users != '—' %}
|
|
||||||
<div class="nowrap-child">
|
|
||||||
<span class="tag">
|
|
||||||
{{ row.cells.num_users }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{{ cell }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% elif cell is True or cell is False %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
{% if cell is True %}
|
|
||||||
<span class="icon has-text-success">
|
|
||||||
<i class="fa-solid fa-check"></i>
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fa-solid fa-xmark"></i>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% elif column.name == "tokens" %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<div class="tags">
|
|
||||||
{% for word in cell %}
|
|
||||||
<a
|
|
||||||
class="tag"
|
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ word }}')">
|
|
||||||
{{ word }}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{% elif column.name == "meta" %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<pre class="small-field" style="cursor: pointer;">{{ cell }}</pre>
|
|
||||||
</td>
|
|
||||||
{% elif 'id' in column.name and column.name != "ident" %}
|
|
||||||
<td class="{{ column.name }}">
|
|
||||||
<div class="buttons">
|
|
||||||
<div class="nowrap-parent">
|
|
||||||
<!-- <input class="input" type="text" value="{{ cell }}" style="width: 50px;" readonly> -->
|
|
||||||
<a
|
|
||||||
class="has-text-grey button nowrap-child"
|
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ cell|escapejs }}')">
|
|
||||||
<span class="icon" data-tooltip="Populate {{ cell }}">
|
|
||||||
<i class="fa-solid fa-arrow-left-long-to-line" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="has-text-grey button nowrap-child"
|
|
||||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ cell|escapejs }}');">
|
|
||||||
<span class="icon" data-tooltip="Copy to clipboard">
|
|
||||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="{{ column.name }}">
|
<div class="nowrap-child">
|
||||||
<a
|
{{ column.header }}
|
||||||
class="has-text-grey"
|
</div>
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ cell|escapejs }}')">
|
|
||||||
{{ cell }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock table.tbody.td %}
|
</div>
|
||||||
{% endif %}
|
</th>
|
||||||
{% endfor %}
|
{% endblock table.thead.th %}
|
||||||
</tr>
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% endblock table.tbody.row %}
|
{% endfor %}
|
||||||
{% empty %}
|
</tr>
|
||||||
{% if table.empty_text %}
|
{% endblock table.thead.row %}
|
||||||
{% block table.tbody.empty_text %}
|
</thead>
|
||||||
<tr><td class="{{ column.name }}" colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
{% endif %}
|
||||||
{% endblock table.tbody.empty_text %}
|
{% endblock table.thead %}
|
||||||
{% endif %}
|
{% block table.tbody %}
|
||||||
{% endfor %}
|
<tbody {{ table.attrs.tbody.as_html }}>
|
||||||
</tbody>
|
{% for row in table.paginated_rows %}
|
||||||
{% endblock table.tbody %}
|
{% block table.tbody.row %}
|
||||||
{% block table.tfoot %}
|
{% if row.cells.type == 'control' %}
|
||||||
{% if table.has_footer %}
|
|
||||||
<tfoot {{ table.attrs.tfoot.as_html }}>
|
|
||||||
{% block table.tfoot.row %}
|
|
||||||
<tr>
|
<tr>
|
||||||
{% for column in table.columns %}
|
<td></td>
|
||||||
{% block table.tfoot.td %}
|
<td>
|
||||||
<td class="{{ column.name }}" {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
<span class="icon has-text-grey" data-tooltip="Hidden">
|
||||||
{% endblock table.tfoot.td %}
|
<i class="fa-solid fa-file-slash"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p class="has-text-grey">Hidden {{ row.cells.hidden }} similar result{% if row.cells.hidden > 1%}s{% endif %}</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
{% for column, cell in row.items %}
|
||||||
|
{% if column.name in show %}
|
||||||
|
{% block table.tbody.td %}
|
||||||
|
{% if cell == '—' %}
|
||||||
|
<td class="{{ column.name }}">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-file-slash"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
{% elif cell is True or cell is False %}
|
||||||
|
<td class="{{ column.name }}">
|
||||||
|
{% if cell is True %}
|
||||||
|
<span class="icon has-text-success">
|
||||||
|
<i class="fa-solid fa-check"></i>
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% elif column.name == "dosages" %}
|
||||||
|
<td class="{{ column.name }}">
|
||||||
|
{{ cell.entry }}
|
||||||
|
</td>
|
||||||
|
{% elif column.name == "name" %}
|
||||||
|
<td class="{{ column.name }}">
|
||||||
|
<a href="{% url 'drug_detail' type='page' pk=row.cells.id %}"><button
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-eye"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
{{ cell }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<td class="{{ column.name }}">
|
||||||
|
<a
|
||||||
|
class="has-text-grey"
|
||||||
|
onclick="populateSearch('{{ column.name }}', '{{ cell|escapejs }}')">
|
||||||
|
{{ cell }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock table.tbody.td %}
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endblock table.tfoot.row %}
|
{% endif %}
|
||||||
</tfoot>
|
{% endblock table.tbody.row %}
|
||||||
{% endif %}
|
{% empty %}
|
||||||
{% endblock table.tfoot %}
|
{% if table.empty_text %}
|
||||||
</table>
|
{% block table.tbody.empty_text %}
|
||||||
</div>
|
<tr><td class="{{ column.name }}" colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
||||||
{% endblock table %}
|
{% endblock table.tbody.empty_text %}
|
||||||
{% block pagination %}
|
|
||||||
{% if table.page and table.paginator.num_pages > 1 %}
|
|
||||||
<nav class="pagination is-justify-content-flex-end" role="navigation" aria-label="pagination">
|
|
||||||
{% block pagination.previous %}
|
|
||||||
<a
|
|
||||||
class="pagination-previous is-flex-grow-0 {% if not table.page.has_previous %}is-hidden-mobile{% endif %}"
|
|
||||||
{% if table.page.has_previous %}
|
|
||||||
hx-get="search/partial/{% querystring table.prefixed_page_field=table.page.previous_page_number %}&{{ uri }}"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-trigger="click"
|
|
||||||
hx-target="#drilldown-table"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
hx-indicator="#spinner"
|
|
||||||
{% else %}
|
|
||||||
href="#"
|
|
||||||
disabled
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
style="order:1;">
|
{% endfor %}
|
||||||
{% block pagination.previous.text %}
|
</tbody>
|
||||||
<span aria-hidden="true">«</span>
|
{% endblock table.tbody %}
|
||||||
{% endblock pagination.previous.text %}
|
{% block table.tfoot %}
|
||||||
</a>
|
{% if table.has_footer %}
|
||||||
{% endblock pagination.previous %}
|
<tfoot {{ table.attrs.tfoot.as_html }}>
|
||||||
{% block pagination.next %}
|
{% block table.tfoot.row %}
|
||||||
<a
|
<tr>
|
||||||
class="pagination-next is-flex-grow-0 {% if not table.page.has_next %}is-hidden-mobile{% endif %}"
|
{% for column in table.columns %}
|
||||||
{% if table.page.has_next %}
|
{% block table.tfoot.td %}
|
||||||
hx-get="search/partial/{% querystring table.prefixed_page_field=table.page.next_page_number %}&{{ uri }}"
|
<td class="{{ column.name }}" {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
{% endblock table.tfoot.td %}
|
||||||
hx-trigger="click"
|
{% endfor %}
|
||||||
hx-target="#drilldown-table"
|
</tr>
|
||||||
hx-swap="outerHTML"
|
{% endblock table.tfoot.row %}
|
||||||
hx-indicator="#spinner"
|
</tfoot>
|
||||||
{% else %}
|
|
||||||
href="#"
|
|
||||||
disabled
|
|
||||||
{% endif %}
|
|
||||||
style="order:3;"
|
|
||||||
>
|
|
||||||
{% block pagination.next.text %}
|
|
||||||
<span aria-hidden="true">»</span>
|
|
||||||
{% endblock pagination.next.text %}
|
|
||||||
</a>
|
|
||||||
{% endblock pagination.next %}
|
|
||||||
{% if table.page.has_previous or table.page.has_next %}
|
|
||||||
{% block pagination.range %}
|
|
||||||
<ul class="pagination-list is-flex-grow-0" style="order:2;">
|
|
||||||
{% for p in table.page|table_page_range:table.paginator %}
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="pagination-link {% if p == table.page.number %}is-current{% endif %}"
|
|
||||||
aria-label="Page {{ p }}" block
|
|
||||||
{% if p == table.page.number %}aria-current="page"{% endif %}
|
|
||||||
{% if p == table.page.number %}
|
|
||||||
href="#"
|
|
||||||
{% else %}
|
|
||||||
hx-get="search/partial/{% querystring table.prefixed_page_field=p %}&{{ uri }}"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-trigger="click"
|
|
||||||
hx-target="#drilldown-table"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
hx-indicator="#spinner"
|
|
||||||
{% endif %}
|
|
||||||
>
|
|
||||||
{% if p == '...' %}
|
|
||||||
<span class="pagination-ellipsis">…</span>
|
|
||||||
{% else %}
|
|
||||||
{{ p }}
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endblock pagination.range %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
{% endblock table.tfoot %}
|
||||||
{% endif %}
|
</table>
|
||||||
{% endblock pagination %}
|
</div>
|
||||||
</div>
|
{% endblock table %}
|
||||||
{% endblock table-wrapper %}
|
{% block pagination %}
|
||||||
{% endcache %}
|
{% if table.page and table.paginator.num_pages > 1 %}
|
||||||
|
<nav class="pagination is-justify-content-flex-end" role="navigation" aria-label="pagination">
|
||||||
|
{% block pagination.previous %}
|
||||||
|
<a
|
||||||
|
class="pagination-previous is-flex-grow-0 {% if not table.page.has_previous %}is-hidden-mobile{% endif %}"
|
||||||
|
{% if table.page.has_previous %}
|
||||||
|
hx-get="search/partial/{% querystring table.prefixed_page_field=table.page.previous_page_number %}&{{ uri }}"
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#drilldown-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-indicator="#spinner"
|
||||||
|
{% else %}
|
||||||
|
href="#"
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
style="order:1;">
|
||||||
|
{% block pagination.previous.text %}
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
{% endblock pagination.previous.text %}
|
||||||
|
</a>
|
||||||
|
{% endblock pagination.previous %}
|
||||||
|
{% block pagination.next %}
|
||||||
|
<a
|
||||||
|
class="pagination-next is-flex-grow-0 {% if not table.page.has_next %}is-hidden-mobile{% endif %}"
|
||||||
|
{% if table.page.has_next %}
|
||||||
|
hx-get="search/partial/{% querystring table.prefixed_page_field=table.page.next_page_number %}&{{ uri }}"
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#drilldown-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-indicator="#spinner"
|
||||||
|
{% else %}
|
||||||
|
href="#"
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
style="order:3;"
|
||||||
|
>
|
||||||
|
{% block pagination.next.text %}
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
{% endblock pagination.next.text %}
|
||||||
|
</a>
|
||||||
|
{% endblock pagination.next %}
|
||||||
|
{% if table.page.has_previous or table.page.has_next %}
|
||||||
|
{% block pagination.range %}
|
||||||
|
<ul class="pagination-list is-flex-grow-0" style="order:2;">
|
||||||
|
{% for p in table.page|table_page_range:table.paginator %}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class="pagination-link {% if p == table.page.number %}is-current{% endif %}"
|
||||||
|
aria-label="Page {{ p }}" block
|
||||||
|
{% if p == table.page.number %}aria-current="page"{% endif %}
|
||||||
|
{% if p == table.page.number %}
|
||||||
|
href="#"
|
||||||
|
{% else %}
|
||||||
|
hx-get="search/partial/{% querystring table.prefixed_page_field=p %}&{{ uri }}"
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#drilldown-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-indicator="#spinner"
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
{% if p == '...' %}
|
||||||
|
<span class="pagination-ellipsis">…</span>
|
||||||
|
{% else %}
|
||||||
|
{{ p }}
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock pagination.range %}
|
||||||
|
{% endif %}
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock pagination %}
|
||||||
|
</div>
|
||||||
|
{% endblock table-wrapper %}
|
||||||
|
{# endcache #}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import orjson
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def pretty(data):
|
||||||
|
return orjson.dumps(data, option=orjson.OPT_INDENT_2).decode("utf-8")
|
|
@ -8,7 +8,9 @@ from core.forms import DrugForm
|
||||||
from core.models import Drug
|
from core.models import Drug
|
||||||
from core.views.helpers import synchronize_async_helper
|
from core.views.helpers import synchronize_async_helper
|
||||||
from mxs.restrictions import StaffMemberRequiredMixin
|
from mxs.restrictions import StaffMemberRequiredMixin
|
||||||
from mxs.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate
|
from mxs.views import ObjectCreate, ObjectDelete, ObjectList, ObjectRead, ObjectUpdate
|
||||||
|
|
||||||
|
# from mixins.views import ObjectRead
|
||||||
|
|
||||||
|
|
||||||
class DrugList(LoginRequiredMixin, StaffMemberRequiredMixin, ObjectList):
|
class DrugList(LoginRequiredMixin, StaffMemberRequiredMixin, ObjectList):
|
||||||
|
@ -54,6 +56,25 @@ class DrugDelete(LoginRequiredMixin, StaffMemberRequiredMixin, ObjectDelete):
|
||||||
model = Drug
|
model = Drug
|
||||||
|
|
||||||
|
|
||||||
|
class DrugDetail(LoginRequiredMixin, StaffMemberRequiredMixin, ObjectRead):
|
||||||
|
model = Drug
|
||||||
|
form_class = DrugForm
|
||||||
|
detail_template = "partials/drug-detail.html"
|
||||||
|
|
||||||
|
detail_url_name = "drug_detail"
|
||||||
|
detail_url_args = ["type", "pk"]
|
||||||
|
|
||||||
|
def get_object(self, **kwargs):
|
||||||
|
print("GET")
|
||||||
|
pk = kwargs.get("pk")
|
||||||
|
info = Drug.objects.get(pk=pk)
|
||||||
|
|
||||||
|
# self.extra_context = {}
|
||||||
|
print("info", info)
|
||||||
|
# return dictionary
|
||||||
|
return info.__dict__
|
||||||
|
|
||||||
|
|
||||||
class DrugClear(LoginRequiredMixin, StaffMemberRequiredMixin, APIView):
|
class DrugClear(LoginRequiredMixin, StaffMemberRequiredMixin, APIView):
|
||||||
def delete(self, request):
|
def delete(self, request):
|
||||||
template_name = "mixins/partials/notify.html"
|
template_name = "mixins/partials/notify.html"
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
|
# from mixins.restrictions import StaffMemberRequiredMixin
|
||||||
|
from mixins.views import (
|
||||||
|
ObjectCreate,
|
||||||
|
ObjectDelete,
|
||||||
|
ObjectList,
|
||||||
|
ObjectRead,
|
||||||
|
ObjectUpdate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from core.forms import FavouriteForm
|
||||||
|
from core.models import Favourite
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteList(LoginRequiredMixin, ObjectList):
|
||||||
|
list_template = "partials/favourite-list.html"
|
||||||
|
model = Favourite
|
||||||
|
page_title = "Global list of favourites"
|
||||||
|
|
||||||
|
list_url_name = "favourites"
|
||||||
|
list_url_args = ["type"]
|
||||||
|
|
||||||
|
submit_url_name = "favourite_create"
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteCreate(LoginRequiredMixin, ObjectCreate):
|
||||||
|
model = Favourite
|
||||||
|
form_class = FavouriteForm
|
||||||
|
|
||||||
|
submit_url_name = "favourite_create"
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteUpdate(LoginRequiredMixin, ObjectUpdate):
|
||||||
|
model = Favourite
|
||||||
|
form_class = FavouriteForm
|
||||||
|
|
||||||
|
submit_url_name = "favourite_update"
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteDelete(LoginRequiredMixin, ObjectDelete):
|
||||||
|
model = Favourite
|
||||||
|
|
||||||
|
|
||||||
|
class FavouriteDetail(LoginRequiredMixin, ObjectRead):
|
||||||
|
model = Favourite
|
||||||
|
form_class = FavouriteForm
|
||||||
|
detail_template = "partials/drug-detail.html"
|
||||||
|
|
||||||
|
detail_url_name = "favourite_detail"
|
||||||
|
detail_url_args = ["type", "pk"]
|
||||||
|
|
||||||
|
def get_object(self, **kwargs):
|
||||||
|
pk = kwargs.get("pk")
|
||||||
|
info = Favourite.objects.get(pk=pk, user=self.request.user)
|
||||||
|
return info.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
# class FavouriteClear(LoginRequiredMixin, APIView):
|
||||||
|
# def delete(self, request):
|
||||||
|
# template_name = "mixins/partials/notify.html"
|
||||||
|
# favourites_all = Favourite.objects.all()
|
||||||
|
# favourites_all.delete()
|
||||||
|
# context = {
|
||||||
|
# "message": "Deleted all favourites",
|
||||||
|
# "class": "success",
|
||||||
|
# }
|
||||||
|
# response = render(request, template_name, context)
|
||||||
|
# response["HX-Trigger"] = "drugEvent"
|
||||||
|
# return response
|
||||||
|
|
||||||
|
|
||||||
|
# class FavouritePullMerge(LoginRequiredMixin, APIView):
|
||||||
|
# def post(self, request):
|
||||||
|
# template_name = "mixins/partials/notify.html"
|
||||||
|
# # Do something
|
||||||
|
# run = synchronize_async_helper(PsychWikiClient())
|
||||||
|
# result = synchronize_async_helper(run.update_favourites())
|
||||||
|
# context = {
|
||||||
|
# "message": f"Favourites fetched: {result}",
|
||||||
|
# "class": "success",
|
||||||
|
# }
|
||||||
|
# response = render(request, template_name, context)
|
||||||
|
# response["HX-Trigger"] = "drugEvent"
|
||||||
|
# return response
|
Loading…
Reference in New Issue