Skip to content
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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ services:
condition: service_healthy
environment:
DATABASE_URL: 'postgis://postgres:postgres@postgres/lametro'
SHARED_DB: "True"
OCD_DIVISION_CSV: '/app/lametro_divisions.csv'
command: sh -c 'pupa update --rpm=600 lametro people && pupa update --rpm=600 lametro bills window=7 && pupa update --rpm=600 lametro events window=7'
volumes:
Expand Down
9 changes: 9 additions & 0 deletions lametro/apps.py
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
31 changes: 0 additions & 31 deletions lametro/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from haystack.query import EmptySearchQuerySet

from councilmatic_core.views import CouncilmaticSearchForm
from lametro.models import LAMetroPerson


class LAMetroCouncilmaticSearchForm(CouncilmaticSearchForm):
Expand Down Expand Up @@ -151,33 +150,3 @@ def clean_agenda(self):
return agenda_pdf
else:
raise forms.ValidationError("File type not supported. Please submit a PDF.")


class PersonHeadshotForm(forms.ModelForm):
headshot_form = forms.BooleanField(widget=forms.HiddenInput, initial=True)

def __init__(self, *args, **kwargs):
super(PersonHeadshotForm, self).__init__(*args, **kwargs)
self.fields["headshot"].widget.attrs.update(
{
"required": "True",
}
)

class Meta:
model = LAMetroPerson
fields = ["headshot"]


class PersonBioForm(forms.ModelForm):
bio_form = forms.BooleanField(widget=forms.HiddenInput, initial=True)

def __init__(self, *args, **kwargs):
super(PersonBioForm, self).__init__(*args, **kwargs)
self.fields["councilmatic_biography"].widget.attrs.update(
{"rows": "5", "required": "True"}
)

class Meta:
model = LAMetroPerson
fields = ["councilmatic_biography"]
132 changes: 132 additions & 0 deletions lametro/migrations/0018_boardmemberdetails.py
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),
),
]
62 changes: 61 additions & 1 deletion lametro/models/cms.py
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
Expand All @@ -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
Comment on lines +51 to +57
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 select_related and prefetch_related? Because I imagine this would be useful in most cases where we know we want to grab some related objects, but I'd also think it'd be more nuanced than that.


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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.)

Copy link
Collaborator

Choose a reason for hiding this comment

The 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 map_geojson but wanted to make sure!


return context

def get_preview_template(self, request, mode_name):
return "person/person.html"


class Alert(models.Model):
include_in_dump = True

Expand Down
24 changes: 0 additions & 24 deletions lametro/models/legislative.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
from dateutil.relativedelta import relativedelta
import json
import logging
import os
from pathlib import Path
import pytz

import requests
Expand All @@ -17,7 +15,6 @@

from django.db.models import Prefetch, Case, When, Value, Q, F, Subquery, OuterRef
from django.db.models.functions import Now, Cast
from django.templatetags.static import static
from opencivicdata.legislative.models import (
EventMedia,
EventAgendaItem,
Expand Down Expand Up @@ -467,27 +464,6 @@ def ceo(cls):

return ceo

@property
def headshot_url(self):
if self.headshot:
return self.headshot.url

file_directory = os.path.dirname(__file__)
absolute_file_directory = os.path.abspath(file_directory)

filename = self.slug_name + ".jpg"

manual_headshot = os.path.join(
absolute_file_directory, "static", "images", "manual-headshots", filename
)

if Path(manual_headshot).exists():
image_url = f"images/manual-headshots/{filename}"
else:
image_url = "images/headshot_placeholder.png"

return static(image_url)

@property
def current_bio(self):
if self.councilmatic_biography:
Expand Down
12 changes: 12 additions & 0 deletions lametro/signals/handlers.py
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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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)
9 changes: 5 additions & 4 deletions lametro/static/css/city_custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,6 @@ hr .events-line {
.current-meeting-img {
max-width: 75%;
}

#person-detail-headshot {
max-height: none;
}
}

@media only screen and (max-width : 415px) {
Expand Down Expand Up @@ -818,3 +814,8 @@ caption {
.alert p:last-of-type {
margin: unset;
}

.thumbnail-square {
width: 75px;
height: 75px;
}
4 changes: 3 additions & 1 deletion lametro/templates/board_members/_council_member_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
<tr id="polygon-{{ membership.post.label | slugify }}-{{ membership.person.name | slugify}}" data-name="{{ membership.person.name | slugify}}" data-polygon="polygon-{{ membership.post.label | slugify }}">
<td>
<div class="thumbnail-square">
<img src='{{ membership.person.headshot_url }}' alt='{{ membership.person.name }}' title='{{ membership.person.name }}' class='img-thumbnail' />
{% with person=membership.person %}
{% include 'common/headshot.html' %}
{% endwith %}
</div>
</td>

Expand Down
6 changes: 0 additions & 6 deletions lametro/templates/board_members/board_members.html
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,6 @@ <h3 class="h4"><i class='fa fa-fw fa-calendar-o' aria-hidden="true"></i> Board o
'html': true,
'container': 'body'
});

$('.thumbnail-square img').each(function() {
if ($(this).width() > $(this).height()) {
$(this).addClass('landscape');
}
});
});


Expand Down
Loading