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

More diversity questions #1724

Merged
merged 4 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
15 changes: 13 additions & 2 deletions apps/common/fields.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import json

from markupsafe import Markup
from wtforms import IntegerField, SelectField, StringField, ValidationError
from wtforms.widgets import Input, HiddenInput
from wtforms import (
IntegerField,
SelectField,
SelectMultipleField,
StringField,
ValidationError,
)
from wtforms.widgets import Input, HiddenInput, CheckboxInput, ListWidget
from wtforms.widgets.html5 import EmailInput
from wtforms.widgets.core import html_params
from email_validator import validate_email, EmailNotValidError
Expand Down Expand Up @@ -117,3 +123,8 @@ def __call__(self, field, **kwargs):

class StaticField(StringField):
widget = StaticWidget()


class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
option_widget = CheckboxInput()
37 changes: 35 additions & 2 deletions apps/common/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from flask import current_app as app
from flask_wtf import FlaskForm

from wtforms import SelectField, BooleanField
from wtforms import SelectField, BooleanField, ValidationError
from wtforms.validators import InputRequired

from models.user import UserDiversity
from models.cfp_tag import DEFAULT_TAGS, Tag
from models.purchase import AdmissionTicket

from .fields import HiddenIntegerField
from .fields import HiddenIntegerField, MultiCheckboxField


class Form(FlaskForm):
Expand Down Expand Up @@ -42,6 +42,26 @@ class Meta(FlaskForm.Meta):
AGE_VALUES = ("0-15", "16-25", "26-35", "36-45", "46-55", "56-65", "66+")
AGE_CHOICES = tuple(OPT_OUT + [(v, v) for v in AGE_VALUES])

SEXUALITY_VALUES = ("straight-or-heterosexual","gay-or-lesbian","bisexual","other")
russss marked this conversation as resolved.
Show resolved Hide resolved
SEXUALITY_CHOICES = tuple(OPT_OUT + [(v, v.capitalize().replace("-", " ")) for v in SEXUALITY_VALUES])


DISABILITY_CHOICES = tuple(
[
("none", "I do not have any of these disabilities or health conditions"),
("autistic", "Autistic spectrum condition or another condition affecting speech, language, communication or social skills"),
("blind", "Blindness or a visual impairment not corrected by glasses"),
("developmental", "Condition affecting motor, cognitive, social and emotional skills, speech or language since childhood"),
("deaf", "Deafness or a serious hearing impairment"),
("dyslexic", "Dyslexia, dyspraxia or attention deficit hyperactivity disorder (ADHD) or another learning disability"),
("long-term-illness", "Long-term illness (for example cancer, HIV, diabetes, chronic heart disease or epilepsy)"),
("mental-health-illness", "Mental health condition (for example depression, schizophrenia or anxiety disorder)"),
("physical-disabled", "Physical disability or mobility issue (for example impaired use of arms or legs, use of a wheelchair or crutches)"),
("other", "Another disability, health condition or impairment affecting daily life"),
]
)


TOPIC_CHOICES = tuple(NULL_SELECTION + [(v, v.capitalize()) for v in DEFAULT_TAGS])

# FIXME these are matchers for transition from freetext diversity form -> select boxes
Expand Down Expand Up @@ -100,6 +120,8 @@ class DiversityForm(Form):
age = SelectField("Age", default=OPT_OUT[0], choices=AGE_CHOICES)
gender = SelectField("Gender", default=OPT_OUT[0], choices=GENDER_CHOICES)
ethnicity = SelectField("Ethnicity", default=OPT_OUT[0], choices=ETHNICITY_CHOICES)
sexuality = SelectField("Sexuality", default=OPT_OUT[0], choices=SEXUALITY_CHOICES)
disability = MultiCheckboxField("Disability", choices=DISABILITY_CHOICES)

# Track CfP reviewer tags
cfp_tag_0 = SelectField("Topic 1", choices=TOPIC_CHOICES)
Expand All @@ -123,6 +145,8 @@ def update_user(self, user):
user.diversity.age = self.age.data
user.diversity.gender = self.gender.data
user.diversity.ethnicity = self.ethnicity.data
user.diversity.sexuality = self.sexuality.data
user.diversity.disability = self.disability.data

