Add user-related collections feature with HTMX support and timestamps for collections

This commit is contained in:
Ross
2026-01-26 11:07:15 +00:00
parent c881da244b
commit 048168e87d
12 changed files with 238 additions and 0 deletions
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('anatomy', '0024_exam_results_supervisor_visible'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0089_remove_condition_primary_remove_condition_synonym'),
]
operations = [
migrations.AddField(
model_name='casecollection',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='casecollection',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
+7
View File
@@ -20,7 +20,14 @@
hx-swap="innerHTML"> hx-swap="innerHTML">
Collections to view / take Collections to view / take
</button> </button>
<button class="btn btn-outline-primary ms-2"
hx-get="{% url 'atlas:user_related_collections' %}"
hx-target="#user-related-collections-placeholder"
hx-swap="innerHTML">
My collections
</button>
<div id="user-collections-placeholder" class="mt-3"></div> <div id="user-collections-placeholder" class="mt-3"></div>
<div id="user-related-collections-placeholder" class="mt-3"></div>
</div> </div>
</div> </div>
@@ -0,0 +1,19 @@
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title">Related collections</h5>
<ul class="list-unstyled">
{% for item in related_collections %}
<li>
<a href="{% url 'atlas:collection_detail' item.collection.pk %}">{{ item.collection.name }}</a>
{% if item.is_author %}<span class="badge bg-primary ms-2">Author</span>{% endif %}
{% if item.is_marker %}<span class="badge bg-secondary ms-2">Marker</span>{% endif %}
{% if item.collection.description %}<small class="text-muted"> — {{ item.collection.description }}</small>{% endif %}
</li>
{% empty %}
<li class="text-muted">No related collections.</li>
{% endfor %}
</ul>
</div>
</div>
+1
View File
@@ -37,6 +37,7 @@ urlpatterns = [
path("collection/case_search", views.case_search, name="case_search"), path("collection/case_search", views.case_search, name="case_search"),
path("collection/", views.CollectionView.as_view(), name="collection_view"), path("collection/", views.CollectionView.as_view(), name="collection_view"),
path("collection/user", views.user_collections, name="user_collections"), path("collection/user", views.user_collections, name="user_collections"),
path("collection/related", views.user_related_collections, name="user_related_collections"),
path("uploads", views.user_uploads, name="user_uploads"), path("uploads", views.user_uploads, name="user_uploads"),
path("uploads/<int:user_pk>/user", views.other_user_uploads, name="other_user_uploads"), path("uploads/<int:user_pk>/user", views.other_user_uploads, name="other_user_uploads"),
path("uploads/all", views.all_uploads, name="all_uploads"), path("uploads/all", views.all_uploads, name="all_uploads"),
+33
View File
@@ -1151,6 +1151,39 @@ def user_collections(request):
# endpoint is called by HTMX we prefer returning a small fragment so it # endpoint is called by HTMX we prefer returning a small fragment so it
# can be swapped into the index page without duplicating layout. # can be swapped into the index page without duplicating layout.
@login_required
def user_related_collections(request):
"""Return up to 10 most recently modified collections where the user
is an author or a marker. If called via HTMX return a fragment suitable
for swapping into the index page.
"""
user = request.user
qs = (
CaseCollection.objects.filter(Q(author=user) | Q(markers=user))
.distinct()
.order_by("-updated")[:10]
)
related = []
for c in qs:
related.append(
{
"collection": c,
"is_author": c.author.filter(id=user.id).exists(),
"is_marker": c.markers.filter(id=user.id).exists(),
}
)
context = {"related_collections": related}
is_htmx = getattr(request, "htmx", False) or request.headers.get("HX-Request", "").lower() == "true"
if is_htmx:
html = render_to_string("atlas/partials/_user_related_collections.html", context, request=request)
return HttpResponse(html)
return render(request, "atlas/user_collections.html", context)
@login_required @login_required
def collection_options(request): def collection_options(request):
if not request.htmx: if not request.htmx:
+3
View File
@@ -667,6 +667,9 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
name = models.CharField(max_length=200, help_text="Name of the exam/collection") name = models.CharField(max_length=200, help_text="Name of the exam/collection")
created = models.DateTimeField(auto_now_add=True, help_text="Creation timestamp")
updated = models.DateTimeField(auto_now=True, help_text="Last updated timestamp")
start_date = models.DateTimeField( start_date = models.DateTimeField(
help_text="Date (and time) the exam/collection starts", null=True, blank=True help_text="Date (and time) the exam/collection starts", null=True, blank=True
) )
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('longs', '0033_alter_longseriesimage_image_md5_hash'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('physics', '0017_exam_results_supervisor_visible'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rapids', '0016_exam_results_supervisor_visible'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sbas', '0022_question_frcr_appropriate'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]
@@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2026-01-26 11:05
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shorts', '0010_alter_useranswer_candidate_feedback_and_more'),
]
operations = [
migrations.AddField(
model_name='exam',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, help_text='Creation timestamp'),
preserve_default=False,
),
migrations.AddField(
model_name='exam',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Last updated timestamp'),
),
]