first commit
This commit is contained in:
0
helix/forms/__init__.py
Normal file
0
helix/forms/__init__.py
Normal file
17
helix/forms/conditional_validator.py
Normal file
17
helix/forms/conditional_validator.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from wtforms.validators import Optional
|
||||
|
||||
|
||||
class ConditionalValidator(object):
|
||||
def __init__(self, other_field_name, other_data_contents, dependent_validator):
|
||||
self.other_field_name = other_field_name
|
||||
self.other_data_contents = other_data_contents
|
||||
self.dependent_validator = dependent_validator
|
||||
|
||||
def __call__(self, form, field):
|
||||
other_field = form._fields.get(self.other_field_name)
|
||||
if other_field is None:
|
||||
raise Exception('no field named "%s" in form' % self.other_field_name)
|
||||
if other_field.data in self.other_data_contents:
|
||||
self.dependent_validator.__call__(form, field)
|
||||
else:
|
||||
Optional().__call__(form, field)
|
||||
183
helix/forms/ebom_form.py
Normal file
183
helix/forms/ebom_form.py
Normal file
@@ -0,0 +1,183 @@
|
||||
from wtforms import StringField, SelectField, FormField, BooleanField
|
||||
from wtforms.fields.html5 import IntegerField
|
||||
from wtforms.validators import NumberRange, DataRequired
|
||||
|
||||
from helix.constants.inverter_brand import InverterBrand
|
||||
from helix.constants.inverter_type import InverterType
|
||||
from helix.constants.system_type import SystemType
|
||||
from helix.forms.grouped_form import GroupedForm
|
||||
|
||||
|
||||
def generate_string_choices(from_i, to_i, only_even=False):
|
||||
step = 2 if only_even else 1
|
||||
return list(
|
||||
map(lambda x: (x, "%s" % x), range(from_i, to_i + 1, step))
|
||||
)
|
||||
|
||||
|
||||
class InverterBrandForm(GroupedForm):
|
||||
form_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden', 'value': "inverter_brand_form"})
|
||||
inverter_brand_id = SelectField('',
|
||||
choices=[(InverterBrand.SMA.value, InverterBrand.SMA.label),
|
||||
(InverterBrand.DELTA.value, InverterBrand.DELTA.label)],
|
||||
coerce=int,
|
||||
default=InverterBrand.default_value(),
|
||||
render_kw={'group': 'inverter_brands'},
|
||||
)
|
||||
|
||||
def populate_choices(self, inverter_brands):
|
||||
if len(inverter_brands) > 0:
|
||||
self.inverter_brand_id.default = next(
|
||||
map(lambda x: x['inverter_brand_id'], inverter_brands)
|
||||
, InverterBrand.default_value())
|
||||
self.process()
|
||||
|
||||
def is_delta(self):
|
||||
return self.inverter_brand_id.data == InverterBrand.DELTA.value
|
||||
|
||||
|
||||
class InverterFormSMA(GroupedForm):
|
||||
quantity = IntegerField('Quantity',
|
||||
default=1,
|
||||
render_kw={'group': 'quantity', 'row_class': 'quantity'},
|
||||
validators=[NumberRange(0, None)])
|
||||
model = SelectField('Model',
|
||||
choices=[(InverterType.SMA.MODEL_12KW.value, InverterType.SMA.MODEL_12KW.label),
|
||||
(InverterType.SMA.MODEL_15KW.value, InverterType.SMA.MODEL_15KW.label),
|
||||
(InverterType.SMA.MODEL_20KW.value, InverterType.SMA.MODEL_20KW.label),
|
||||
(InverterType.SMA.MODEL_24KW.value, InverterType.SMA.MODEL_24KW.label)],
|
||||
coerce=int,
|
||||
default=InverterType.SMA.default_value(),
|
||||
render_kw={'group': 'non-optional', 'row_class': 'inverter_model'},
|
||||
)
|
||||
strings_per_inverter = SelectField('# Strings/Inverter',
|
||||
coerce=int,
|
||||
choices=generate_string_choices(2, 8),
|
||||
default=8,
|
||||
render_kw={'group': 'non-optional', 'row_class': 'inverter_strings'})
|
||||
sunshade = BooleanField('Sun Shade', render_kw={'group': 'optional'})
|
||||
dc_switch = BooleanField('DC Switch', render_kw={'group': 'optional'})
|
||||
|
||||
def update_strings(self, system_type):
|
||||
self.strings_per_inverter.choices = generate_string_choices(
|
||||
2,
|
||||
8,
|
||||
system_type != SystemType.singleTilt
|
||||
)
|
||||
|
||||
|
||||
class InverterFormDelta(GroupedForm):
|
||||
quantity = IntegerField('Quantity',
|
||||
default=1,
|
||||
render_kw={'group': 'quantity', 'row_class': 'quantity'},
|
||||
validators=[NumberRange(0, None)])
|
||||
model = SelectField('Model',
|
||||
choices=[(InverterType.DELTA.MODEL_36KW.value, InverterType.DELTA.MODEL_36KW.label),
|
||||
(InverterType.DELTA.MODEL_42KW.value, InverterType.DELTA.MODEL_42KW.label),
|
||||
(InverterType.DELTA.MODEL_60KW.value, InverterType.DELTA.MODEL_60KW.label),
|
||||
# (InverterType.DELTA.MODEL_80KW.value, InverterType.DELTA.MODEL_80KW.label),
|
||||
],
|
||||
coerce=int,
|
||||
default=InverterType.DELTA.default_value(),
|
||||
render_kw={'group': 'non-optional', 'row_class': 'inverter_model'},
|
||||
)
|
||||
strings_per_inverter = SelectField('# Strings/Inverter',
|
||||
coerce=int,
|
||||
choices=generate_string_choices(0, 24),
|
||||
default=8,
|
||||
render_kw={'group': 'non-optional', 'row_class': 'inverter_strings'})
|
||||
splice_box = BooleanField('Splice Box', default=True, render_kw={'group': 'optional'})
|
||||
|
||||
|
||||
class EbomForm(GroupedForm):
|
||||
power_station_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden'})
|
||||
form_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden', 'value': 'power_station_form'})
|
||||
power_station_description = StringField('Power Station Description', render_kw={'group': 'header'},
|
||||
validators=[DataRequired(message='Power Station Description is required.')],
|
||||
default='Power Station 1')
|
||||
power_station_quantity = IntegerField('Power Station Quantity',
|
||||
default=1,
|
||||
validators=[NumberRange(0, None)],
|
||||
render_kw={'group': 'header'})
|
||||
ac_run_length = IntegerField('Total AC Run Length for Power Station(s) (ft)',
|
||||
render_kw={'group': 'header'}, default=0, validators=[NumberRange(0, None)])
|
||||
monitor_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden'})
|
||||
|
||||
inverter_quantity = SelectField('Inverters', choices=[(1, "1"), (2, "2"), (3, "3"), (4, "4")],
|
||||
coerce=int,
|
||||
default=4,
|
||||
render_kw={'group': 'inverter_quantity'})
|
||||
|
||||
inverter_1 = FormField(InverterFormSMA, 'Inverter 1', render_kw={'group': 'inverters'})
|
||||
inverter_2 = FormField(InverterFormSMA, 'Inverter 2', render_kw={'group': 'inverters'})
|
||||
inverter_3 = FormField(InverterFormSMA, 'Inverter 3', render_kw={'group': 'inverters'})
|
||||
inverter_4 = FormField(InverterFormSMA, 'Inverter 4', render_kw={'group': 'inverters'})
|
||||
|
||||
def update_inverter_strings_choices(self, system_type):
|
||||
self.inverter_1.update_strings(system_type)
|
||||
self.inverter_2.update_strings(system_type)
|
||||
self.inverter_3.update_strings(system_type)
|
||||
self.inverter_4.update_strings(system_type)
|
||||
|
||||
|
||||
class StandAloneInverterForm(GroupedForm):
|
||||
standalone_inverter_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden'})
|
||||
form_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden', 'value': "standalone_inverter_form"})
|
||||
standalone_ac_run_length = IntegerField('AC Run Length for Inverter (ft)',
|
||||
render_kw={'group': 'power_station'},
|
||||
default=0, validators=[NumberRange(0, None)])
|
||||
|
||||
def update_inverter_strings_choices(self, system_type):
|
||||
self.inverter.update_strings(system_type)
|
||||
|
||||
def populate_choices(self):
|
||||
pass
|
||||
|
||||
|
||||
class StandAloneInverterFormSMA(StandAloneInverterForm):
|
||||
inverter = FormField(InverterFormSMA, 'Inverter', render_kw={'group': 'inverters'})
|
||||
attachment_point = SelectField('Attachment Point',
|
||||
choices=[],
|
||||
default='switch_gear',
|
||||
render_kw={'group': 'power_station'})
|
||||
|
||||
def populate_choices(self, power_stations, standalone_inverters):
|
||||
standalone_inverter_count_per_power_station = {}
|
||||
for inverter in standalone_inverters:
|
||||
key = inverter['attachment_point'][1]
|
||||
standalone_count = standalone_inverter_count_per_power_station.get(key) or 0
|
||||
standalone_inverter_count_per_power_station[key] = standalone_count + 1
|
||||
|
||||
power_stations_with_free_slots = []
|
||||
for power_station in power_stations:
|
||||
standalone_count = standalone_inverter_count_per_power_station.get(power_station['power_station_id']) or 0
|
||||
inverter_count = power_station['inverter_quantity'] + standalone_count
|
||||
if inverter_count < 4 and power_station['power_station_quantity'] == 1:
|
||||
power_stations_with_free_slots.append(power_station)
|
||||
|
||||
choices = map(lambda x: (str(x['power_station_id']), x['power_station_description']), power_stations_with_free_slots)
|
||||
self.attachment_point.choices = [('switch_gear', 'Switch Gear')] + list(choices)
|
||||
|
||||
|
||||
class StandAloneInverterFormDelta(StandAloneInverterForm):
|
||||
inverter = FormField(InverterFormDelta, 'Inverter', render_kw={'group': 'inverters'})
|
||||
|
||||
|
||||
class SupervisorForm(GroupedForm):
|
||||
monitor_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden'})
|
||||
form_id = StringField(render_kw={'group': 'hidden', 'class': 'hidden', 'value': "supervisor_form"})
|
||||
power_source = SelectField('Power Source',
|
||||
render_kw={'group': 'power_source'},
|
||||
choices=[('switch_gear', 'Switch Gear/External')])
|
||||
|
||||
|
||||
class SupervisorFormSMA(SupervisorForm):
|
||||
def populate_choices(self, power_stations, supervisors):
|
||||
supervisor_power_sources = list(map(lambda x: x['power_source'][1], supervisors))
|
||||
power_stations_without_supervisors = []
|
||||
for power_station in power_stations:
|
||||
if power_station['power_station_id'] not in supervisor_power_sources:
|
||||
power_stations_without_supervisors.append(power_station)
|
||||
|
||||
choices = map(lambda x: (str(x['power_station_id']), x['power_station_description']), power_stations_without_supervisors)
|
||||
self.power_source.choices = self.power_source.choices[:1] + list(choices)
|
||||
6
helix/forms/grouped_form.py
Normal file
6
helix/forms/grouped_form.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from flask.ext.wtf import Form
|
||||
|
||||
class GroupedForm(Form):
|
||||
def group(self, label):
|
||||
return [field for field in self if field.render_kw and field.render_kw['group'] == label]
|
||||
|
||||
95
helix/forms/input_form.py
Normal file
95
helix/forms/input_form.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from flask.ext.wtf.file import FileField
|
||||
|
||||
from helix.constants.anchor_type import AnchorType
|
||||
from helix.constants.exposure_category import ExposureCategory
|
||||
from helix.constants.module_type import ModuleType
|
||||
from helix.constants.system_type import SystemType
|
||||
from wtforms import SelectField, StringField, BooleanField
|
||||
from wtforms.fields.html5 import DecimalField, IntegerField
|
||||
from wtforms.validators import NumberRange, DataRequired
|
||||
|
||||
from helix.forms.conditional_validator import ConditionalValidator
|
||||
from helix.forms.grouped_form import GroupedForm
|
||||
|
||||
|
||||
class InputForm(GroupedForm):
|
||||
project_name = StringField('Project Name', validators=[DataRequired(message='Project Name is required.')],
|
||||
render_kw={'group': 'project_info'})
|
||||
building_height = DecimalField('Building Height (ft)', places=1, validators=[NumberRange(0, None)],
|
||||
render_kw={'group': 'site_info'})
|
||||
building_width = DecimalField('Building Width (ft)', places=1, validators=[NumberRange(0, None)],
|
||||
render_kw={'group': 'site_info'})
|
||||
building_length = DecimalField('Building Length (ft)', places=1, validators=[NumberRange(0, None)],
|
||||
render_kw={'group': 'site_info'})
|
||||
building_parapet_height = DecimalField('Parapet Height (ft)', places=1, validators=[NumberRange(0, None)],
|
||||
render_kw={'group': 'site_info'})
|
||||
wind_speed = IntegerField('Wind Speed (ASCE 7-10) (mph)',
|
||||
validators=[NumberRange(100, 200)],
|
||||
render_kw={'group': 'site_info',
|
||||
'link': {'text': 'Look up',
|
||||
'href': 'http://windspeed.atcouncil.org/'}})
|
||||
exposure_category = SelectField('Exposure Category',
|
||||
choices=[(ExposureCategory.B.value, ExposureCategory.B.value),
|
||||
(ExposureCategory.B_C.value, "B to C"),
|
||||
(ExposureCategory.C_B.value, "C to B"),
|
||||
(ExposureCategory.C.value, ExposureCategory.C.value),
|
||||
(ExposureCategory.D.value, ExposureCategory.D.value)],
|
||||
default=ExposureCategory.default_value(),
|
||||
render_kw={'group': 'site_info',
|
||||
'link': {'text': 'More info',
|
||||
'href': '/exposure_categories'}
|
||||
})
|
||||
exposure_category_transition_distance = IntegerField('Exposure Transition Distance (ft)',
|
||||
default=0,
|
||||
validators=[ConditionalValidator('exposure_category',
|
||||
['B to C', 'C to B'],
|
||||
NumberRange(1, None))],
|
||||
render_kw={'group': 'site_info'})
|
||||
ballast_block_weight = DecimalField('Ballast Block Weight (lbs)',
|
||||
validators=[NumberRange(12, 20)],
|
||||
default=14,
|
||||
places=1,
|
||||
render_kw={'group': 'site_info'})
|
||||
max_system_pressure = DecimalField('Max Allowable System Pressure (psf)',
|
||||
places=1,
|
||||
validators=[NumberRange(0, None)],
|
||||
default=12,
|
||||
render_kw={'group': 'site_info'})
|
||||
system_type = SelectField('System Type',
|
||||
choices=[(SystemType.singleTilt.value, SystemType.singleTilt.display_name()),
|
||||
(SystemType.dualTilt.value, SystemType.dualTilt.display_name())],
|
||||
default=SystemType.default_value(),
|
||||
render_kw={'group': 'project_info'})
|
||||
module_type = SelectField('Module Type',
|
||||
choices=[(ModuleType.Cell128.value, ModuleType.Cell128.value),
|
||||
(ModuleType.PSeries.value, ModuleType.PSeries.value),
|
||||
(ModuleType.Cell96.value, ModuleType.Cell96.value)],
|
||||
default=ModuleType.default_value(),
|
||||
render_kw={'group': 'project_info'})
|
||||
anchor_type = SelectField('Anchor Type',
|
||||
choices=[(AnchorType.OMG_PowerGrip.value, AnchorType.OMG_PowerGrip.value),
|
||||
(AnchorType.OMG_PowerGrip_Plus.value, AnchorType.OMG_PowerGrip_Plus.value),
|
||||
(AnchorType.EcoFasten.value, AnchorType.EcoFasten.value)],
|
||||
default=AnchorType.default_value(),
|
||||
render_kw={'group': 'site_info', 'tooltip': 'OMG anchors are compatible with TPO and PVC roof membranes.<br>EcoFasten anchors are compatible with Built Up Roofing (BUR), Hot Tar, Sips Panels and membrane type roofs.'})
|
||||
design_spectral_response = DecimalField('Design Spectral Response Acceleration (S<sub>DS</sub>) (g)',
|
||||
places=1, validators=[NumberRange(0, 5)],
|
||||
render_kw={'group': 'site_info',
|
||||
'link': {'text': 'Look up',
|
||||
'href': 'http://earthquake.usgs.gov/designmaps/us/application.php'
|
||||
}
|
||||
})
|
||||
importance_factor = SelectField('Seismic Importance Factor (I<sub>p</sub>)',
|
||||
choices=[('1', 1), ('1.5', 1.5)],
|
||||
default=1,
|
||||
render_kw={'group': 'site_info', 'tooltip': 'Use 1.5 for essential facilities such as: Hospitals, Police, Fire & Rescue stations & Designated emergency shelters. All other structures should use 1.0.'})
|
||||
|
||||
|
||||
class ArrayForm(GroupedForm):
|
||||
file_upload = FileField('System Data (txt)', render_kw={'group': 'array_info', 'class': 'system_upload'})
|
||||
dxf_upload = FileField('Cad File (dxf)', render_kw={'group': 'dxf_file', 'class': 'system_upload'})
|
||||
|
||||
|
||||
class TestDXFForm(GroupedForm):
|
||||
dxf_upload = FileField('Cad File (dxf)', render_kw={'group': 'array_info', 'class': 'system_upload'})
|
||||
show_wind_zones = BooleanField('Show Wind Zones', default=True, render_kw={'group': 'array_info'})
|
||||
Reference in New Issue
Block a user