style front page updates, support FOIRequests

This commit is contained in:
Jonas Heinrich 2025-09-19 14:50:42 +02:00
parent c56689893b
commit e3d283f463
7 changed files with 239 additions and 97 deletions

View file

@ -22,6 +22,7 @@ from .models import (
GovernmentPlanFollower, GovernmentPlanFollower,
GovernmentPlanSection, GovernmentPlanSection,
GovernmentPlanUpdate, GovernmentPlanUpdate,
FOIRequest,
) )
User = auth.get_user_model() User = auth.get_user_model()
@ -345,12 +346,22 @@ class GovernmentPlanSectionAdmin(SortableAdminMixin, admin.ModelAdmin):
) )
class FOIRequestAdmin(admin.ModelAdmin):
list_display = ("title", "government_plan", "url", "created_at")
list_filter = ("government_plan",)
search_fields = ("title", "government_plan__title")
date_hierarchy = "created_at"
admin.site.register(Government, GovernmentAdmin) admin.site.register(Government, GovernmentAdmin)
admin.site.register(GovernmentPlan, GovernmentPlanAdmin) admin.site.register(GovernmentPlan, GovernmentPlanAdmin)
admin.site.register(GovernmentPlanUpdate, GovernmentPlanUpdateAdmin) admin.site.register(GovernmentPlanUpdate, GovernmentPlanUpdateAdmin)
admin.site.register(GovernmentPlanSection, GovernmentPlanSectionAdmin) admin.site.register(GovernmentPlanSection, GovernmentPlanSectionAdmin)
admin.site.register(GovernmentPlanFollower, FollowerAdmin) admin.site.register(GovernmentPlanFollower, FollowerAdmin)
admin.site.register(FOIRequest, FOIRequestAdmin)
govplan_admin_site = GovPlanAdminSite(name="govplanadmin") govplan_admin_site = GovPlanAdminSite(name="govplanadmin")
govplan_admin_site.register(FOIRequest, FOIRequestAdmin)
govplan_admin_site.register(GovernmentPlan, GovernmentPlanAdmin) govplan_admin_site.register(GovernmentPlan, GovernmentPlanAdmin)
govplan_admin_site.register(GovernmentPlanUpdate, GovernmentPlanUpdateAdmin) govplan_admin_site.register(GovernmentPlanUpdate, GovernmentPlanUpdateAdmin)

View file

