Skip to content

Instantly share code, notes, and snippets.

@bengolder
Created July 20, 2016 21:30
Show Gist options
  • Save bengolder/7efa8dda0de1f8d2e498afdd8bb8ae32 to your computer and use it in GitHub Desktop.
Save bengolder/7efa8dda0de1f8d2e498afdd8bb8ae32 to your computer and use it in GitHub Desktop.
from intake.forms.form_base import CombinableForm
from intake.constants import Counties
from intake.forms import fields
class OtherCountyForm(CombinableForm):
"""This is used by Code for America to send applicants
information on clean slate services in other counties or states.
"""
counties = {Counties.OTHER}
fields = {
fields.contact_preferences,
fields.first_name,
fields.phone_number,
fields.email,
fields.address,
fields.how_did_you_hea'
}
required_fields = {
fields.contact_preferences,
fields.first_name,
}
class SanFranciscoCountyForm(CombinableForm):
counties = {Counties.SAN_FRANCISCO}
fields = {
fields.contact_preferences,
fields.first_name,
fields.middle_name,
fields.last_name,
fields.phone_number,
fields.email,
fields.address,
fields.dob,
fields.ssn,
fields.us_citizen,
fields.serving_sentence,
fields.on_probation_parole,
fields.where_probation_parole,
fields.when_probation_parole,
fields.rap_outside_sf,
fields.when_where_outside_sf,
fields.financial_screening_note,
fields.currently_employed,
fields.monthly_income,
fields.monthly_expenses,
fields.how_did_you_hear,
}
required_fields = {
fields.first_name,
fields.last_name,
}
recommended_fields = {
fields.address,
fields.dob,
fields.ss'
}
class ContraCostaForm(CombinableForm):
"""Based on
https://ca-contracostacounty2.civicplus.com/FormCenter/Public-Defender-7/Prop-47-Contact-Form-144/
"""
counties = {Counties.CONTRA_COSTA}
fields = {
fields.contact_preferences,
fields.first_name,
fields.middle_name,
fields.last_name,
fields.phone_number,
fields.email,
fields.address,
fields.dob,
fields.us_citizen,
fields.serving_sentence,
fields.on_probation_parole,
fields.financial_screening_note,
fields.currently_employed,
fields.monthly_income,
fields.income_source,
fields.monthly_expenses,
fields.case_number,
fields.how_did_you_hear,
fields.additional_informatio'
}
required_fields = {
fields.first_name,
fields.last_name,
fields.address,
fields.us_citizen,
fields.currently_employed,
fields.dob,
fields.monthly_income,
fields.income_source,
fields.monthly_expenses,
fields.serving_sentence,
fields.on_probation_parole,
}
from django.contrib.postgres.fields import JSONField
from django.utils.translation import ugettext as _
from intake import validators
import rest_framework
from rest_framework import serializers
YES_NO_CHOICES = (
('yes', _('Yes')),
('no', _('No')),
)
class FalseIfHasEmptyValue:
def __bool__(self):
return all(vars(self).values())
def __repr__(self):
fields = sorted(vars(self).keys())
field_strs = ["{}='{}'".format(f, getattr(self, f)) for f in fields]
return '<{}({})>'.format(self.__class__.__name__, ', '.join(field_strs))
def __eq__(self, other):
return vars(self) == vars(other)
class Address(FalseIfHasEmptyValue):
def __init__(self, street='', city='', state='', zip=''):
self.street = street
self.city = city
self.state = state
self.zip = zip
class DateOfBirth(FalseIfHasEmptyValue):
def __init__(self, month='', day='', year=''):
self.month = month
self.day = day
self.year = year
class ContactInfoJSONField(JSONField):
"""
A field for storing contact information that validates
data against expected keys and structure
"""
def validate(self, value, model_instance):
validators.contact_info_json(value)
super().validate(value, model_instance)
class FormFieldMixin:
"""A serializer field that adds some additional convenience attributes
for rendering as an HTML form and mimicking parts of the Django
FormField API
"""
label = None
help_text = ""
html_attrs = {}
def __init__(self, *args, **kwargs):
self.add_default_init_args(kwargs)
super().__init__(*args, **kwargs)
self.html_attrs.update(kwargs.get('html_attrs', {}))
def add_default_init_args(self, kwargs):
"""Allows FormField classes to set class attributes
that are passed to the parent class __init__ method
in order to be used as instance attributes.
In other words, add_default_init_args allows subclasses
to set class attributes that are used as default values
for instance attributes.
"""
inheritable_args = ['required', 'label', 'help_text']
for key in inheritable_args:
if key not in kwargs and hasattr(self, key):
kwargs[key] = getattr(self, key)
def get_value(self, dictionary):
value = super().get_value(dictionary)
return self.coerce_if_empty(value)
def get_empty_value(self):
"""Gets the default value for empty versions of this field
Often overridden in subclasses in order to provide different types of empty
values
"""
return ""
def field_errors(self):
"""Returns any errors from a parent field or form that pertain to this field
"""
if hasattr(self, 'parent'):
parent = self.parent
if hasattr(parent, '_errors'):
return parent.errors.get(self.field_name, [])
return []
def field_warnings(self):
"""Returns any warnings from a parent field or form that pertain to this field
"""
if hasattr(self, 'parent'):
parent = self.parent
if hasattr(parent, 'warnings'):
return parent.warnings.get(self.field_name, [])
return []
def coerce_if_empty(self, value):
"""Coerces empty values to the default empty value for this field
"""
if value == rest_framework.fields.empty:
return self.get_empty_value()
return value
def current_value(self):
"""Returns the current value for this field
More research into Django REST Framework
might simplify or obviate this method
"""
if hasattr(self, 'parent'):
base_data = getattr(self.parent, 'initial_data', self.parent.get_initial())
if hasattr(self, 'fields'):
base_value = self.get_value(base_data)
value = self.to_internal_value(base_value)
elif isinstance(self.parent, FormFieldMixin):
value = getattr(self.parent.current_value(), self.field_name)
else:
value = self.get_value(base_data)
return self.coerce_if_empty(value)
def input_name(self):
"""Returns a string for use as an html input name value
"""
if hasattr(self, 'parent'):
if getattr(self.parent, 'field_name', ''):
return "{}.{}".format(self.parent.field_name, self.field_name)
return self.field_name
def class_name(self):
return self.input_name().replace('.', '_')
class ChoicesMixin(FormFieldMixin):
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices', None)
if not choices and hasattr(self, 'choices'):
choices = getattr(self, 'choices')
if not choices:
raise TypeError("'choices' is a required attribute. It must be provided or passed to __init__.")
super().__init__(choices, *args, **kwargs)
class BlankIfNotRequiredMixin:
def __init__(self, *args, **kwargs):
if not kwargs.get('required', getattr(self, 'required', True)):
kwargs['allow_blank'] = True
super().__init__(*args, **kwargs)
class CharField(FormFieldMixin, BlankIfNotRequiredMixin, serializers.CharField):
pass
class ChoiceField(ChoicesMixin, BlankIfNotRequiredMixin, serializers.ChoiceField):
pass
class YesNoField(ChoiceField):
choices = YES_NO_CHOICES
class MultipleChoiceField(ChoicesMixin, serializers.MultipleChoiceField):
def to_internal_value(self, data):
return serializers.MultipleChoiceField.to_internal_value(self, data)
def to_representation(self, obj):
return list(obj)
def get_empty_value(self):
return []
class MultiValueFormField(serializers.Serializer, FormFieldMixin):
def to_internal_value(self, data):
base_data = super().to_internal_value(data)
kwargs = {}
for key in self.fields:
kwargs[key] = base_data.get(key, "").strip()
# just need something that gives
return self.build_instance(**kwargs)
def get_empty_value(self):
return self.build_instance()
def to_representation(self, obj):
return vars(obj)
def get_value(self, dictionary):
value = super().get_value(dictionary)
if value == rest_framework.fields.empty:
return {}
return value
class MultiValueFormField(serializers.Serializer, FormFieldMixin):
def to_internal_value(self, data):
base_data = super().to_internal_value(data)
kwargs = {}
for key in self.fields:
kwargs[key] = base_data.get(key, "").strip()
# just need something that gives
return self.build_instance(**kwargs)
def get_empty_value(self):
return self.build_instance()
def to_representation(self, obj):
return vars(obj)
def get_value(self, dictionary):
value = super().get_value(dictionary)
if value == rest_framework.fields.empty:
return {}
return value
class AddressMultiValueFormField(MultiValueFormField):
street = CharField(required=False)
city = CharField(label=_("City"), required=False)
state = CharField(label=_("State"), required=False)
zip = CharField(label=_("Zip"), required=False)
def build_instance(self, **kwargs):
return Address(**kwargs)
class DateOfBirthMultiValueFormField(MultiValueFormField):
label = _("What is your date of birth?")
help_text = _("For example: 4/28/1986")
month = CharField(label=_("Month"), required=False)
day = CharField(label=_("Day"), required=False)
year = CharField(label=_("Year"), required=False)
def build_instance(self, **kwargs):
return DateOfBirth(**kwargs)
class SocialSecurityNumberField(CharField):
label = _('What is your Social Security Number?')
class PhoneNumberField(CharField):
label= _('What is your phone number?')
class EmailField(FormFieldMixin, BlankIfNotRequiredMixin, serializers.EmailField):
label =_ ('What is your email?')
help_text = _('For example "yourname@example.com"')
"""
Contains a complete list of all the field instances
that should be used by forms in intake.
"""
from collections import OrderedDict
from django.utils.translation import ugettext as _
from intake.forms import field_types
from intake.constants import COUNTY_CHOICES, CONTACT_PREFERENCE_CHOICES
# Meta fields about the application
counties = field_types.MultipleChoiceField(
choices=COUNTY_CHOICES,
label=_("Which counties were you arrested in?"))
how_did_you_hear = field_types.CharField(
label=_("How did you hear about this program or website?"))
case_number = field_types.CharField(
label=_('If you have one, what is your case number?'),
help_text=_("If you don't have one or don't remember, that's okay."))
additional_information = field_types.CharField(
label=_("Is there anything else you want to say?"))
# Identification questions
first_name = field_types.CharField(
label=_('What is your first name?'))
middle_name = field_types.CharField(
label=_('What is your middle name?'))
last_name = field_types.CharField(
label=_('What is your last name?'))
dob = field_types.DateOfBirthMultiValueFormField()
ssn = field_types.SocialSecurityNumberField(
help_text=_("The public defender's office will use this to get your San Francisco RAP sheet and find any convictions that can be reduced or dismissed."))
# Contact info questions
contact_preferences = field_types.MultipleChoiceField(
choices=CONTACT_PREFERENCE_CHOICES,
label=_('How would you like us to contact you?'),
help_text=_('Code for America will use this to update you about your application.'))
phone_number = field_types.PhoneNumberField(
help_text=_('Code for America and the public defender will use this to contact you about your application.'))
email = field_types.EmailField()
address = field_types.AddressMultiValueFormField()
# Case status and screening questions
us_citizen = field_types.YesNoField(
label=_("Are you a U.S. citizen?"),
help_text=_("The public defender handles non-citizen cases differently and has staff who can help with citizenship issues."))
being_charged = field_types.YesNoField(
label=_("Are you currently being charged with a crime?"))
serving_sentence = field_types.YesNoField(
label=_("Are you currently serving a sentence?"))
on_probation_parole = field_types.YesNoField(
label=_("Are you on probation or parole?"))
where_probation_or_parole = field_types.CharField(
label=_("Where is your probation or parole?"))
when_probation_or_parole = field_types.CharField(
label=_("When does your probation or parole end?"))
rap_outside_sf = field_types.YesNoField(
label=_("Have you ever been arrested or convicted outside of San Francisco?"))
when_where_outside_sf = field_types.CharField(
label=_("When and where were you arrested or convicted outside of San Francisco?"))
# Financial questions
financial_screening_note = _("The Clean Slate program is free for you, but the public defender uses this information to get money from government programs.")
currently_employed = field_types.YesNoField(
label=_("Are you currently employed?"))
monthly_income = field_types.CharField(
label=_("What is your monthly_income?"))
income_source = field_types.CharField(
label=_("Where does your income come from?"),
help_text=_("For example: Job, Social Security, Food stamps"))
monthly_expenses = field_types.CharField(
label=_("How much do you spend each month on things like rent, groceries, utilities, medical expenses, or childcare expenses?"))
# This defines the order of all fields in relation to each other
INTAKE_FIELDS = [
counties,
contact_preferences,
first_name,
middle_name,
last_name,
phone_number,
email,
address,
dob,
ssn,
us_citizen,
serving_sentence,
on_probation_parole,
where_probation_or_parole,
when_probation_or_parole,
rap_outside_sf,
when_where_outside_sf,
financial_screening_note,
currently_employed,
monthly_income,
income_source,
monthly_expenses,
case_number,
how_did_you_hear,
additional_information,
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment