-
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,9 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class LAMetroCouncilmaticConfig(AppConfig): | ||
name = "lametro" | ||
verbose_name = "LA Metro Councilmatic" | ||
|
||
def ready(self): | ||
import lametro.signals.handlers # noqa |
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 ''}" | ||
Comment on lines
+63
to
+69
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. Ah TIL, this is cool! A neat way to reduce the number of queries. When do you personally like to use these |
||
|
||
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.) 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 currently isn't showing up on the non-preview version of the person detail page. This might be because it's on a review app that doesn't have access to |
||
|
||
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. 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 review app didn't have any board members listed in the cms to start, but saving Najarian in the shell got him listed, just like you're describing! Would it make sense to perform this save for all existing members in the new migration in this pr? That way it wouldn't have to be a manual step for staging and production when this gets pushed |
||
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) |
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