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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
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 ''}"

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


return context

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


class Alert(models.Model):
include_in_dump = True

Expand Down
5 changes: 3 additions & 2 deletions lametro/models/legislative.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,9 @@ def ceo(cls):

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

file_directory = os.path.dirname(__file__)
absolute_file_directory = os.path.abspath(file_directory)
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.

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 aspect_ratio="fill-100x100" %}
{% include 'common/headshot.html' %}
{% endwith %}
</div>
</td>

Expand Down
8 changes: 6 additions & 2 deletions lametro/templates/committee.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ <h2 class="d-inline h4">
<tr>
<td data-order="{{ membership.index }}">
<div class="thumbnail-square">
<img src="{{ membership.person.headshot_url }}" alt="{{membership.person.name}}" title="{{membership.person.name}}" class="img-responsive img-thumbnail">
{% with person=membership.person %}
{% include "common/headshot.html" %}
{% endwith %}
</div>
</td>
<td>
Expand Down Expand Up @@ -107,7 +109,9 @@ <h2 class="d-inline h4">
<tr>
<td class="w-12pcnt">
<div class="thumbnail-square">
<img src="{{ ceo.headshot_url }}" alt="{{ceo.name}}" title="{{ceo.name}}" class="img-responsive img-thumbnail">
{% with person=ceo %}
{% include "common/headshot.html" %}
{% endwith %}
</div>
</td>
<td class="w-22pcnt"><a href="{% url 'lametro:person' ceo.slug %}">{{ ceo.name }}</a></td>
Expand Down
11 changes: 11 additions & 0 deletions lametro/templates/common/headshot.html
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 %}
39 changes: 25 additions & 14 deletions lametro/templates/person/_person_ceo.html
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>
Loading