@ -0,0 +1,43 @@
# Generated by Django 5.1.12 on 2025-09-18 20:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('froide_govplan', '0014_remove_governmentplansection_content_placeholder'),
]
operations = [
migrations.AlterField(
model_name='categorizedgovernmentplan',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='government',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='governmentplan',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='governmentplanfollower',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='governmentplansection',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='governmentplanupdate',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View file

@ -0,0 +1,30 @@
# Generated by Django 5.1.12 on 2025-09-19 12:36
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('froide_govplan', '0015_alter_categorizedgovernmentplan_id_and_more'),
]
operations = [
migrations.CreateModel(
name='FOIRequest',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=1024, verbose_name='title')),
('created_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='created at')),
('government_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='foi_requests', to='froide_govplan.governmentplan', verbose_name='government plan')),
],
options={
'verbose_name': 'FOI request',
'verbose_name_plural': 'FOI requests',
'ordering': ('-created_at',),
'get_latest_by': 'created_at',
},
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 5.1.12 on 2025-09-19 12:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('froide_govplan', '0016_foirequest'),
]
operations = [
migrations.AddField(
model_name='foirequest',
name='url',
field=models.CharField(blank=True, max_length=1024, verbose_name='URL'),
),
]

View file

@ -321,23 +321,29 @@ class GovernmentPlan(models.Model):
return "govplan:plan@{}".format(self.pk) return "govplan:plan@{}".format(self.pk)
def get_related_foirequests(self): def get_related_foirequests(self):
if FoiRequest is None: return FOIRequest.objects.filter(government_plan=self).order_by('-created_at')
return []
if not self.responsible_publicbody:
return []
if hasattr(self, "_related_foirequests"):
return self._related_foirequests
self._related_foirequests = (
FoiRequest.objects.filter(
visibility=FoiRequest.VISIBILITY.VISIBLE_TO_PUBLIC, class FOIRequest(models.Model):
public_body=self.responsible_publicbody, title = models.CharField(max_length=1024, verbose_name=_("title"))
government_plan = models.ForeignKey(
'GovernmentPlan',
on_delete=models.CASCADE,
related_name='foi_requests',
verbose_name=_("government plan"),
) )
.filter(tags__name=conf.GOVPLAN_NAME) url = models.CharField(max_length=1024, blank=True, verbose_name=_("URL"))
.filter(reference=self.get_foirequest_reference()) created_at = models.DateTimeField(default=timezone.now, verbose_name=_("created at"))
.order_by("-created_at")
) class Meta:
return self._related_foirequests ordering = ("-created_at",)
get_latest_by = "created_at"
verbose_name = _("FOI request")
verbose_name_plural = _("FOI requests")
def __str__(self):
return f"{self.title} - {self.government_plan.title}"
class GovernmentPlanUpdate(models.Model): class GovernmentPlanUpdate(models.Model):

View file

@ -83,7 +83,7 @@
{% with foirequest=object.get_recent_foirequest %} {% with foirequest=object.get_recent_foirequest %}
<dt>Anfrage</dt> <dt>Anfrage</dt>
<dd> <dd>
{% include "foirequest/snippets/request_item_mini.html" with object=foirequest %} <a href="{{ foirequest.url }}" class="text-decoration-none">Aktuell laufende Anfrage vom {{ foirequest.created_at|date:"d.m.Y" }}</a>
</dd> </dd>
{% endwith %} {% endwith %}
{% endif %} {% endif %}

View file

@ -3,6 +3,7 @@
<style> <style>
.govplan-update .card-body div.tight-margin { .govplan-update .card-body div.tight-margin {
flex-grow: 1; flex-grow: 1;
overflow: hidden; /* make sure content doesnt spill */
} }
.govplan-update .card-header h3 { .govplan-update .card-header h3 {
@ -10,6 +11,37 @@
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
} }
#myCarousel .card {
height: 380px;
display: flex;
flex-direction: column;
}
#myCarousel .card-body {
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Multiline ellipsis for content */
#myCarousel .tight-margin {
overflow: hidden;
}
#myCarousel .tight-margin p,
#myCarousel .tight-margin div {
display: -webkit-box;
-webkit-line-clamp: 3; /* number of lines to show */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
#myCarousel .badge {
font-weight: 400;
font-size: 0.85rem;
}
</style> </style>
<!-- Update tiles on start page --> <!-- Update tiles on start page -->
@ -32,21 +64,13 @@
<div class="card col-md-4"> <div class="card col-md-4">
<a href="{{ update.get_absolute_url }}" class="text-body text-decoration-none"> <a href="{{ update.get_absolute_url }}" class="text-body text-decoration-none">
<div class="card-header p-4 tight-margin text-start"> <div class="card-header p-4 tight-margin text-start">
{% if show_context %}
<span class="badge text-bg-light text-decoration-none me-2 mb-2">
{{ update.plan.get_section }}
</span>
{% endif %}
{% if show_context %} {% if show_context %}
<h3 class="h5 mt-0 ellipsis">{{ update.plan }}</h3> <h3 class="h5 mt-0 ellipsis">{{ update.plan }}</h3>
{% else %} {% else %}
<h3 class="h4 mt-0 ellipsis">{{ update.title }}</h3> <h3 class="h4 mt-0 ellipsis">{{ update.title }}</h3>
{% endif %} {% endif %}
<div class="small"> <div class="small">
<time datetime="{{ update.timestamp|date:'c' }}">{{ update.timestamp|date:"DATE_FORMAT" }}</time> <time datetime="{{ update.timestamp|date:'c' }}">{{ update.timestamp|date:"DATE_FORMAT" }}</time>
{% if update.user or update.organization %} {% if update.user or update.organization %}
<span> <span>
von {{ update.user.get_full_name }}{% if update.user and update.organization %},{% endif %} von {{ update.user.get_full_name }}{% if update.user and update.organization %},{% endif %}
@ -54,17 +78,15 @@
</span> </span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</a> </a>
<div class="card-body tight-margin text-start p-4 d-flex flex-column"> <div class="card-body tight-margin text-start p-4 d-flex flex-column">
{% if update.content or show_context %} {% if update.content or show_context %}
<div class="tight-margin"> <div class="tight-margin flex-grow-1">
{% if show_context %} {% if show_context %}
<h4 class="h5">{{ update.title }}</h4> <h4 class="h5">{{ update.title }}</h4>
{% endif %} {% endif %}
{% with update.content|markdown as content %} {% with update.content|markdown as content %}
{% if show_context %} {% if show_context %}
{{ content|truncatewords_html:15 }} {{ content|truncatewords_html:15 }}
@ -81,9 +103,10 @@
<a href="{{ update.get_absolute_url }}" class="action-link">→ zum Vorhaben</a> <a href="{{ update.get_absolute_url }}" class="action-link">→ zum Vorhaben</a>
{% else %} {% else %}
{% if update.url %} {% if update.url %}
<a href="{{ update.url }}" class="action-link me-3" target="_blank" rel="noopener">→ mehr auf {{ update.get_url_domain }} lesen…</a> <a href="{{ update.url }}" class="action-link me-3" target="_blank" rel="noopener">
→ mehr auf {{ update.get_url_domain }} lesen…
</a>
{% endif %} {% endif %}
{% if update.foirequest %} {% if update.foirequest %}
<a href="{{ update.foirequest.get_absolute_url }}" class="action-link">→ zur Anfrage</a> <a href="{{ update.foirequest.get_absolute_url }}" class="action-link">→ zur Anfrage</a>
{% endif %} {% endif %}
@ -91,13 +114,26 @@
</div> </div>
{% endif %} {% endif %}
{% if show_context %}
<div class="d-md-flex mt-2 align-items-center mt-3">
<ul class="list-unstyled d-flex m-0">
{% for cat in update.plan.categories.all %}
<li>
<span class="badge bg-secondary text-decoration-none me-2 mb-2">
#{{ cat.name }}
</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<button class="carousel-control-prev" type="button" data-bs-target="#myCarousel" data-bs-slide="prev"> <button class="carousel-control-prev" type="button" data-bs-target="#myCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span> <span class="visually-hidden">Previous</span>
@ -107,10 +143,8 @@
<span class="visually-hidden">Next</span> <span class="visually-hidden">Next</span>
</button> </button>
</div> </div>
</div> </div>
<!-- Update tiles on single page -->
{% else %} {% else %}
<div class="py-3" style="margin-top: 1rem; margin-bottom: -2rem;"> <div class="py-3" style="margin-top: 1rem; margin-bottom: -2rem;">