if self.cfp_tags_required:
user.cfp_reviewer_tags = [
Expand All @@ -138,6 +162,8 @@ def set_from_user(self, user):
self.age.data = guess_age(user.diversity.age)
self.gender.data = guess_gender(user.diversity.gender)
self.ethnicity.data = guess_ethnicity(user.diversity.ethnicity)
self.sexuality.data = user.diversity.sexuality
self.disability.data = user.diversity.disability

if self.cfp_tags_required and user.cfp_reviewer_tags:
self.cfp_tag_0.data = user.cfp_reviewer_tags[0].tag
Expand All @@ -146,6 +172,13 @@ def set_from_user(self, user):

return self

def validate_disability(form, field):
if len(field.data) > 1 and "none" in field.data:
raise ValidationError("Cannot select 'no disability' and a disability")
elif len(field.data) > 1 and "" in field.data:
raise ValidationError("Cannot select 'prefer not to say' and a disability")


def validate(self, extra_validators=None):
if not super().validate(extra_validators):
return False
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""add disability and sexuality to diversity table

Revision ID: 7249f66e2ae0
Revises: e9c68e8f78c2
Create Date: 2024-06-03 23:13:28.010124

"""

# revision identifiers, used by Alembic.
revision = '7249f66e2ae0'
down_revision = 'e9c68e8f78c2'

from alembic import op
import sqlalchemy as sa


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('diversity', sa.Column('disability', sa.String(), nullable=True))
op.add_column('diversity', sa.Column('sexuality', sa.String(), nullable=True))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('diversity', 'sexuality')
op.drop_column('diversity', 'disability')
# ### end Alembic commands ###
16 changes: 15 additions & 1 deletion models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,17 @@ class UserDiversity(BaseModel):
age = db.Column(db.String)
gender = db.Column(db.String)
ethnicity = db.Column(db.String)
disability = db.Column(db.String)
sexuality = db.Column(db.String)

@classmethod
def get_export_data(cls):
valid_ages = []
ages = defaultdict(int)
sexes = defaultdict(int)
ethnicities = defaultdict(int)
disability = defaultdict(int)
sexuality = defaultdict(int)

for row in cls.query:
matches = re.findall(r"\b[0-9]{1,3}\b", row.age)
Expand Down Expand Up @@ -523,11 +527,21 @@ def get_export_data(cls):
else:
ethnicities["other"] += 1

# This doesn't need a matcher because it's already encoded
disability[row.disability] += 1
sexuality[row.sexuality] += 1

ages.update(bucketise(valid_ages, [0, 15, 25, 35, 45, 55, 65]))

data = {
"private": {
"diversity": {"age": ages, "sex": sexes, "ethnicity": ethnicities}
"diversity": {
"age": ages,
"sex": sexes,
"ethnicity": ethnicities,
"disability": disability,
"sexuality": sexuality,
}
},
"tables": ["diversity"],
}
Expand Down
4 changes: 3 additions & 1 deletion templates/_diversityform.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% from "_formhelpers.html" import render_field %}
{% from "_formhelpers.html" import render_field, render_multi_checkbox %}

{% macro render_diversity_fields(form, current_user) %}
{# Should only be rendered inside a .panel #}
Expand All @@ -13,6 +13,8 @@
{{ render_field(form.age, True) }}
{{ render_field(form.gender, True) }}
{{ render_field(form.ethnicity, True) }}
{{ render_field(form.sexuality, True) }}
{{ render_multi_checkbox(form.disability) }}
{% if current_user.has_permission("cfp_reviewer") %}
<p>
Just so we know that we're reflecting our attendees' interests reasonably well:
Expand Down
23 changes: 23 additions & 0 deletions templates/_formhelpers.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,26 @@
</dd>

{% endmacro %}

{% macro render_multi_checkbox(field) %}
{% if field.errors %}
{# If we have help text, add an aria-describedby attribute to the field. #}
{% do kwargs.update({'aria-describedby': "help-block-" + field.name}) %}
{% endif %}

<dt><label for="{{ field.name }}" class="control-label">
{{ field.label.text }}
</label></dt>
<dd {%- if field.errors %} class="has-error" {%- endif %}>
<div class="form-check">
{{ field(class_="form-check-input") }}
</div>
{% if field.errors %}
{% for error in field.errors %}
<p class="help-block" id="help-block-{{ field.name }}">{{ error }}</p>
{% endfor %}
{% endif %}
</dd>

{% endmacro %}