-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement board member details as snippet #1189
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
# Generated by Django 3.2.25 on 2024-12-20 15:25 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
import wagtail.fields | ||
import wagtail.models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
("wagtailimages", "0025_alter_image_file_alter_rendition_file"), | ||
("wagtailcore", "0089_log_entry_data_json_null_to_object"), | ||
("lametro", "0017_alter_alert_description"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="BoardMemberDetails", | ||
fields=[ | ||
( | ||
"id", | ||
models.AutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
( | ||
"live", | ||
models.BooleanField( | ||
default=True, editable=False, verbose_name="live" | ||
), | ||
), | ||
( | ||
"has_unpublished_changes", | ||
models.BooleanField( | ||
default=False, | ||
editable=False, | ||
verbose_name="has unpublished changes", | ||
), | ||
), | ||
( | ||
"first_published_at", | ||
models.DateTimeField( | ||
blank=True, | ||
db_index=True, | ||
null=True, | ||
verbose_name="first published at", | ||
), | ||
), | ||
( | ||
"last_published_at", | ||
models.DateTimeField( | ||
editable=False, null=True, verbose_name="last published at" | ||
), | ||
), | ||
( | ||
"go_live_at", | ||
models.DateTimeField( | ||
blank=True, null=True, verbose_name="go live date/time" | ||
), | ||
), | ||
( | ||
"expire_at", | ||
models.DateTimeField( | ||
blank=True, null=True, verbose_name="expiry date/time" | ||
), | ||
), | ||
( | ||
"expired", | ||
models.BooleanField( | ||
default=False, editable=False, verbose_name="expired" | ||
), | ||
), | ||
( | ||
"headshot_source", | ||
models.CharField( | ||
blank=True, default="Metro", max_length=256, null=True | ||
), | ||
), | ||
("bio", wagtail.fields.RichTextField(blank=True, null=True)), | ||
( | ||
"headshot", | ||
models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name="+", | ||
to="wagtailimages.image", | ||
), | ||
), | ||
( | ||
"latest_revision", | ||
models.ForeignKey( | ||
blank=True, | ||
editable=False, | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name="+", | ||
to="wagtailcore.revision", | ||
verbose_name="latest revision", | ||
), | ||
), | ||
( | ||
"live_revision", | ||
models.ForeignKey( | ||
blank=True, | ||
editable=False, | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name="+", | ||
to="wagtailcore.revision", | ||
verbose_name="live revision", | ||
), | ||
), | ||
( | ||
"person", | ||
models.OneToOneField( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="details", | ||
to="lametro.lametroperson", | ||
), | ||
), | ||
], | ||
options={ | ||
"verbose_name_plural": "Board Member Details", | ||
}, | ||
bases=(wagtail.models.PreviewableMixin, models.Model), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
from django.contrib.contenttypes.fields import GenericRelation | ||
from django.db import models | ||
from django.urls import reverse | ||
from django.utils.html import format_html, strip_tags | ||
|
||
from wagtail.models import Page | ||
from wagtail.models import Page, PreviewableMixin, DraftStateMixin, RevisionMixin | ||
from wagtail.fields import StreamField, RichTextField | ||
from wagtail.admin.panels import FieldPanel | ||
from wagtail.rich_text import expand_db_html | ||
|
@@ -24,6 +26,64 @@ class AboutPage(Page): | |
] | ||
|
||
|
||
class BoardMemberDetails( | ||
DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model | ||
): | ||
include_in_dump = True | ||
|
||
class Meta: | ||
verbose_name_plural = "Board Member Details" | ||
|
||
person = models.OneToOneField( | ||
"lametro.LAMetroPerson", on_delete=models.CASCADE, related_name="details" | ||
) | ||
headshot = models.ForeignKey( | ||
"wagtailimages.Image", | ||
null=True, | ||
blank=True, | ||
on_delete=models.SET_NULL, | ||
related_name="+", | ||
) | ||
headshot_source = models.CharField( | ||
max_length=256, blank=True, null=True, default="Metro" | ||
) | ||
bio = RichTextField(blank=True, null=True) | ||
_revisions = GenericRelation( | ||
"wagtailcore.Revision", related_query_name="member_details" | ||
) | ||
|
||
@property | ||
def revisions(self): | ||
return self._revisions | ||
|
||
def get_url(self): | ||
return reverse("lametro:person", kwargs={"slug": self.person.slug}) | ||
|
||
def __str__(self): | ||
loaded_obj = ( | ||
type(self) | ||
.objects.select_related("person") | ||
.prefetch_related("person__memberships") | ||
.get(id=self.id) | ||
) | ||
return f"{loaded_obj.person.name}{' (current)' if loaded_obj.person.current_memberships.exists() else ''}" | ||
|
||
def get_preview_context(self, request, mode_name): | ||
context = super().get_preview_context(request, mode_name) | ||
context["person"] = self.person | ||
context["person_details"] = self | ||
|
||
council_post = self.person.latest_council_membership.post | ||
context["qualifying_post"] = council_post.acting_label | ||
|
||
context["preview"] = True | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The map and committee/board report modules require a bunch of extra context, so I use this to purposefully omit them from page previews. (I also added a note to the admin interface.) |
||
|
||
return context | ||
|
||
def get_preview_template(self, request, mode_name): | ||
return "person/person.html" | ||
|
||
|
||
class Alert(models.Model): | ||
include_in_dump = True | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from django.db.models.signals import post_save | ||
from django.dispatch import receiver | ||
|
||
from lametro.models import LAMetroPerson, BoardMemberDetails | ||
|
||
|
||
@receiver(post_save, sender=LAMetroPerson) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will handle instantiating details for new members moving forward. To backfill, we simply need to call save on all existing board members. |
||
def create_member_details(sender, instance, created, **kwargs): | ||
details_exist = BoardMemberDetails.objects.filter(person=instance).exists() | ||
|
||
if not details_exist: | ||
BoardMemberDetails.objects.create(person=instance) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{% load static wagtailimages_tags %} | ||
|
||
{% if person.details.headshot %} | ||
{% if original %} | ||
{% image person.details.headshot width-640 class="img-fluid rounded-3 p-1" alt=person.name %} | ||
{% else %} | ||
{% image person.details.headshot fill-100x100 class="img-fluid rounded-3 p-1" alt=person.name %} | ||
{% endif %} | ||
{% else %} | ||
<img src='{% static "images/headshot_placeholder.png" %}' alt="{{person.name}}" title="{{person.name}}" class="img-fluid rounded-3 p-1" id="person-detail-headshot" /> | ||
{% endif %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,38 @@ | ||
{% load extras %} | ||
{% load lametro_extras %} | ||
{% load lametro_extras wagtailcore_tags %} | ||
|
||
|
||
<div class="row"> | ||
<div class="col-md-4"> | ||
<img src='{{person.headshot_url}}' alt='{{person.name}}' title='{{person.name}}' class='img-responsive img-thumbnail img-padded' id="person-detail-headshot" /> | ||
<div class="col-sm-4 col-md-12"> | ||
{% include "common/headshot.html" %} | ||
{% if person_details.headshot %} | ||
<p class="small"> | ||
<i class="fa fa-fw fa-camera" aria-hidden="true"></i> | ||
Credit: {{person_details.headshot_source}} | ||
</p> | ||
{% endif %} | ||
</div> | ||
|
||
<div class="col-sm-6 col-md-12 mt-3 mt-md-0"> | ||
{% if qualifying_post %} | ||
<p class="small mb-1"> | ||
<i class='fa fa-fw fa-briefcase' aria-hidden="true"></i> | ||
{{ qualifying_post | appointment_label }} | ||
</p> | ||
{% endif %} | ||
|
||
{% if person.headshot_source %} | ||
<p class='small mb-1'> | ||
<i class='fa fa-fw fa-camera' aria-hidden="true"></i> | ||
Credit: {{person.headshot_source}} | ||
</p> | ||
{% endif %} | ||
</div> | ||
<p class="small"> | ||
<a href="/about/#about-la-metro"> | ||
<i class="fa fa-info-circle" aria-hidden="true"></i> | ||
More about Metro appointments | ||
</a> | ||
</p> | ||
</div> | ||
|
||
<div class="col-md-8 mt-3"> | ||
<h3>About {{ ceo.name }}</h3> | ||
<p>{{ member_bio | safe }}</p> | ||
</div> | ||
{% if person_details.bio %} | ||
<div class="col-md-8 mt-3"> | ||
<h3>About {{ person.name }}</h3> | ||
<p>{{ person_details.bio | richtext }}</p> | ||
</div> | ||
{% endif %} | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.wagtail.org/en/v5.2.7/topics/snippets/features.html#saving-draft-changes-of-snippets