# -*- coding: utf-8 -*-
"""General, standardized definitions for form data.
Used for standardizing data from various sources with
one "schema." The fields are described through meta
classes: description, field data structure, regex.
Autocomplete values inserted from sheet at:
https://docs.google.com/a/ave81.com/spreadsheets/d/1qpPtPvbFTGKrBMxTjYIWERv8zmEPR0dNc4x31IS00ZM/edit?usp=sharing
"""
from __future__ import absolute_import
import inspect
import six
from req import schema
class _BaseField(schema.Schema):
"""The generic representation of a field.
"""
#: The field's name, which maps to an HTML ``name`` attribute. Required.
name = schema.String(
_description="The field's name, this maps to an HTML <code>name</code> attribute."
)
#: The field's label or caption, which maps to an HTML ``label`` element. Required.
label = schema.String(
_description="The field's label/caption, this maps to an HTML <code>label</code> element."
)
#: The field's description, which maps to the HTML ``description`` attribute. Optional.
description = schema.String(
_optional=True,
_description="The field's description, this maps to the HTML <code>description</code> attribute.",
)
#: Optional: a placeholder that is displayed when the field has no value. Optional.
placeholder = schema.String(
_optional=True,
_description="A placeholder that is displayed when the field has no value.",
)
#: Represents whether or not this field is required.
required = schema.Boolean(
_description="Represents whether or not this field is required."
)
autocomplete = schema.String(
_name="autoComplete",
_optional=True,
_description="The autocomplete merge field name",
)
[docs]class TextField(_BaseField):
"""A generic text field.
"""
#: The field's default value. Optional.
value = schema.String(_optional=True, _description="The field's default value.")
[docs]class CheckBoxField(_BaseField):
"""A generic checkbox field.
"""
#: A value that will get sent through when ``checked`` is ``True``.
value = schema.String(
_description="""A value that will get sent through when
<code>checked</code> is <code>True</code>."""
)
#: Represents whether or not the ``value`` should be inspected.
checked = schema.Boolean(
_description="Represents whether or not the <code>value</code> should be inspected."
)
[docs]class ChoiceField(_BaseField):
"""A generic field representing a single choice of
a number of choices.
"""
#: Represents the default choice (by its ``value``).
value = schema.String(
_optional=True,
_description="Represents the default choice (by its <code>value</code>).",
)
#: This represents the possible choices for this field's value.
values = schema.List(
schema.Dictionary(value=schema.String(), label=schema.String()),
_optional=True,
_description="This represents the possible choices for this field's value.",
)
[docs]class SelectField(ChoiceField):
"""A generic select field.
"""
[docs]class MultiSelectField(_BaseField):
"""A field representing multiple choice select.
Differs from a SelectField in that SelectField may have only one
selection (of the available choices), whereas a MultiSelectField
may have multiple choices selected for its value.
"""
#: This represents the default selected possible value(s) for this field's value.
value = schema.List(
schema.String(),
_optional=True,
_description="""This represents the default selected possible
value(s) for this field's value.""",
)
#: This represents the possible values this field can have (any number of).
values = schema.List(
schema.Dictionary(value=schema.String(), label=schema.String()),
_optional=True,
_description="""This represents the possible values this field
can have (any number of).""",
)
[docs]class RadioField(ChoiceField):
"""A field representing multiple linked radio buttons.
"""
[docs]class ReadOnlyField(TextField):
"""A read only field. Must contain a default value.
"""
#: The field's value.
value = schema.String(_description="The field's value.")
[docs]class HiddenField(TextField):
"""A hidden text field.
"""
[docs]class SystemHiddenField(HiddenField):
"""A hidden text field reserved for system use.
This should be retained everywhere normal HiddenFields are, but generally
should not be displayed to, or made editable by, users.
"""
[docs]class HiddenMultiSelectField(MultiSelectField):
"""A hidden field containing a list of values.
This should be literally a MultiSelectField, but hidden.
Differs from a HiddenField, which only has one value, and a
MultiSelectField by being hidden.
"""
[docs]class NumberField(TextField):
"""A generic numeric field.
"""
#: The minimum numeric value that this field can contain.
min_value = schema.Number(
_name="minValue",
_optional=True,
_description="The minimum numeric value that this field can contain.",
)
#: The maximum numeric value that this field can contain.
max_value = schema.Number(
_name="maxValue",
_optional=True,
_description="The maximum numeric value that this field can contain.",
)
#: Specifies the legal number intervals for this field.
step = schema.Number(
_optional=True,
_description="Specifies the legal number intervals for this field.",
)
#: A number representing this field's default value.
value = schema.Number(
_optional=True, _description="A number representing this field's value."
)
[docs]class IntegerField(NumberField):
"""A generic numeric field.
"""
#: The minimum numeric value that this field can contain.
min_value = schema.Integer(
_name="minValue",
_optional=True,
_description="The minimum numeric value that this field can contain.",
)
#: The maximum numeric value that this field can contain.
max_value = schema.Integer(
_name="maxValue",
_optional=True,
_description="The maximum numeric value that this field can contain.",
)
#: Specifies the legal number intervals for this field.
step = schema.Integer(
_optional=True,
_description="Specifies the legal integer intervals for this field.",
)
#: A number representing this field's default value.
value = schema.Integer(
_optional=True, _description="A integer representing this field's value."
)
[docs]class PaymentAmountField(IntegerField):
"""A field representing a payment amount as a multiple of a
currency's atomic units. For example, for USD this would represent
some number of cents. The currency may be specified as a separate
(CurrencyField or StrictCurrencyField) field.
"""
[docs]class RefundAmountField(PaymentAmountField):
"""A field representing a refund. See PaymentAmountField.
"""
[docs]class CouponAmountField(PaymentAmountField):
"""A field representing a coupon value (usually subtracted from the
item's original price). See PaymentAmountField.
"""
[docs]class CurrencyField(TextField):
"""A generic field representing some currency in any format.
"""
AUTOCOMPLETE = "transaction-currency"
[docs]class StrictCurrencyField(ChoiceField):
"""A choice field representing the set of 3-letter currency codes
defined by ISO4217.
"""
[docs]class PercentageField(NumberField):
"""A generic field representing a percentage.
"""
#: A number representing a percentage.
value = schema.Number(
_optional=True,
_minimum=0,
_maximum=1,
_description="A number representing a percentage.",
)
[docs]class TextAreaField(TextField):
"""A generic text area field.
"""
[docs]class DayField(TextField):
"""A text field representing a day of the year.
"""
#: Day field values are in ``MM-DD`` format.
value = schema.Regexp(
r"\d{2}-\d{2}$",
_optional=True,
_description="Day field values are in <code>MM-DD</code> format.",
)
[docs]class DateField(TextField):
"""A text field representing a date.
"""
#: Date field values are in ``YYYY-MM-DD`` format.
value = schema.Regexp(
r"\d{4}-\d{2}-\d{2}$",
_optional=True,
_description="Date field values are in <code>YYYY-MM-DD</code> format.",
)
[docs]class DateTimeField(TextField):
"""A text field representing a date and time.
"""
# This could match:
# 9999-99-99T99:99:99+99:99
#: Date time field values are in ``YYYY-MM-DDTHH:mm:SS+HH:mm`` format.
value = schema.Regexp(
r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+|-)\d{2}(:?\d{2})?)$",
_optional=True,
_description="Date time field values are in <code>YYYY-MM-DDTHH:mm:SS+HH:mm</code> format.",
)
[docs]class TimezoneOffsetField(TextField):
"""A text field representing an ISO 8601 timezone offset.
"""
# This could match +99:99
#: Timezone offset values are in ``±hh:mm``, ``±hhmm``, or ``±hh`` format, or ``Z``.
value = schema.Regexp(
r"(Z|(\+|-)\d{2}(:?\d{2})?)",
_optional=True,
_description=(
"Timezone offset values are in <code>±hh:mm</code>, "
"<code>±hhmm</code>, or <code>±hh</code> "
"format, or <code>Z</code>."
),
)
[docs]class URIField(TextField):
"""A generic text field representing a URI.
"""
[docs]class PathField(TextField):
"""A generic text field representing the path portion of a URI.
"""
[docs]class UserAgentField(TextField):
"""A generic text field representing a user agent.
"""
[docs]class IPAddressField(TextField):
"""A generic text field representing an IPv4 or IPv6 address.
"""
value = schema.Or(schema.IPv4(), schema.IPv6())
[docs]class IPv4AddressField(TextField):
"""A generic text field representing an IPv4 address.
"""
[docs]class IPv6AddressField(TextField):
"""A generic text field representing an IPv6 address.
"""
[docs]class PasswordField(TextField):
"""A generic password field.
"""
[docs]class EmailField(TextField):
"""A generic e-mail address field.
"""
AUTOCOMPLETE = "email"
# This matches: jane@invaliddomain jane
value = schema.Regexp(
r".+@.+$", _optional=True, _description="E-mail fields must contain an @ sign."
)
[docs]class SalutationField(ChoiceField):
"""A single choice field representing a user's salutation.
"""
AUTOCOMPLETE = "honorific-prefix"
[docs]class FullNameField(TextField):
"""A text field representing a user's full name.
"""
AUTOCOMPLETE = "name"
[docs]class FirstNameField(TextField):
"""A text field representing a user's first name.
"""
AUTOCOMPLETE = "given-name"
[docs]class MiddleNameField(TextField):
"""A text field representing a user's middle name.
"""
AUTOCOMPLETE = "additional-name"
[docs]class LastNameField(TextField):
"""A text field representing a user's last name.
"""
AUTOCOMPLETE = "family-name"
[docs]class AgeField(NumberField):
"""A text field representing a user's age.
"""
[docs]class BirthdayField(DayField):
"""A text field representing a user's birth _day_.
"""
[docs]class BirthdateField(DateField):
"""A text field representing a user's birth _date_.
"""
AUTOCOMPLETE = "bday"
[docs]class PhoneNumberField(TextField):
"""A text field representing a user's phone number.
"""
AUTOCOMPLETE = "tel"
[docs]class HomePhoneNumberField(PhoneNumberField):
"""A text field representing a user's home phone number.
"""
AUTOCOMPLETE = "home tel"
[docs]class WorkPhoneNumberField(PhoneNumberField):
"""A text field representing a user's work phone number.
"""
AUTOCOMPLETE = "work tel"
[docs]class MobilePhoneNumberField(PhoneNumberField):
"""A text field representing a user's mobile/cell phone number.
"""
AUTOCOMPLETE = "mobile tel"
[docs]class FaxNumberField(PhoneNumberField):
"""A text field representing a user's fax number.
"""
AUTOCOMPLETE = "fax tel"
[docs]class HomeFaxNumberField(FaxNumberField):
"""A text field representing a user's home fax number.
"""
AUTOCOMPLETE = "fax tel"
[docs]class WorkFaxNumberField(FaxNumberField):
"""A text field representing a user's work fax number.
"""
AUTOCOMPLETE = "fax tel"
[docs]class CompanyField(TextField):
"""A text field representing a user's company.
"""
AUTOCOMPLETE = "organization"
[docs]class JobTitleField(TextField):
"""A text field representing a user's job title.
"""
AUTOCOMPLETE = "organization-title"
[docs]class IndustryField(TextField):
"""A text field representing a user's industry.
"""
[docs]class NumberOfEmployeesField(NumberField):
"""A numeric field representing a company's employee count.
"""
[docs]class AnnualRevenueField(CurrencyField):
"""A currency field representing a user's annual revenue.
"""
[docs]class WebsiteField(URIField):
"""A URI field representing a user's website.
"""
AUTOCOMPLETE = "url"
[docs]class TagField(TextField):
"""A text field representing a single tag.
"""
[docs]class MultiTagField(_BaseField):
"""A field representing 0 or more tags.
"""
#: This represents the possible tags for this field's values.
values = schema.List(schema.String(), _optional=True)
#: This represents the selected tags for this field
selected = schema.List(schema.String(), _optional=True)
[docs]class HiddenMultiTagField(MultiTagField):
"""A hidden field representing 0 or more tags.
This should be literally a MultiTagField, but hidden.
Differs from a HiddenField, which only has one value, and a
MultiTagField by being hidden.
"""
[docs]class PostalCodeField(TextField):
"""An integer field representing a zip/postal code.
"""
AUTOCOMPLETE = "postal-code"
[docs]class AddressStreetField(TextField):
"""A text field representing the street identifier of a user's
address.
"""
AUTOCOMPLETE = "street-address"
[docs]class AddressCityField(TextField):
"""A text field representing the city identifier of a user's
address.
"""
AUTOCOMPLETE = "address-level2"
[docs]class AddressStateField(TextField):
"""A text field representing the state identifier of a user's
address.
"""
AUTOCOMPLETE = "address-level1"
[docs]class AddressStrictStateField(SelectField):
"""A choice field representing the set of country subdivisions
defined by ISO3166-2.
"""
AUTOCOMPLETE = "address-level1"
[docs]class AddressPostalCodeField(PostalCodeField):
"""A text field representing the postal code identifier of a
user's address.
"""
AUTOCOMPLETE = "postal-code"
[docs]class AddressCountryField(TextField):
"""A text field representing the country identifier of a user's
address. The values of these fields are free-form text, see
:class:`.AddressStrictCountryField` for a more restrictive
representation of country fields.
"""
AUTOCOMPLETE = "country-name"
[docs]class AddressStrictCountryField(SelectField):
"""A choice field representing the set of countries defined by
ISO3166-2. These fields' values must be in ISO3166-2 alpha-2
format.
"""
AUTOCOMPLETE = "country"
[docs]class BillingAddressStreetField(AddressStreetField):
"""A text field representing the street identifier of a user's
billing address.
"""
AUTOCOMPLETE = "billing street-address"
[docs]class BillingAddressCityField(AddressCityField):
"""A text field representing the city identifier of a user's
billing address.
"""
AUTOCOMPLETE = "billing address-level2"
[docs]class BillingAddressStateField(AddressStateField):
"""A text field representing the state identifier of a user's
billing address.
"""
AUTOCOMPLETE = "billing address-level1"
[docs]class BillingAddressStrictStateField(AddressStrictStateField):
"""A billing address version of the :class:`.AddressStrictStateField`.
"""
AUTOCOMPLETE = "billing address-level1"
[docs]class BillingAddressPostalCodeField(AddressPostalCodeField):
"""A text field representing the postal code identifier of a
user's billing address.
"""
AUTOCOMPLETE = "billing postal-code"
[docs]class BillingAddressCountryField(AddressCountryField):
"""A text field representing the country identifier of a user's
billing address.
"""
AUTOCOMPLETE = "billing country-name"
[docs]class BillingAddressStrictCountryField(AddressStrictCountryField):
"""A billing address version of the :class:`.AddressStrictCountryField`.
"""
AUTOCOMPLETE = "billing country"
[docs]class ShippingAddressStreetField(AddressStreetField):
"""A text field representing the street identifier of a user's
shipping address.
"""
AUTOCOMPLETE = "shipping street-address"
[docs]class ShippingAddressCityField(AddressCityField):
"""A text field representing the city identifier of a user's
shipping address.
"""
AUTOCOMPLETE = "shipping address-level2"
[docs]class ShippingAddressStateField(AddressStateField):
"""A text field representing the state identifier of a user's
shipping address.
"""
AUTOCOMPLETE = "shipping address-level1"
[docs]class ShippingAddressStrictStateField(AddressStrictStateField):
"""A shipping address version of the :class:`.AddressStrictStateField`.
"""
AUTOCOMPLETE = "shipping address-level1"
[docs]class ShippingAddressPostalCodeField(AddressPostalCodeField):
"""A text field representing the postal code identifier of a
user's shipping address.
"""
AUTOCOMPLETE = "shipping postal-code"
[docs]class ShippingAddressCountryField(AddressCountryField):
"""A text field representing the country identifier of a user's
shipping address.
"""
AUTOCOMPLETE = "shipping country-name"
[docs]class ShippingAddressStrictCountryField(AddressStrictCountryField):
"""A shipping address version of the :class:`.AddressStrictCountryField`.
"""
AUTOCOMPLETE = "shipping country"
[docs]class UserHandleField(TextField):
"""A user's handle or username on a website.
"""
[docs]class TwitterUserHandleField(UserHandleField):
"""A user's Twitter handle.
"""
[docs]class FacebookUserHandleField(UserHandleField):
"""A user's Facebook username.
"""
[docs]class LinkedInUserHandleField(UserHandleField):
"""A user's LinkedIn username.
"""
[docs]class GooglePlusUserHandleField(UserHandleField):
"""A user's Google Plus username.
"""
[docs]class GeographicLatitudeField(NumberField):
"""A geographic latitude in the WGS84 spatial reference system.
"""
[docs]class GeographicLongitudeField(NumberField):
"""A geographic longitude in the WGS84 spatial reference system.
"""
[docs]class GeographicElevationField(NumberField):
"""A geographic elevation relative to Mean Sea Level.
"""
[docs]class ViewportWidthField(IntegerField):
"""The horizontal number of pixels in a viewport (screen, window, etc.).
"""
[docs]class ViewportHeightField(IntegerField):
"""The vertical number of pixels in a viewport (screen, window, etc.).
"""
[docs]class LocaleField(TextField):
"""A generic field representing a locale in any format.
"""
AUTOCOMPLETE = "language"
[docs]class StrictLocaleField(ChoiceField):
"""A choice field representing the set of language codes as
defined by ISO639/BCP47.
"""
[docs]class RatingField(IntegerField):
"""A rating on a scale from `min_value` to `max_value`.
"""
[docs]class LeadScoreField(IntegerField):
"""A score ranking a lead relative to other leads.
"""
[docs]class LifetimeValueField(IntegerField):
"""A field representing a contact's lifetime value as a multiple of a
currency's atomic units. For example, for USD this would represent
some number of cents. The currency may be specified as a separate
(CurrencyField or StrictCurrencyField) field.
"""
[docs]class DurationMinutesField(IntegerField):
"""A duration expressed in seconds but which should be displayed
as a pair of minute and second inputs.
"""
[docs]class CenterViewerUUIDField(HiddenField):
"""The UUID of a viewer (visitor) on a website on which the Center tracking
code is installed.
"""
FIELD_SCHEMAS = {}
for name, clazz in list(six.iteritems(locals())):
if not inspect.isclass(clazz) or clazz == _BaseField:
continue
if issubclass(clazz, _BaseField):
FIELD_SCHEMAS[name] = clazz()
[docs]class Field(schema.Schema):
#: The class name of the exact kind of field, as a string.
kind = schema.String(
_choices=list(six.iterkeys(FIELD_SCHEMAS)),
_description="The field's exact type.",
)
#: A map containing the field's properties defined in the field class described by ``kind``.
field = schema.Or(
*six.itervalues(FIELD_SCHEMAS),
_description="The structure of this must match the type described by <code>kind</code>."
)
[docs] def materialize(self, sparse=False):
"""Materialize, setting autocomplete value before returning.
Parameters:
sparse (bool): Controls whether or not null properties
should be included in the result.
Returns:
dict: The dictionary representation of this instance.
(An alternative way to do this might be to make a special kind of
Property for `autocomplete`)
"""
result = super(Field, self).materialize(sparse=sparse)
kind = FIELD_SCHEMAS.get(self.kind, None)
autocomplete = getattr(kind, "AUTOCOMPLETE", None)
if not sparse or autocomplete is not None:
result["field"]["autoComplete"] = autocomplete
return result