Update atst to atat
This commit is contained in:
48
atat/forms/application.py
Normal file
48
atat/forms/application.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from .forms import BaseForm, remove_empty_string
|
||||
from wtforms.fields import StringField, TextAreaField, FieldList
|
||||
from wtforms.validators import Required, Optional, Length
|
||||
from atat.forms.validators import ListItemRequired, ListItemsUnique, Name, AlphaNumeric
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
class EditEnvironmentForm(BaseForm):
|
||||
name = StringField(
|
||||
label=translate("forms.environments.name_label"),
|
||||
validators=[Required(), Name(), Length(max=100)],
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
|
||||
|
||||
class NameAndDescriptionForm(BaseForm):
|
||||
name = StringField(
|
||||
label=translate("forms.application.name_label"),
|
||||
validators=[Required(), Name(), Length(max=100)],
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
description = TextAreaField(
|
||||
label=translate("forms.application.description_label"),
|
||||
validators=[Optional(), Length(max=1_000)],
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
|
||||
|
||||
class EnvironmentsForm(BaseForm):
|
||||
environment_names = FieldList(
|
||||
StringField(
|
||||
label=translate("forms.application.environment_names_label"),
|
||||
filters=[remove_empty_string],
|
||||
validators=[AlphaNumeric(), Length(max=100)],
|
||||
),
|
||||
validators=[
|
||||
ListItemRequired(
|
||||
message=translate(
|
||||
"forms.application.environment_names_required_validation_message"
|
||||
)
|
||||
),
|
||||
ListItemsUnique(
|
||||
message=translate(
|
||||
"forms.application.environment_names_unique_validation_message"
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
72
atat/forms/application_member.py
Normal file
72
atat/forms/application_member.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms.fields import FormField, FieldList, HiddenField, BooleanField
|
||||
from wtforms.validators import UUID
|
||||
from wtforms import Form
|
||||
|
||||
from .member import NewForm as BaseNewMemberForm
|
||||
from .data import ENV_ROLES, ENV_ROLE_NO_ACCESS as NO_ACCESS
|
||||
from atat.forms.fields import SelectField
|
||||
from atat.domain.permission_sets import PermissionSets
|
||||
from atat.utils.localization import translate
|
||||
from atat.forms.validators import AlphaNumeric
|
||||
from wtforms.validators import Length
|
||||
|
||||
|
||||
class EnvironmentForm(Form):
|
||||
environment_id = HiddenField(validators=[UUID()])
|
||||
environment_name = HiddenField(validators=[AlphaNumeric(), Length(max=100)])
|
||||
role = SelectField(
|
||||
environment_name,
|
||||
choices=ENV_ROLES,
|
||||
default=NO_ACCESS,
|
||||
filters=[lambda x: NO_ACCESS if x == "None" else x],
|
||||
)
|
||||
disabled = BooleanField("Revoke Access", default=False)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
_data = super().data
|
||||
if "role" in _data and _data["role"] == NO_ACCESS:
|
||||
_data["role"] = None
|
||||
return _data
|
||||
|
||||
|
||||
class PermissionsForm(FlaskForm):
|
||||
perms_env_mgmt = BooleanField(
|
||||
translate("portfolios.applications.members.form.env_mgmt.label"),
|
||||
default=False,
|
||||
description=translate(
|
||||
"portfolios.applications.members.form.env_mgmt.description"
|
||||
),
|
||||
)
|
||||
perms_team_mgmt = BooleanField(
|
||||
translate("portfolios.applications.members.form.team_mgmt.label"),
|
||||
default=False,
|
||||
description=translate(
|
||||
"portfolios.applications.members.form.team_mgmt.description"
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
_data = super().data
|
||||
_data.pop("csrf_token", None)
|
||||
perm_sets = []
|
||||
|
||||
if _data["perms_env_mgmt"]:
|
||||
perm_sets.append(PermissionSets.EDIT_APPLICATION_ENVIRONMENTS)
|
||||
|
||||
if _data["perms_team_mgmt"]:
|
||||
perm_sets.append(PermissionSets.EDIT_APPLICATION_TEAM)
|
||||
|
||||
_data["permission_sets"] = perm_sets
|
||||
return _data
|
||||
|
||||
|
||||
class NewForm(PermissionsForm):
|
||||
user_data = FormField(BaseNewMemberForm)
|
||||
environment_roles = FieldList(FormField(EnvironmentForm))
|
||||
|
||||
|
||||
class UpdateMemberForm(PermissionsForm):
|
||||
environment_roles = FieldList(FormField(EnvironmentForm))
|
13
atat/forms/ccpo_user.py
Normal file
13
atat/forms/ccpo_user.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms.validators import Required, Length
|
||||
from wtforms.fields import StringField
|
||||
|
||||
from atat.forms.validators import Number
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
class CCPOUserForm(FlaskForm):
|
||||
dod_id = StringField(
|
||||
translate("forms.new_member.dod_id_label"),
|
||||
validators=[Required(), Length(min=10, max=10), Number()],
|
||||
)
|
30
atat/forms/data.py
Normal file
30
atat/forms/data.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from atat.models import CSPRole
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
SERVICE_BRANCHES = [
|
||||
("air_force", translate("forms.portfolio.defense_component.choices.air_force")),
|
||||
("army", translate("forms.portfolio.defense_component.choices.army")),
|
||||
(
|
||||
"marine_corps",
|
||||
translate("forms.portfolio.defense_component.choices.marine_corps"),
|
||||
),
|
||||
("navy", translate("forms.portfolio.defense_component.choices.navy")),
|
||||
("space_force", translate("forms.portfolio.defense_component.choices.space_force")),
|
||||
("ccmd_js", translate("forms.portfolio.defense_component.choices.ccmd_js")),
|
||||
("dafa", translate("forms.portfolio.defense_component.choices.dafa")),
|
||||
("osd_psas", translate("forms.portfolio.defense_component.choices.osd_psas")),
|
||||
("other", translate("forms.portfolio.defense_component.choices.other")),
|
||||
]
|
||||
|
||||
ENV_ROLE_NO_ACCESS = "No Access"
|
||||
ENV_ROLES = [(role.name, role.value) for role in CSPRole] + [
|
||||
(ENV_ROLE_NO_ACCESS, ENV_ROLE_NO_ACCESS)
|
||||
]
|
||||
|
||||
JEDI_CLIN_TYPES = [
|
||||
("JEDI_CLIN_1", translate("JEDICLINType.JEDI_CLIN_1")),
|
||||
("JEDI_CLIN_2", translate("JEDICLINType.JEDI_CLIN_2")),
|
||||
("JEDI_CLIN_3", translate("JEDICLINType.JEDI_CLIN_3")),
|
||||
("JEDI_CLIN_4", translate("JEDICLINType.JEDI_CLIN_4")),
|
||||
]
|
93
atat/forms/edit_user.py
Normal file
93
atat/forms/edit_user.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import pendulum
|
||||
from copy import deepcopy
|
||||
from wtforms.fields.html5 import DateField, EmailField, TelField
|
||||
from wtforms.fields import RadioField, StringField
|
||||
from wtforms.validators import Email, DataRequired, Optional
|
||||
|
||||
from .fields import SelectField
|
||||
from .forms import BaseForm
|
||||
from .data import SERVICE_BRANCHES
|
||||
from atat.models.user import User
|
||||
from atat.utils.localization import translate
|
||||
from wtforms.validators import Length
|
||||
from atat.forms.validators import Number
|
||||
|
||||
from .validators import Name, DateRange, PhoneNumber
|
||||
|
||||
|
||||
USER_FIELDS = {
|
||||
"first_name": StringField(
|
||||
translate("forms.edit_user.first_name_label"),
|
||||
validators=[Name(), Length(max=100)],
|
||||
),
|
||||
"last_name": StringField(
|
||||
translate("forms.edit_user.last_name_label"),
|
||||
validators=[Name(), Length(max=100)],
|
||||
),
|
||||
"email": EmailField(translate("forms.edit_user.email_label"), validators=[Email()]),
|
||||
"phone_number": TelField(
|
||||
translate("forms.edit_user.phone_number_label"), validators=[PhoneNumber()]
|
||||
),
|
||||
"phone_ext": StringField("Extension", validators=[Number(), Length(max=10)]),
|
||||
"service_branch": SelectField(
|
||||
translate("forms.edit_user.service_branch_label"), choices=SERVICE_BRANCHES
|
||||
),
|
||||
"citizenship": RadioField(
|
||||
choices=[
|
||||
("United States", "United States"),
|
||||
("Foreign National", "Foreign National"),
|
||||
("Other", "Other"),
|
||||
]
|
||||
),
|
||||
"designation": RadioField(
|
||||
translate("forms.edit_user.designation_label"),
|
||||
choices=[
|
||||
("military", "Military"),
|
||||
("civilian", "Civilian"),
|
||||
("contractor", "Contractor"),
|
||||
],
|
||||
),
|
||||
"date_latest_training": DateField(
|
||||
translate("forms.edit_user.date_latest_training_label"),
|
||||
description=translate("forms.edit_user.date_latest_training_description"),
|
||||
validators=[
|
||||
DateRange(
|
||||
lower_bound=pendulum.duration(years=1),
|
||||
upper_bound=pendulum.duration(days=0),
|
||||
message="Must be a date within the last year.",
|
||||
)
|
||||
],
|
||||
format="%m/%d/%Y",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def inherit_field(unbound_field, required=True):
|
||||
kwargs = deepcopy(unbound_field.kwargs)
|
||||
if not "validators" in kwargs:
|
||||
kwargs["validators"] = []
|
||||
|
||||
if required:
|
||||
kwargs["validators"].append(DataRequired())
|
||||
else:
|
||||
kwargs["validators"].append(Optional())
|
||||
|
||||
return unbound_field.field_class(*unbound_field.args, **kwargs)
|
||||
|
||||
|
||||
def inherit_user_field(field_name):
|
||||
required = field_name in User.REQUIRED_FIELDS
|
||||
return inherit_field(USER_FIELDS[field_name], required=required)
|
||||
|
||||
|
||||
class EditUserForm(BaseForm):
|
||||
|
||||
first_name = inherit_user_field("first_name")
|
||||
last_name = inherit_user_field("last_name")
|
||||
email = inherit_user_field("email")
|
||||
phone_number = inherit_user_field("phone_number")
|
||||
phone_ext = inherit_user_field("phone_ext")
|
||||
service_branch = inherit_user_field("service_branch")
|
||||
citizenship = inherit_user_field("citizenship")
|
||||
designation = inherit_user_field("designation")
|
||||
date_latest_training = inherit_user_field("date_latest_training")
|
8
atat/forms/fields.py
Normal file
8
atat/forms/fields.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from wtforms.fields import SelectField as SelectField_
|
||||
|
||||
|
||||
class SelectField(SelectField_):
|
||||
def __init__(self, *args, **kwargs):
|
||||
render_kw = kwargs.get("render_kw", {})
|
||||
kwargs["render_kw"] = {**render_kw, "required": False}
|
||||
super().__init__(*args, **kwargs)
|
46
atat/forms/forms.py
Normal file
46
atat/forms/forms.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from flask import current_app, request as http_request
|
||||
import re
|
||||
|
||||
from atat.utils.flash import formatted_flash as flash
|
||||
|
||||
EMPTY_LIST_FIELD = ["", None]
|
||||
|
||||
|
||||
def remove_empty_string(value):
|
||||
# only return strings that contain non whitespace characters
|
||||
if value and re.search(r"\S", value):
|
||||
return value.strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class BaseForm(FlaskForm):
|
||||
def __init__(self, formdata=None, **kwargs):
|
||||
# initialize the form with data from the cache
|
||||
formdata = formdata or {}
|
||||
cached_data = current_app.form_cache.from_request(http_request)
|
||||
cached_data.update(formdata)
|
||||
super().__init__(cached_data, **kwargs)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
# remove 'csrf_token' key/value pair
|
||||
# remove empty strings and None from list fields
|
||||
# prevent values that are not an option in a RadioField from being saved to the DB
|
||||
_data = super().data
|
||||
_data.pop("csrf_token", None)
|
||||
for field in _data:
|
||||
if _data[field].__class__.__name__ == "list":
|
||||
_data[field] = [el for el in _data[field] if el not in EMPTY_LIST_FIELD]
|
||||
if self[field].__class__.__name__ == "RadioField":
|
||||
choices = [el[0] for el in self[field].choices]
|
||||
if _data[field] not in choices:
|
||||
_data[field] = None
|
||||
return _data
|
||||
|
||||
def validate(self, *args, flash_invalid=True, **kwargs):
|
||||
valid = super().validate(*args, **kwargs)
|
||||
if not valid and flash_invalid:
|
||||
flash("form_errors")
|
||||
return valid
|
30
atat/forms/member.py
Normal file
30
atat/forms/member.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms.fields.html5 import EmailField, TelField
|
||||
from wtforms.validators import Required, Email, Length, Optional
|
||||
from wtforms.fields import StringField
|
||||
|
||||
from atat.forms.validators import Number, PhoneNumber, Name
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
class NewForm(FlaskForm):
|
||||
first_name = StringField(
|
||||
label=translate("forms.new_member.first_name_label"),
|
||||
validators=[Required(), Name(), Length(max=100)],
|
||||
)
|
||||
last_name = StringField(
|
||||
label=translate("forms.new_member.last_name_label"),
|
||||
validators=[Required(), Name(), Length(max=100)],
|
||||
)
|
||||
email = EmailField(
|
||||
translate("forms.new_member.email_label"), validators=[Required(), Email()]
|
||||
)
|
||||
phone_number = TelField(
|
||||
translate("forms.new_member.phone_number_label"),
|
||||
validators=[Optional(), PhoneNumber()],
|
||||
)
|
||||
phone_ext = StringField("Extension", validators=[Number(), Length(max=10)])
|
||||
dod_id = StringField(
|
||||
translate("forms.new_member.dod_id_label"),
|
||||
validators=[Required(), Length(min=10), Number()],
|
||||
)
|
47
atat/forms/portfolio.py
Normal file
47
atat/forms/portfolio.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from wtforms.fields import (
|
||||
SelectMultipleField,
|
||||
StringField,
|
||||
TextAreaField,
|
||||
)
|
||||
from wtforms.validators import Length, InputRequired
|
||||
from atat.forms.validators import Name
|
||||
from wtforms.widgets import ListWidget, CheckboxInput
|
||||
|
||||
from .forms import BaseForm
|
||||
from atat.utils.localization import translate
|
||||
|
||||
from .data import SERVICE_BRANCHES
|
||||
|
||||
|
||||
class PortfolioForm(BaseForm):
|
||||
name = StringField(
|
||||
translate("forms.portfolio.name.label"),
|
||||
validators=[
|
||||
Length(
|
||||
min=4,
|
||||
max=100,
|
||||
message=translate("forms.portfolio.name.length_validation_message"),
|
||||
),
|
||||
Name(),
|
||||
],
|
||||
)
|
||||
description = TextAreaField(
|
||||
translate("forms.portfolio.description.label"), validators=[Length(max=1_000)]
|
||||
)
|
||||
|
||||
|
||||
class PortfolioCreationForm(PortfolioForm):
|
||||
defense_component = SelectMultipleField(
|
||||
translate("forms.portfolio.defense_component.title"),
|
||||
description=translate("forms.portfolio.defense_component.help_text"),
|
||||
choices=SERVICE_BRANCHES,
|
||||
widget=ListWidget(prefix_label=False),
|
||||
option_widget=CheckboxInput(),
|
||||
validators=[
|
||||
InputRequired(
|
||||
message=translate(
|
||||
"forms.portfolio.defense_component.validation_message"
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
64
atat/forms/portfolio_member.py
Normal file
64
atat/forms/portfolio_member.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from wtforms.validators import Required
|
||||
from wtforms.fields import BooleanField, FormField
|
||||
|
||||
from .forms import BaseForm
|
||||
from .member import NewForm as BaseNewMemberForm
|
||||
from atat.domain.permission_sets import PermissionSets
|
||||
from atat.forms.fields import SelectField
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
class PermissionsForm(BaseForm):
|
||||
perms_app_mgmt = BooleanField(
|
||||
translate("forms.new_member.app_mgmt.label"),
|
||||
default=False,
|
||||
description=translate("forms.new_member.app_mgmt.description"),
|
||||
)
|
||||
perms_funding = BooleanField(
|
||||
translate("forms.new_member.funding.label"),
|
||||
default=False,
|
||||
description=translate("forms.new_member.funding.description"),
|
||||
)
|
||||
perms_reporting = BooleanField(
|
||||
translate("forms.new_member.reporting.label"),
|
||||
default=False,
|
||||
description=translate("forms.new_member.reporting.description"),
|
||||
)
|
||||
perms_portfolio_mgmt = BooleanField(
|
||||
translate("forms.new_member.portfolio_mgmt.label"),
|
||||
default=False,
|
||||
description=translate("forms.new_member.portfolio_mgmt.description"),
|
||||
)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
_data = super().data
|
||||
_data.pop("csrf_token", None)
|
||||
perm_sets = []
|
||||
|
||||
if _data["perms_app_mgmt"]:
|
||||
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
|
||||
|
||||
if _data["perms_funding"]:
|
||||
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_FUNDING)
|
||||
|
||||
if _data["perms_reporting"]:
|
||||
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_REPORTS)
|
||||
|
||||
if _data["perms_portfolio_mgmt"]:
|
||||
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_ADMIN)
|
||||
|
||||
_data["permission_sets"] = perm_sets
|
||||
return _data
|
||||
|
||||
|
||||
class NewForm(PermissionsForm):
|
||||
user_data = FormField(BaseNewMemberForm)
|
||||
|
||||
|
||||
class AssignPPOCForm(PermissionsForm):
|
||||
role_id = SelectField(
|
||||
label=translate("forms.assign_ppoc.dod_id"),
|
||||
validators=[Required()],
|
||||
choices=[("", "- Select -")],
|
||||
)
|
176
atat/forms/task_order.py
Normal file
176
atat/forms/task_order.py
Normal file
@@ -0,0 +1,176 @@
|
||||
from wtforms.fields import (
|
||||
BooleanField,
|
||||
DecimalField,
|
||||
FieldList,
|
||||
FormField,
|
||||
StringField,
|
||||
HiddenField,
|
||||
)
|
||||
from wtforms.fields.html5 import DateField
|
||||
from wtforms.validators import (
|
||||
Required,
|
||||
Length,
|
||||
Optional,
|
||||
NumberRange,
|
||||
ValidationError,
|
||||
)
|
||||
from flask_wtf import FlaskForm
|
||||
import numbers
|
||||
|
||||
from atat.forms.validators import Number, AlphaNumeric
|
||||
|
||||
from .data import JEDI_CLIN_TYPES
|
||||
from .fields import SelectField
|
||||
from .forms import BaseForm, remove_empty_string
|
||||
from atat.utils.localization import translate
|
||||
from flask import current_app as app
|
||||
|
||||
MAX_CLIN_AMOUNT = 1_000_000_000
|
||||
|
||||
|
||||
def coerce_enum(enum_inst):
|
||||
if getattr(enum_inst, "value", None):
|
||||
return enum_inst.value
|
||||
else:
|
||||
return enum_inst
|
||||
|
||||
|
||||
def validate_funding(form, field):
|
||||
if (
|
||||
isinstance(form.total_amount.data, numbers.Number)
|
||||
and isinstance(field.data, numbers.Number)
|
||||
and form.total_amount.data < field.data
|
||||
):
|
||||
raise ValidationError(
|
||||
translate("forms.task_order.clin_funding_errors.obligated_amount_error")
|
||||
)
|
||||
|
||||
|
||||
def validate_date_in_range(form, field):
|
||||
contract_start = app.config.get("CONTRACT_START_DATE")
|
||||
contract_end = app.config.get("CONTRACT_END_DATE")
|
||||
|
||||
if field.data and (field.data < contract_start or field.data > contract_end):
|
||||
raise ValidationError(
|
||||
translate(
|
||||
"forms.task_order.pop_errors.range",
|
||||
{
|
||||
"start": contract_start.strftime("%b %d, %Y"),
|
||||
"end": contract_end.strftime("%b %d, %Y"),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def remove_dashes(value):
|
||||
return value.replace("-", "") if value else None
|
||||
|
||||
|
||||
def coerce_upper(value):
|
||||
return value.upper() if value else None
|
||||
|
||||
|
||||
class CLINForm(FlaskForm):
|
||||
jedi_clin_type = SelectField(
|
||||
translate("task_orders.form.clin_type_label"),
|
||||
choices=JEDI_CLIN_TYPES,
|
||||
coerce=coerce_enum,
|
||||
)
|
||||
|
||||
number = StringField(
|
||||
label=translate("task_orders.form.clin_number_label"),
|
||||
validators=[Number(), Length(max=4)],
|
||||
)
|
||||
start_date = DateField(
|
||||
translate("task_orders.form.pop_start"),
|
||||
description=translate("task_orders.form.pop_example"),
|
||||
format="%m/%d/%Y",
|
||||
validators=[validate_date_in_range],
|
||||
)
|
||||
end_date = DateField(
|
||||
translate("task_orders.form.pop_end"),
|
||||
description=translate("task_orders.form.pop_example"),
|
||||
format="%m/%d/%Y",
|
||||
validators=[validate_date_in_range],
|
||||
)
|
||||
total_amount = DecimalField(
|
||||
label=translate("task_orders.form.total_funds_label"),
|
||||
validators=[
|
||||
NumberRange(
|
||||
0,
|
||||
MAX_CLIN_AMOUNT,
|
||||
translate("forms.task_order.clin_funding_errors.funding_range_error"),
|
||||
)
|
||||
],
|
||||
)
|
||||
obligated_amount = DecimalField(
|
||||
label=translate("task_orders.form.obligated_funds_label"),
|
||||
validators=[
|
||||
validate_funding,
|
||||
NumberRange(
|
||||
0,
|
||||
MAX_CLIN_AMOUNT,
|
||||
translate("forms.task_order.clin_funding_errors.funding_range_error"),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
valid = super().validate(*args, **kwargs)
|
||||
|
||||
if (
|
||||
self.start_date.data
|
||||
and self.end_date.data
|
||||
and self.start_date.data > self.end_date.data
|
||||
):
|
||||
self.start_date.errors.append(
|
||||
translate("forms.task_order.pop_errors.date_order")
|
||||
)
|
||||
valid = False
|
||||
|
||||
return valid
|
||||
|
||||
|
||||
class AttachmentForm(BaseForm):
|
||||
filename = HiddenField(
|
||||
id="attachment_filename",
|
||||
validators=[
|
||||
Length(
|
||||
max=100, message=translate("forms.attachment.filename.length_error")
|
||||
),
|
||||
AlphaNumeric(),
|
||||
],
|
||||
)
|
||||
object_name = HiddenField(
|
||||
id="attachment_object_name",
|
||||
validators=[
|
||||
Length(
|
||||
max=40, message=translate("forms.attachment.object_name.length_error")
|
||||
),
|
||||
AlphaNumeric(),
|
||||
],
|
||||
)
|
||||
accept = ".pdf,application/pdf"
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
return super().validate(*args, **{**kwargs, "flash_invalid": False})
|
||||
|
||||
|
||||
class TaskOrderForm(BaseForm):
|
||||
number = StringField(
|
||||
label=translate("forms.task_order.number_description"),
|
||||
filters=[remove_empty_string, remove_dashes, coerce_upper],
|
||||
validators=[AlphaNumeric(), Length(min=13, max=17), Optional()],
|
||||
)
|
||||
pdf = FormField(AttachmentForm)
|
||||
clins = FieldList(FormField(CLINForm))
|
||||
|
||||
|
||||
class SignatureForm(BaseForm):
|
||||
signature = BooleanField(
|
||||
translate("task_orders.sign.digital_signature_description"),
|
||||
validators=[Required()],
|
||||
)
|
||||
confirm = BooleanField(
|
||||
translate("task_orders.sign.confirmation_description"), validators=[Required()],
|
||||
)
|
104
atat/forms/validators.py
Normal file
104
atat/forms/validators.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from datetime import datetime
|
||||
import re
|
||||
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from wtforms.validators import ValidationError, Regexp
|
||||
import pendulum
|
||||
|
||||
from atat.utils.localization import translate
|
||||
|
||||
|
||||
def DateRange(lower_bound=None, upper_bound=None, message=None):
|
||||
def _date_range(form, field):
|
||||
if field.data is None:
|
||||
return
|
||||
|
||||
now = pendulum.now().date()
|
||||
|
||||
if isinstance(field.data, str):
|
||||
date = datetime.strptime(field.data, field.format)
|
||||
else:
|
||||
date = field.data
|
||||
|
||||
if lower_bound is not None:
|
||||
if (now - lower_bound) > date:
|
||||
raise ValidationError(message)
|
||||
|
||||
if upper_bound is not None:
|
||||
if (now + upper_bound) < date:
|
||||
raise ValidationError(message)
|
||||
|
||||
return _date_range
|
||||
|
||||
|
||||
def Number(message=translate("forms.validators.is_number_message")):
|
||||
def _is_number(form, field):
|
||||
if field.data:
|
||||
try:
|
||||
int(field.data)
|
||||
except (ValueError, TypeError):
|
||||
raise ValidationError(message)
|
||||
|
||||
return _is_number
|
||||
|
||||
|
||||
def PhoneNumber(message=translate("forms.validators.phone_number_message")):
|
||||
def _is_phone_number(form, field):
|
||||
digits = re.sub(r"\D", "", field.data)
|
||||
if len(digits) not in [5, 10]:
|
||||
raise ValidationError(message)
|
||||
|
||||
match = re.match(r"[\d\-\(\) ]+", field.data)
|
||||
if not match or match.group() != field.data:
|
||||
raise ValidationError(message)
|
||||
|
||||
return _is_phone_number
|
||||
|
||||
|
||||
def Name(message=translate("forms.validators.name_message")):
|
||||
def _name(form, field):
|
||||
match = re.match(r"[\w \,\.\'\-]+", field.data)
|
||||
if not match or match.group() != field.data:
|
||||
raise ValidationError(message)
|
||||
|
||||
return _name
|
||||
|
||||
|
||||
def ListItemRequired(
|
||||
message=translate("forms.validators.list_item_required_message"),
|
||||
empty_values=[None],
|
||||
):
|
||||
def _list_item_required(form, field):
|
||||
non_empty_values = [
|
||||
v for v in field.data if (v not in empty_values and re.search(r"\S", v))
|
||||
]
|
||||
if len(non_empty_values) == 0:
|
||||
raise ValidationError(message)
|
||||
|
||||
return _list_item_required
|
||||
|
||||
|
||||
def ListItemsUnique(message=translate("forms.validators.list_items_unique_message")):
|
||||
def _list_items_unique(form, field):
|
||||
if len(field.data) > len(set(field.data)):
|
||||
raise ValidationError(message)
|
||||
|
||||
return _list_items_unique
|
||||
|
||||
|
||||
def FileLength(max_length=50000000, message=None):
|
||||
def _file_length(_form, field):
|
||||
if field.data is None or not isinstance(field.data, FileStorage):
|
||||
return True
|
||||
|
||||
content = field.data.read()
|
||||
if len(content) > max_length:
|
||||
raise ValidationError(message)
|
||||
else:
|
||||
field.data.seek(0)
|
||||
|
||||
return _file_length
|
||||
|
||||
|
||||
def AlphaNumeric(message=translate("forms.validators.alpha_numeric_message")):
|
||||
return Regexp(regex=r"^[A-Za-z0-9\-_ \.]*$", message=message)
|
Reference in New Issue
Block a user