merge with upstream master

This commit is contained in:
Senad Uka
2017-12-19 15:18:35 +01:00
parent efb4547a37
commit 197db1003b
85 changed files with 2522 additions and 697 deletions

View File

@@ -1 +1 @@
web: python helix/main.py
web: gunicorn -c gunicorn_config.py --pythonpath helix main:app

View File

@@ -1,3 +1,6 @@
[![CircleCI](https://circleci.com/gh/SunPower/Helix_Roof_Calculator.svg?style=svg&circle-token=aacf2ae59dae99075992ed10d1e27f119e223e75)](https://circleci.com/gh/SunPower/Helix_Roof_Calculator)
## Helix Calculator
- [Staging](https://sp-helix-staging.herokuapp.com)
@@ -31,6 +34,22 @@
| google-chrome-stable/stable,now 62.0.3202.94-1 amd64 |
| nodejs/unknown,now 6.12.0-1nodesource1 amd64 |
##### Environment Variables
Set the environment variables for your specific environment. Use the `.env` file below as reference:
```
AWS_S3_BUCKET="..."
AWS_ACCESS_KEY_ID="..."
AWS_SECRET_ACCESS_KEY="..."
SF_BASE_URL="https://test.salesforce.com"
SFDC_ACCESS_KEY_ID="..."
SFDC_SECRET_ACCESS_KEY="..."
```
##### Set up for Develop and Testing
- Run the docker container for the 1st time
@@ -47,7 +66,7 @@
- Log in the docker container
```docker exec -t -i helix /bin/bash```
```docker exec -ti helix bash```
- Run this commands inside the docker conatiner
@@ -367,3 +386,9 @@ docker exec -it helixroofcalculator_helix_1 invoke db_migrate
```
Debugging is possible directly from PyCharm, all code changes are transparent between host and container.
## Database schema
![Database Schema](documentation/db_schema.png)

View File

@@ -1,51 +0,0 @@
version: 2
jobs:
build:
docker:
- image: ivannnn/heroku_cedar14:2.0
environment:
PGUSER: pivotal
PGPASSWORD: password
PASSWORD: password
environment:
PATH: /usr/local/rvm/gems/ruby-2.3.4/bin:/usr/local/rvm/gems/ruby-2.3.4@global/bin:/usr/local/rvm/rubies/ruby-2.3.4/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/rvm/bin
steps:
- checkout
- run:
name: Set up and Run tests
command: |
source /etc/profile.d/rvm.sh
rvm use 2.3.4
/etc/init.d/postgresql start
python3.4 -m venv env
source env/bin/activate
pip install invoke
invoke install
npm install
invoke db_migrate
invoke test_ci
- deploy:
name: staging
command: |
invoke update_heroku_version staging
"[[ ! -s \"$(git rev-parse --git-dir)/shallow\" ]] || git fetch --unshallow"
git push -f git@heroku.com:sp-helix-staging.git $CIRCLE_SHA1:refs/heads/master
heroku run --app sp-helix-staging invoke db_migrate
rake ci:deliver
name: preprod
command: |
invoke update_heroku_version preprod
"[[ ! -s \"$(git rev-parse --git-dir)/shallow\" ]] || git fetch --unshallow"
git push -f git@heroku.com:sp-helix-preprod.git $CIRCLE_SHA1:refs/heads/master
heroku run --app sp-helix-preprod invoke db_migrate
rake ci:deliver
name: production
commands: |
invoke update_heroku_version production
"[[ ! -s \"$(git rev-parse --git-dir)/shallow\" ]] || git fetch --unshallow"
git push -f git@heroku.com:sp-helix-production.git $CIRCLE_SHA1:refs/heads/master
heroku run --app sp-helix-production invoke db_migrate
rake ci:deliver
notify:
webhooks:
- url: http://pulse.pivotallabs.com/projects/03ba990f-b8f5-4508-b4c1-19038b2cb791/status

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
documentation/db_schema.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
dump.rdb Normal file

Binary file not shown.

13
gunicorn_config.py Normal file
View File

@@ -0,0 +1,13 @@
"""gunicorn WSGI server configuration."""
import multiprocessing
import os
bind = '0.0.0.0:' + os.getenv('PORT', '5000')
max_requests = 1000
worker_class = 'meinheld.gmeinheld.MeinheldWorker'
workers = multiprocessing.cpu_count()
timeout = os.getenv('TIMEOUT', 120)
graceful_timeout = os.getenv('TIMEOUT', 120)
keepalive = 5
daemon = False

View File

@@ -234,24 +234,43 @@ class DXFHelper(object):
node_store.add_node(node)
# Optimization: avoid creating thousands of replicated objects
graph_directions_cache = {}
for x in (0, 1, -1):
for y in (0, 1, -1):
if x == y == 0:
continue
graph_directions_cache[(x, y)] = GraphDirection((x, y))
for node in nodes:
if len(node.neighboring_nodes()) == 8:
continue
rotation = math.radians(node.coordinate.rotation)
# Optimization: Avoid replicate calculations thousands of times
x_spacing_cos_rotation = (node.x_spacing * math.cos(rotation))
y_spacing_sin_rotation = (node.y_spacing * math.sin(rotation))
x_spacing_sin_rotation = (node.x_spacing * math.sin(rotation))
y_spacing_cos_rotation = (node.y_spacing * math.cos(rotation))
for x in (0, 1, -1):
for y in (0, 1, -1):
if x == y == 0:
continue
rotation = math.radians(node.coordinate.rotation)
x_spacing = (x * node.x_spacing * math.cos(rotation)) - (y * node.y_spacing * math.sin(rotation))
y_spacing = (x * node.x_spacing * math.sin(rotation)) + (y * node.y_spacing * math.cos(rotation))
x_spacing = (x * x_spacing_cos_rotation) - (y * y_spacing_sin_rotation)
y_spacing = (x * x_spacing_sin_rotation) + (y * y_spacing_cos_rotation)
coordinate = Coordinate(node.coordinate.x + x_spacing, node.coordinate.y + y_spacing, node.coordinate.rotation)
if coordinate.x < 0 or coordinate.y < 0:
continue
direction = GraphDirection((x, y))
# Optimization for `direction = GraphDirection((x, y))`
direction = graph_directions_cache[(x, y)]
if node.has_existing_neighbor(direction):
continue
# FIXME: This is the bottleneck of the loop
# Calling this ~10000 times needs ~20 seconds
neighbor = node_store.find_coordinate(coordinate)
if neighbor:
node.add_neighbor(neighbor, direction)
@@ -417,7 +436,7 @@ class DXFHelper(object):
def __compute_segment_direction(p1, p2):
"""
Computes direction of a segment. Points taken from building outline are assumed to be in counterclockwise order.
:param p1: first point
:param p2: second point
:return: tuple representing orientation ('north', 'south', 'east', 'west') and variation in degrees from ideally
@@ -438,7 +457,7 @@ class DXFHelper(object):
def compute_corner_directions(vertex, prev, next, angle_correction):
"""
Determines if point is located in north/east corner
:param vertex: point located in the corner
:param prev: point previous to vertex, assuming counterclockwise order
:param next: point next to vertex, assuming counterclockwise order
@@ -510,7 +529,7 @@ class DXFHelper(object):
@staticmethod
def __generate_wind_zone__(buildings, scaling_factor, points_callback, panel_orientation):
"""
Important: polygons representing buildings are expected to have points in counterclockwise order
Important: polygons representing buildings are expected to have points in counterclockwise order
"""
wind_zones = []

View File

@@ -2,9 +2,6 @@ import io
import dxfgrabber
from helix.constants.file_validation_error import FileValidationMessage
from helix.models.dxf.dxf_error import OldDxfFormatException
class DXFService(object):
"""
@@ -51,6 +48,7 @@ class DXFService(object):
panels = dxf_helper.generate_panels(modules, translated_modules)
# FIXME: Building a graph with many entities is very slow
node_graph = dxf_helper.build_node_graph(panels, module_constants.panel_spacing)
subarrays = dxf_helper.detect_subarrays(node_graph, panels)
for subarray in subarrays:

View File

@@ -0,0 +1,26 @@
import boto3
import os
import uuid
def s3_upload(bytes_or_file_like, filename=None, file_extension=None):
'''
@bytes_or_file_like: bytes(), open('filepath') or io.StringIO('')
'''
if filename is None:
filename = uuid.uuid4().hex
if file_extension:
filename += file_extension
s3 = boto3.resource('s3',
aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'))
# Default: test environment
bucket_name = os.getenv('AWS_S3_BUCKET', 'sunpower-test-dgplatform-spectrum')
# Assuming bucket already exists
s3.Bucket(bucket_name).put_object(Key=filename, Body=bytes_or_file_like.read(), ACL='public-read')
file_url = 'https://s3.amazonaws.com/{}/{}'.format(bucket_name, filename)
print('Uploaded filename {} to S3: {}'.format(filename, file_url))
return file_url

View File

@@ -5,7 +5,6 @@ from helix.presenters.panel_presenter import ProjectPresenter
from helix.session_manager import SessionManager
from helix.constants import redis_constant, sql_constant
from helix.seismic_validator_user_values import SeismicValidatorUserValues
from helix.validators.file_validator import FileValidator
from helix.validators.seismic_anchor_validator import SeismicAnchorValidator
api = Blueprint('api', __name__, template_folder='templates')

View File

@@ -50,7 +50,7 @@ class BomCalculator(object):
row_count = sum(subarray.row_count for subarray in self.subarrays)
column_count = sum(subarray.column_count for subarray in self.subarrays)
parts_list = MechanicalBomCalculator(self.values, self.panels, self.subarrays).mechanical_bom()
ebom_parts_list = EbomCalculator(self.values, ceil(row_count), ceil(column_count), parts_list.get(module)).compute_ebom()
ebom_parts_list = EbomCalculator(self.values, ceil(row_count), ceil(column_count), parts_list.get(module), self.subarrays).compute_ebom()
add_parts_to_list(parts_list, ebom_parts_list)

View File

@@ -1,4 +1,3 @@
from math import ceil, floor
import copy
from helix.Repositories.graph_repository import GraphRepository
from helix.calculators.ballast_calculator import BallastCalculator

View File

@@ -6,15 +6,15 @@ from helix.constants.ebom_parts import *
from helix.constants.parts import wire_clip_large, cable_support, cable_support_lid, channel_nut, sunshade
from helix.constants.system_type import SystemType
from helix.constants.inverter_brand import InverterBrand
from helix.forms.ebom_form import InverterBrandForm
class EbomCalculator(object):
def __init__(self, user_values, row_count, column_count, modules_count = None):
def __init__(self, user_values, row_count, column_count, modules_count = None, panels = None):
self.values = user_values
self.row_count = row_count
self.column_count = column_count
self.modules_count = modules_count
self.panels = panels
def resolve_power_monitor_type(self):
module_type = self.values.module_type()
@@ -30,6 +30,14 @@ class EbomCalculator(object):
else:
return monitor_controller_240_v
def resolve_is_delta(self):
if len(self.values.inverter_brands()) > 0:
return self.values.inverter_brands()[0]['inverter_brand_id'] == InverterBrand.DELTA.value
if len(self.values.standalone_inverters()) > 0:
return self.values.standalone_inverters()[0]['model'].get_type == InverterBrand.DELTA.label
# can't determine without data
return False
def compute_ebom(self):
part_list = {}
@@ -38,25 +46,14 @@ class EbomCalculator(object):
monitors = self.values.power_monitors()
module_type = self.values.module_type()
system_type = self.values.system_type()
is_delta=None
try:
is_delta=(self.values.inverter_brands()[0]['inverter_brand_id']==InverterBrand.DELTA.value)
except IndexError :
#Some tests are calculating bom without providing inverter brand so inverter_brands is empty
#for those tests, inverter brand is irrelevant
is_delta=False
is_delta = self.resolve_is_delta()
inverter_count = 0
total_ac_run_length = 0
panel_board_counts = [0, 0]
proper_monitor_controller = self.resolve_power_monitor_type()
try:
is_delta = (self.values.inverter_brands()[0]['inverter_brand_id']==InverterBrand.DELTA.value)
except IndexError:
is_delta = False
for power_station in power_stations:
power_station_count = power_station['power_station_quantity']
total_ac_run_length += power_station['ac_run_length']
@@ -94,12 +91,17 @@ class EbomCalculator(object):
if (is_delta):
add_parts_to_list(part_list, {ethernet_plug: 2},1)
add_parts_to_list(part_list, {wire_clip_large: inverter_count}, self.row_count)
if is_delta:
clips_amount = inverter_count * self.row_count * 1.15
clips_rounded = int(ceil(clips_amount / 10.0)) * 10
add_parts_to_list(part_list, {wire_clip_large: clips_rounded})
else:
add_parts_to_list(part_list, {wire_clip_large: inverter_count}, self.row_count)
add_parts_to_list(part_list, {stump: 1}, ceil(total_ac_run_length / 4.0))
cable_supports = self.calculate_cable_supports(panel_board_counts, len(standalone_inverters))
cable_supports = self.calculate_cable_supports(panel_board_counts, len(standalone_inverters), is_delta)
add_parts_to_list(part_list, {cable_support: 1, cable_support_lid: 1}, cable_supports)
add_parts_to_list(part_list, {rear_skirt: -1}, ceil(cable_supports*.38))
add_parts_to_list(part_list, {rear_skirt_1_1: -1}, ceil(cable_supports*.38))
dependent_part_list = {}
@@ -121,21 +123,37 @@ class EbomCalculator(object):
if inverter['dc_switch']:
add_parts_to_list(part_list, dc_switch_parts, multiplier)
def calculate_cable_supports(self, panel_board_counts, standalone_inverter_count):
if sum(panel_board_counts) == 0:
def calculate_cable_supports(self, panel_board_counts, standalone_inverter_count, is_delta):
# There are some tests that don't have panels
if is_delta and self.panels is None:
return 0
if self.values.system_type() == SystemType.dualTilt:
dimension1 = self.column_count
dimension2 = self.row_count
if is_delta:
panels_count = len(self.panels)
if panels_count == 0:
return 0
if self.values.system_type() == SystemType.dualTilt:
Avg_Columns = self.column_count / panels_count
Col2Row_Ratio = self.column_count / self.row_count
Col2Row_Ratio = 1 if Col2Row_Ratio < 1 else Col2Row_Ratio
return ceil(standalone_inverter_count * 1.25 * Avg_Columns * Col2Row_Ratio + Avg_Columns)
else:
Avg_Rows = self.row_count / panels_count
Row2Col_Ratio = self.row_count / self.column_count
Row2Col_Ratio = 1 if Row2Col_Ratio < 1 else Row2Col_Ratio
return ceil(standalone_inverter_count * 1.25 * Avg_Rows * Row2Col_Ratio + Avg_Rows)
else:
dimension1 = self.row_count
dimension2 = self.column_count
result = (standalone_inverter_count + panel_board_counts[0] + (2 * panel_board_counts[1])) / sum(panel_board_counts)
result *= dimension1 * max(dimension1 / dimension2, 1)
result += dimension1
return ceil(result)
if sum(panel_board_counts) == 0:
return 0
if self.values.system_type() == SystemType.dualTilt:
dimension1 = self.column_count
dimension2 = self.row_count
else:
dimension1 = self.row_count
dimension2 = self.column_count
result = (standalone_inverter_count + panel_board_counts[0] + (2 * panel_board_counts[1])) / sum(panel_board_counts)
result *= dimension1 * max(dimension1 / dimension2, 1)
result += dimension1
return ceil(result)
def get_standalone_inverters(self, power_station):
standalone_inverters = self.values.standalone_inverters()

View File

@@ -5,7 +5,7 @@ from helix.calculators.bom_helper import add_parts_to_list, apply_fudge_factors,
from helix.calculators.subarray_helper import extract_subarray
from helix.constants.module_type import ModuleType
from helix.constants.panel_type import PanelType
from helix.constants.parts import link_tray, cross_tray, ballast, cross_tray_1_1, leading_tray
from helix.constants.parts import link_tray, cross_tray, ballast, cross_tray_1_1
from helix.constants.system_type import SystemType

View File

@@ -1,6 +1,5 @@
from math import ceil, floor
from helix.calculators.subarray_graph import SubarrayGraph
from helix.calculators.subarray_helper import extract_subarray
from helix.constants.global_constants import minimum_racking_capacity

View File

@@ -1,4 +1,3 @@
import copy
from enum import Enum
from helix.constants.system_type import SystemType

View File

@@ -7,6 +7,10 @@ class InverterTypeSMA(IntEnum):
MODEL_20KW = 6
MODEL_24KW = 8
@property
def get_type(self):
return "SMA"
@property
def default_string(self):
return {
@@ -49,6 +53,10 @@ class InverterTypeDelta(IntEnum):
MODEL_60KW = 11
MODEL_80KW = 12
@property
def get_type(self):
return "Delta"
@property
def label(self):
return {

View File

@@ -60,14 +60,37 @@ class DualTilt128CellConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
if panel_type == PanelType.Corner:
return [108.66, 110.96, 112.11, 116.44, 119.62, 122.80, 125.98][tray_count]
elif panel_type == PanelType.NorthSouth:
return [107.58, 109.88, 111.03, 114.21, 117.39, 120.57, 123.75][tray_count]
elif panel_type == PanelType.EastWest:
return [103.19, 105.49, 105.49, 108.67, 111.85, 115.03, 118.21][tray_count]
else:
return [102.11, 104.41, 104.41, 107.59, 110.77, 113.95, 117.13][tray_count]
values_per_panel_type = {
PanelType.Corner: [122.70,
124.55,
126.40,
129.55,
132.71,
135.86,
139.01][tray_count],
PanelType.NorthSouth: [121.63,
123.48,
125.33,
128.48,
131.64,
134.79,
137.94][tray_count],
PanelType.EastWest: [118.28,
120.13,
121.99,
125.14,
128.29,
131.45,
134.60][tray_count],
PanelType.Middle: [117.21,
119.06,
120.92,
124.07,
127.22,
130.38,
133.53][tray_count],
}
return values_per_panel_type.get(panel_type)
def link_tray_thresholds(self, panel_type):
if panel_type == PanelType.Corner or panel_type == PanelType.NorthSouth:

View File

@@ -60,23 +60,38 @@ class DualTilt96CellConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
if panel_type == PanelType.Corner or panel_type == PanelType.NorthSouth:
return [92.58,
94.31,
96.03,
98.33,
100.63,
102.93,
105.23][tray_count]
else:
return [87.11,
88.84,
89.41,
91.71,
94.01,
96.31,
98.61][tray_count]
values_per_panel_type = {
PanelType.Corner: [92.41,
94.26,
96.12,
98.54,
100.97,
103.39,
105.82][tray_count],
PanelType.NorthSouth: [91.63,
93.48,
95.33,
97.76,
100.18,
102.61,
105.03][tray_count],
PanelType.EastWest: [88.00,
89.85,
91.70,
94.13,
96.55,
98.98,
101.40][tray_count],
PanelType.Middle: [87.21,
89.06,
90.92,
93.34,
95.77,
98.19,
100.62][tray_count],
}
return values_per_panel_type.get(panel_type)
def link_tray_thresholds(self, panel_type):
if panel_type == PanelType.Corner or panel_type == PanelType.NorthSouth:
return [7.5, 10, 15]

View File

@@ -60,14 +60,37 @@ class DualTiltPSeriesConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
if panel_type == PanelType.Corner:
return [103.66, 105.96, 107.11, 111.44, 114.62, 117.80, 120.98][tray_count]
elif panel_type == PanelType.NorthSouth:
return [102.58, 104.88, 106.03, 109.21, 112.39, 115.57, 118.75][tray_count]
elif panel_type == PanelType.EastWest:
return [98.19, 100.49, 100.49, 103.67, 106.85, 110.03, 113.21][tray_count]
else:
return [97.11, 99.41, 99.41, 102.59, 105.77, 108.95, 112.13][tray_count]
values_per_panel_type = {
PanelType.Corner: [116.70,
118.55,
120.40,
123.55,
126.71,
129.86,
133.01][tray_count],
PanelType.NorthSouth: [115.63,
117.48,
119.33,
122.48,
125.64,
128.79,
131.94][tray_count],
PanelType.EastWest: [112.28,
114.13,
115.99,
119.14,
122.29,
125.45,
128.60][tray_count],
PanelType.Middle: [111.21,
113.06,
114.92,
118.07,
121.22,
124.38,
127.53][tray_count],
}
return values_per_panel_type.get(panel_type)
def link_tray_thresholds(self, panel_type):
if panel_type == PanelType.Corner or panel_type == PanelType.NorthSouth:

View File

@@ -131,14 +131,10 @@ class SingleTilt128CellConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
if panel_type == PanelType.Corner:
return [71.91, 71.91, 75.09, 78.27][tray_count]
elif panel_type == PanelType.NorthSouth:
return [65.8, 65.8, 68.98, 72.16][tray_count]
elif panel_type == PanelType.EastWest:
return [69.75, 72.05, 75.23, 78.41][tray_count]
else:
return [65.08, 67.38, 70.56, 73.74][tray_count]
return [[68.02, 68.02, 71.17, 74.32],
[65.05, 65.05, 68.20, 71.35],
[65.87, 67.73, 70.88, 74.03],
[63.26, 65.11, 68.26, 71.41]][panel_type.index()][tray_count]
def link_tray_thresholds(self, panel_type):
return [[0, 13.0],

View File

@@ -130,10 +130,10 @@ class SingleTilt96CellConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
return [[54.50, 54.50, 56.80, 59.10],
[49.47, 49.47, 51.77, 54.07],
[53.42, 55.72, 58.02, 60.32],
[48.75, 51.05, 53.35, 55.65]][panel_type.index()][tray_count]
return [[51.10, 51.10, 53.52, 55.95],
[48.13, 48.13, 50.55, 52.98],
[49.24, 51.09, 53.52, 55.94],
[48.33, 50.19, 52.61, 55.04]][panel_type.index()][tray_count]
def link_tray_thresholds(self, panel_type):
return [[0, 12.0],

View File

@@ -130,14 +130,10 @@ class SingleTiltPSeriesConstants(object):
return 1, 1
def base_weight(self, panel_type, tray_count):
if panel_type == PanelType.Corner:
return [66.91, 66.91, 70.09, 73.27][tray_count]
elif panel_type == PanelType.NorthSouth:
return [60.8, 60.8, 63.98, 67.16][tray_count]
elif panel_type == PanelType.EastWest:
return [64.75, 67.05, 70.23, 73.41][tray_count]
else:
return [60.08, 62.38, 65.56, 68.74][tray_count]
return [[65.02, 65.02, 68.17, 71.32],
[62.05, 62.05, 65.20, 68.35],
[62.87, 64.73, 67.88, 71.03],
[60.26, 62.11, 65.26, 68.41]][panel_type.index()][tray_count]
def link_tray_thresholds(self, panel_type):
return [[0, 13.0],

View File

@@ -55,7 +55,7 @@ class SingleTiltParts(object):
def module(self, module_type):
if module_type == ModuleType.Cell96:
rear_skirt_parts = rear_skirt
rear_skirt_parts = rear_skirt_1_1
spoiler_parts = spoiler
else:
rear_skirt_parts = rear_skirt_1_1

View File

@@ -0,0 +1,16 @@
import re
def convert_camel_case_to_snake_case(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def convert_dict_keys_to_snake_case(a_dict):
new_dict = {}
for old_key in a_dict.keys():
new_key = convert_camel_case_to_snake_case(old_key)
new_dict[new_key] = a_dict[old_key]
return new_dict

View File

@@ -130,8 +130,14 @@ class NodeQuadTree():
self.nodeList = toKeep
# Return a list of all possible nodes that can be near this point
def retrieve(self, nearPoint):
retNodes = list(self.nodeList)
# Optimization `only_quads_related = True`:
# Avoid replicate a large self.nodeList. Get only the other elements.
# Call this in complement of `self.nodeList`
def retrieve(self, nearPoint, only_quads_related=False):
if only_quads_related:
retNodes = []
else:
retNodes = self.nodeList[:] # = list(self.nodeList)
if self.quads[0] is not None:
index = self.getIndex(nearPoint)

View File

@@ -1,22 +1,26 @@
let AutoUpload = () => {
$("#file_upload").change((e) => {
var ten_megabyte_max_upload = 10000000;
$("#error_container_txt").empty();
if(e.currentTarget.files[0].size < ten_megabyte_max_upload){
e.currentTarget.form.submit();
}else{
$("#error_container_txt").append('<span class="error_message centered_error" id="error_message_txt">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
var ten_megabyte_max_upload = 10000000;
$("#error_container_txt").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
// $('#spinner-panel').show();
$('#spinner-panel').css('width', '100%'); // Workaround for Safari issue
e.currentTarget.form.submit();
} else {
$("#error_container_txt").append('<span class="error_message centered_error" id="error_message_txt">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
$("#dxf_upload").change((e) => {
var ten_megabyte_max_upload = 10000000;
$("#error_container_dxf").empty();
if(e.currentTarget.files[0].size < ten_megabyte_max_upload){
e.currentTarget.form.submit();
}else{
$("#error_container_dxf").append('<span class="error_message centered_error" id="error_message_dxf">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
var ten_megabyte_max_upload = 10000000;
$("#error_container_dxf").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
// $('#spinner-panel').show();
$('#spinner-panel').css('width', '100%'); // Workaround for Safari issue
e.currentTarget.form.submit();
} else {
$("#error_container_dxf").append('<span class="error_message centered_error" id="error_message_dxf">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
};

View File

@@ -8,13 +8,16 @@ import AutoUpload from './auto_upload';
$(document).ready(function () {
AutoUpload();
let subarrayDisplay = new SubarrayDisplay();
subarrayDisplay.init($('#current_anchors'), $('#needed_anchors'), $('#subarray_weight'), panel_data);
let arrayVisualization = new ArrayVisualization(panel_data, is_dual_tilt, subarrayDisplay, buildings_coordinates);
arrayVisualization.init();
new ZoomControl(arrayVisualization).init($('#zoom_control'));
new OverlayControl(arrayVisualization).init($('#overlay_control'), $('#legend_container'));
new SeismicControl(arrayVisualization, subarrayDisplay).init($('.seismic_anchor_control'), $("#seismic_save"));
window.arrayVisualization = arrayVisualization;
if (is_csv_available) {
let subarrayDisplay = new SubarrayDisplay();
subarrayDisplay.init($('#current_anchors'), $('#needed_anchors'), $('#subarray_weight'), panel_data);
let arrayVisualization = new ArrayVisualization(panel_data, is_dual_tilt, subarrayDisplay, buildings_coordinates);
arrayVisualization.init();
new ZoomControl(arrayVisualization).init($('#zoom_control'));
new OverlayControl(arrayVisualization).init($('#overlay_control'), $('#legend_container'));
new SeismicControl(arrayVisualization, subarrayDisplay).init($('.seismic_anchor_control'), $("#seismic_save"));
window.arrayVisualization = arrayVisualization;
}
});

16
helix/json_builder.py Normal file
View File

@@ -0,0 +1,16 @@
try:
import ujson as json
except ImportError:
import json
class JsonBuilder:
def build_bom_output(self, rows):
data = []
headers = ['Part #', 'Description', 'Total']
for row in rows:
d = {}
for i, value in enumerate(row):
d[headers[i]] = value
data.append(d)
return json.dumps(data)

View File

@@ -6,8 +6,10 @@ from flask import Flask, request, make_response, session, render_template, \
redirect, url_for
from flask import got_request_exception
from flask.ext import assets
from flask_oauthlib.client import OAuth
from webassets.filter import get_filter
from helix.sales_force import tasks as sf_tasks
from helix.Services.doc_gen_service import DocGenService
from helix.Services.dxf_helper import DXFHelper
from helix.Services.dxf_service import DXFService
@@ -34,6 +36,20 @@ app.register_blueprint(api, url_prefix='/api')
app.secret_key = os.getenv('SECRET_KEY', 'verysecretkey')
app.config['PROFILE'] = True
# Sales Force integrations
oauth = OAuth()
SF_BASE_URL = os.getenv('SFDC_BASE_URL', 'https://test.salesforce.com')
sales_force = oauth.remote_app('sales_force',
consumer_key=os.getenv('SFDC_ACCESS_KEY_ID'),
consumer_secret=os.getenv('SFDC_SECRET_ACCESS_KEY'),
base_url=SF_BASE_URL,
request_token_url=None, # OAuth 2
access_token_method='POST', # Sales Force requirement
access_token_url=SF_BASE_URL + '/services/oauth2/token',
authorize_url=SF_BASE_URL + '/services/oauth2/authorize',
)
assets_env = assets.Environment(app)
assets_env.init_app(app)
assets_env.load_path = [
@@ -67,6 +83,10 @@ def init_rollbar():
got_request_exception.connect(rollbar.contrib.flask.report_exception, app)
def is_sfdc_session():
return 'SFID' in session
@app.route("/")
def index():
return redirect(url_for('site_characterization'))
@@ -109,6 +129,9 @@ def test_dxf():
# wizard steps
@app.route("/site_characterization/", methods=['GET', 'POST'])
def site_characterization():
if is_sfdc_session():
return redirect('/summary/')
db_session = sql_constant.sql_session_maker()
session_manager = SessionManager(session, redis_constant.redis_store, db_session)
site_info_form = InputForm()
@@ -136,6 +159,7 @@ def summary():
context['current_step'] = 2
if context['site_data_available']:
context['project_name'] = session_manager.site.project_name
user_values = session_manager.user_values()
calculator = Calculator(user_values, calculate_panel_data=False)
context['wind_zones'] = user_values.system_type().system_constants().wind_zones
@@ -153,6 +177,9 @@ def summary():
else:
context['no_proceed'] = True
if is_sfdc_session():
context['hide_back'] = True
db_session.close()
return render_template('site_summary.html.jinja', context=context)
@@ -199,6 +226,7 @@ def array_summary():
else:
try:
module_constants = user_values.module_system_constants()
# FIXME: parsing a file with many entities is very slow
dxf_data = DXFService().parse(file_contents,
module_constants,
user_values.system_type(),
@@ -428,6 +456,70 @@ def helix_documentation():
return render_template('helix_documentation.jinja', context=context)
# Sales Force Integration
@app.route('/sales_force_login')
def sales_force_login():
# To test it locally: https://localhost:8443/sales_force_login?SFID=a3cL00000004QsQIAU
sfid = request.args.get('SFID')
if sfid:
session.clear()
session['SFID'] = sfid
return sales_force.authorize(callback=url_for('sales_force_authorized', _external=True, SFID=sfid), SFID=sfid)
else:
return redirect('/')
@app.route('/sales_force_authorized')
def sales_force_authorized():
next_url = url_for('summary')
resp = sales_force.authorized_response()
if resp is None:
print('Unable to authenticate to SFDC.')
return redirect(next_url)
print('New Sales Force - OAuth2 login')
db_session = sql_constant.sql_session_maker()
session_manager = SessionManager(session, redis_constant.redis_store, db_session)
session['sales_force_token'] = resp['access_token']
data = sf_tasks.get_site_characterization_from_sales_force(session, resp['instance_url'])
if data:
session_manager.save_form_submission(data)
return redirect(next_url)
else:
return sales_force_logout()
# FIXME
from flask import jsonify
@app.route("/export-sfdc")
def export_sfdc():
if not is_sfdc_session():
return redirect('/')
db_session = sql_constant.sql_session_maker()
session_manager = SessionManager(session, redis_constant.redis_store, db_session)
session_id = session_manager.session['id']
data = sf_tasks.export_to_sfdc(session_id)
db_session.close()
return jsonify(data)
# return redirect('/download')
@sales_force.tokengetter
def get_sales_force_token(token=None):
return session.get('sales_force_token')
@app.route('/sales_force_logout')
def sales_force_logout():
session.pop('SFID', None)
session.pop('sales_force_token', None)
session.clear()
return redirect('/')
# End of Sales Force Integration
@app.template_filter('format_number')
def format_number(number):
return "{:,g}".format(number)
@@ -457,8 +549,13 @@ def enum():
def main():
host = '0.0.0.0'
port = int(os.getenv('PORT', 5000))
app.run(host=host, port=port, debug=bool(os.getenv("FLASK_DEBUG", False)))
debug = bool(os.getenv("FLASK_DEBUG", False))
if os.getenv('FLASK_DEBUG_SSL', None):
port = int(os.getenv('PORT', 8443))
app.run(host=host, port=port, debug=debug, ssl_context='adhoc')
else:
port = int(os.getenv('PORT', 5000))
app.run(host=host, port=port, debug=debug)
@app.route("/fail-test")

View File

@@ -25,6 +25,7 @@ class GraphNodeStore(list):
dy = node.coordinate.y - coordinate.y
return dx * dx + dy * dy
# FIXME: This is slow if called thousands of times. Do not add any overhead in this method.
def find_coordinate(self, coordinate):
# create and populate the quadtree on first request
if self.quadTree is None:
@@ -33,9 +34,15 @@ class GraphNodeStore(list):
self.quadTree.insert(node)
del self[:]
possibilities = self.quadTree.retrieve(coordinate)
variance_square = self.variance ** 2
# Optimization: avoid creating a copy of the big list `self.quadTree.nodeList`
# Old: possibilities = self.quadTree.retrieve(coordinate)
possibilities = self.quadTree.nodeList
for node in possibilities:
if self.distance_squared(node, coordinate) <= self.variance ** 2:
if self.distance_squared(node, coordinate) <= variance_square:
return node
else:
return None
more_possibilities = self.quadTree.retrieve(coordinate, only_quads_related=True)
for node in more_possibilities:
if self.distance_squared(node, coordinate) <= variance_square:
return node
return None

View File

@@ -1,6 +1,3 @@
import math
from helix.models.coordinate import Coordinate
import sys
class ProjectPresenter(object):

View File

118
helix/sales_force/tasks.py Normal file
View File

@@ -0,0 +1,118 @@
import io
import uuid
import requests
from helix.calculators.calculator import Calculator
from helix.constants import redis_constant, sql_constant
from helix.constants.system_type import SystemType
from helix.csv_builder import CsvBuilder
from helix.doc_gen_params_builder import DocGenParamsBuilder
from helix.helpers.camel_case import convert_dict_keys_to_snake_case
from helix.json_builder import JsonBuilder
from helix.presenters.image_presenter import ImagePresenter
from helix.Services.doc_gen_service import DocGenService
from helix.Services.s3_helper import s3_upload
from helix.session_manager import SessionManager
def get_site_characterization_from_sales_force(session, base_url):
'''
@base_url: Avoid URL_NOT_RESET errors
'''
access_token = session['sales_force_token']
sfid = session['SFID']
helix_id = session['id']
url = base_url + '/services/apexrest/v1/HelixRoofDetails'
headers = {'Authorization': 'Bearer {}'.format(access_token)}
result = requests.get(url, headers=headers, params={'SFID': sfid, 'helix_session_id': helix_id})
if result.status_code == 200:
data = result.json()
if data:
data = convert_sales_force_data_format_to_helix(data)
return data
else:
print('Error while getting data from Sales Force: {}'.format(result.status_code))
print(result.content)
def convert_sales_force_data_format_to_helix(data):
data = convert_dict_keys_to_snake_case(data)
if data['system_type'] == 'Single-Tilt':
data['system_type'] = SystemType.singleTilt.value
elif data['system_type'] == 'Dual-Tilt':
data['system_type'] = SystemType.dualTilt.value
data['ballast_block_weight'] = data['ballast_weight']
data['max_system_pressure'] = data['max_psf'] = data['system_pressure']
return data
# data['spectral_response_acceleration']
def export_to_sfdc(session_id):
step = 'Exporting to SFDC'
try:
# 1. Load User Values
step = 'Loading User Values'
session = {'id': session_id}
db_session = sql_constant.sql_session_maker()
session_manager = SessionManager(session, redis_constant.redis_store, db_session)
user_values = session_manager.user_values()
calculator = Calculator(user_values)
# 2. Generate BOM CSV file
step = 'Generating BOM'
bom = calculator.compute_bom()
csv_file = CsvBuilder().build_bom_output(bom)
# 2.1 Generate BOM CSV file
step = 'Generating BOM as JSON'
json_str = JsonBuilder().build_bom_output(bom)
# 3. Generate DOCUMENTATION PDF file
step = 'Generating Documentation'
image_presenter = ImagePresenter(user_values.system_type(), user_values.module_type())
doc_gen_service = DocGenService(requests, DocGenParamsBuilder(user_values, user_values.system_type(), calculator, image_presenter))
document = doc_gen_service.generate() # Call external service
# 4. Get Uploaded DXF file in the Helix system
step = 'Loading uploaded DXF'
dxf_contents = session_manager.site.dxf_file or session_manager.site.cad_file
# dxf_filename = session_manager.site.dxf_file_name
# 5. Save CSV/PDF/DXF files into AWS-S3
step = 'Uploading to S3'
filename = uuid.uuid4().hex
bom_csv_url = s3_upload(io.StringIO(csv_file), filename=filename + '.csv')
bom_json_url = s3_upload(io.StringIO(json_str), filename=filename + '.json')
doc_url = s3_upload(io.BytesIO(document), filename=filename + '.pdf')
if dxf_contents: # Optional
dxf_url = s3_upload(io.StringIO(dxf_contents), filename=filename + '.dxf')
else:
dxf_url = None
# 6. Notify SFDC system with an API request
step = 'Notifying SFDC'
SFDC_API_URL = 'https://localhost:8443/' # FIXME
data = {
'dxf_url': dxf_url,
'bom_csv_url': bom_csv_url,
'bom_json_url': bom_json_url,
'documentation_url': doc_url,
}
print(data)
# result = requests.post(SFDC_API_URL, data=data, timeout=30)
# 7. Internal logs
# if result.status_code != 200: # FIXME
# print('')
# else:
# print('')
db_session.close()
return data
# return result.status_code
except Exception as e:
msg = 'Error while {} for session {}'.format(step, session_id)
print(msg)
raise e

85
helix/static/css/animation.css Executable file
View File

@@ -0,0 +1,85 @@
/*
Animation example, for spinners
*/
.animate-spin {
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
display: inline-block;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}

View File

@@ -1,11 +1,11 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?10976371');
src: url('../font/fontello.eot?10976371#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?10976371') format('woff2'),
url('../font/fontello.woff?10976371') format('woff'),
url('../font/fontello.ttf?10976371') format('truetype'),
url('../font/fontello.svg?10976371#fontello') format('svg');
src: url('../font/fontello.eot?24283821');
src: url('../font/fontello.eot?24283821#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?24283821') format('woff2'),
url('../font/fontello.woff?24283821') format('woff'),
url('../font/fontello.ttf?24283821') format('truetype'),
url('../font/fontello.svg?24283821#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -15,7 +15,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?10976371#fontello') format('svg');
src: url('../font/fontello.svg?24283821#fontello') format('svg');
}
}
*/
@@ -69,4 +69,5 @@
.icon-info:before { content: '\e80b'; } /* '' */
.icon-close:before { content: '\e80c'; } /* '' */
.icon-sunpower-logo:before { content: '\e80d'; } /* '' */
.icon-upload-cloud:before { content: '\e80e'; } /* '' */
.icon-upload-cloud:before { content: '\e80e'; } /* '' */
.icon-spin6:before { content: '\e839'; } /* '' */

View File

@@ -1023,3 +1023,21 @@ table .right_border_cell {
margin-bottom: 25px;
}
.spinner-panel {
position: fixed;
margin: 0 auto;
top: 0;
left: 0;
width: 0; /* It will be updated to 100% in JS. Workaround for Safari issue with display:none; */
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 1;
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 24px;
}

Binary file not shown.

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2016 by original authors @ fontello.com</metadata>
<metadata>Copyright (C) 2017 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
@@ -35,6 +35,8 @@
<glyph glyph-name="sunpower-logo" unicode="&#xe80d;" d="M466 633c-35 61-77 96-152 96-78 0-146-56-146-137 0-76 78-111 138-138l59-26c114-50 211-107 211-248 0-154-124-272-277-272-141 0-247 91-275 228l96 27c13-90 82-164 177-164 94 0 181 73 181 172 0 103-81 138-163 175l-53 24c-103 47-192 99-192 225 0 137 116 225 247 225 98 0 181-50 228-137l-79-50 0 0z m537-353c0-66-3-140 38-197 39-53 114-84 178-84 63 0 132 29 173 79 47 56 43 133 43 202l0 525 99 0 0-552c0-96-7-172-78-244-60-66-148-101-237-101-83 0-168 32-228 90-77 74-86 154-86 255l0 552 98 0 0-525 0 0z m874 562l688-720 0 683 98 0 0-922-688 720 0-680-98 0 0 919z m1255-128l36 0c114 0 230-11 230-155 0-126-92-159-201-159l-65 0 0 314 0 0z m0-405l71 0c74 0 150 9 209 58 55 46 84 119 84 190 0 78-33 155-99 201-64 44-143 47-219 47l-145 0 0-882 99 0 0 386 0 0z m1949-165l285 706 285-706 232 661 106 0-338-927-285 709-285-709-338 927 106 0 232-661z m1128 661l457 0 0-91-359 0 0-262 348 0 0-91-348 0 0-347 359 0 0-91-457 0 0 882z m886-91l29 0c118 0 225-14 225-159 0-137-113-158-224-158l-30 0 0 317 0 0z m0-404l24 0 267-387 120 0-280 395c136 12 221 108 221 244 0 198-156 243-323 243l-127 0 0-882 98 0 0 387 0 0z m-2487 53c0-257-204-453-463-453-253 0-456 205-456 451 0 249 201 454 456 454 259 0 463-195 463-452m-816-2c0-188 150-354 348-354 204 0 363 154 363 354 0 203-157 357-363 357-200 0-348-166-348-357m3755 309c0 24 6 46 18 67 12 21 29 38 50 50 21 12 43 18 67 18 24 0 47-6 68-18 21-12 37-29 49-49 12-21 18-44 18-68 0-24-6-46-17-66-12-21-28-38-49-50-21-13-44-19-69-19-24 0-47 6-68 19-21 12-37 29-49 49-12 21-18 43-18 67l0 0z m17 0c0-21 5-41 16-59 11-18 25-33 43-43 18-11 38-16 59-16 22 0 41 5 59 16 19 10 33 25 44 43 10 18 16 38 16 59 0 21-6 40-16 58-10 18-24 33-43 44-18 11-38 16-60 16-21 0-40-5-59-16-18-10-32-25-43-43-11-19-16-38-16-59l0 0z m166 33c0-9-2-17-6-25-5-7-12-12-20-17l42-70-22 0-37 65-30 0 0-65-18 0 0 158 37 0c18 0 31-4 40-11 10-8 14-19 14-35l0 0z m-73-33l20 0c11 0 19 3 25 8 7 6 10 14 10 25 0 20-12 30-36 30l-19 0 0-63 0 0z" horiz-adv-x="7828" />
<glyph glyph-name="upload-cloud" unicode="&#xe80e;" d="M760 494q100 0 170-68t70-166-70-166-170-68l-190 0 0 190 106 0-176 230-174-230 104 0 0-190-248 0q-74 0-128 52t-54 124q0 74 53 126t129 52q14 0 20-2-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z" horiz-adv-x="1000" />
<glyph glyph-name="spin6" unicode="&#xe839;" d="M855 9c-189-190-520-172-705 13-190 190-200 494-28 695 11 13 21 26 35 34 36 23 85 18 117-13 30-31 35-76 16-112-5-9-9-15-16-22-140-151-145-379-8-516 153-153 407-121 542 34 106 122 142 297 77 451-83 198-305 291-510 222l0 1c236 82 492-24 588-252 71-167 37-355-72-493-11-15-23-29-36-42z" horiz-adv-x="1000" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -73,15 +73,18 @@
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
$(document).ready(function () {
(0, _auto_upload2.default)();
var subarrayDisplay = new _subarray_display2.default();
subarrayDisplay.init($('#current_anchors'), $('#needed_anchors'), $('#subarray_weight'), panel_data);
var arrayVisualization = new _array_visualization2.default(panel_data, is_dual_tilt, subarrayDisplay, buildings_coordinates);
arrayVisualization.init();
new _zoom_control2.default(arrayVisualization).init($('#zoom_control'));
new _overlay_control2.default(arrayVisualization).init($('#overlay_control'), $('#legend_container'));
new _seismic_control2.default(arrayVisualization, subarrayDisplay).init($('.seismic_anchor_control'), $("#seismic_save"));
window.arrayVisualization = arrayVisualization;
(0, _auto_upload2.default)();
if (is_csv_available) {
var subarrayDisplay = new _subarray_display2.default();
subarrayDisplay.init($('#current_anchors'), $('#needed_anchors'), $('#subarray_weight'), panel_data);
var arrayVisualization = new _array_visualization2.default(panel_data, is_dual_tilt, subarrayDisplay, buildings_coordinates);
arrayVisualization.init();
new _zoom_control2.default(arrayVisualization).init($('#zoom_control'));
new _overlay_control2.default(arrayVisualization).init($('#overlay_control'), $('#legend_container'));
new _seismic_control2.default(arrayVisualization, subarrayDisplay).init($('.seismic_anchor_control'), $("#seismic_save"));
window.arrayVisualization = arrayVisualization;
}
});
/***/ }),
@@ -24730,28 +24733,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
value: true
});
var AutoUpload = function AutoUpload() {
$("#file_upload").change(function (e) {
var ten_megabyte_max_upload = 10000000;
$("#error_container_txt").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
e.currentTarget.form.submit();
} else {
$("#error_container_txt").append('<span class="error_message centered_error" id="error_message_txt">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
$("#file_upload").change(function (e) {
var ten_megabyte_max_upload = 10000000;
$("#error_container_txt").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
// $('#spinner-panel').show();
$('#spinner-panel').css('width', '100%'); // Workaround for Safari issue
e.currentTarget.form.submit();
} else {
$("#error_container_txt").append('<span class="error_message centered_error" id="error_message_txt">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
$("#dxf_upload").change(function (e) {
var ten_megabyte_max_upload = 10000000;
$("#error_container_dxf").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
e.currentTarget.form.submit();
} else {
$("#error_container_dxf").append('<span class="error_message centered_error" id="error_message_dxf">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
$("#dxf_upload").change(function (e) {
var ten_megabyte_max_upload = 10000000;
$("#error_container_dxf").empty();
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
// $('#spinner-panel').show();
$('#spinner-panel').css('width', '100%'); // Workaround for Safari issue
e.currentTarget.form.submit();
} else {
$("#error_container_dxf").append('<span class="error_message centered_error" id="error_message_dxf">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
}
});
};
exports.default = AutoUpload;

View File

@@ -1,6 +1,15 @@
{% extends "layout.html.jinja" %}
{% set title = "Helix Calculator" %}
{% block contents %}
<script>
var is_csv_available = {{ context['csv_available']|tojson|safe }};
</script>
<div id="spinner-panel" class="spinner-panel">
<p>Uploading files. Please wait, this may take a while.</p>
<i class="icon-spin6 animate-spin"></i>
</div>
{% if not context['csv_available'] %}
<form action="" method="post" enctype="multipart/form-data">
{{ form.csrf_token }}

View File

@@ -1,6 +1,10 @@
{% extends "layout.html.jinja" %}
{% set title = "Helix Calculator" %}
{% block contents %}
{% for warning in context['warning_messages'] %}
<div class="summary_warning">{{ warning.value }}</div>
{% endfor %}
<div class="form_section">
<h3>Download</h3>
</div>
@@ -20,6 +24,14 @@
<button>Download BOM (.txt)</button>
</a>
</div>
{% if 'SFID' in session %}
<div class="download">
<a href="/export-sfdc">
<button>Export documents to Salesforce</button>
</a>
</div>
{% endif %}
{% else %}
Please complete previous steps first!
{% endif %}

View File

@@ -8,6 +8,7 @@
<link href="//cdn.rawgit.com/noelboss/featherlight/1.7.6/release/featherlight.min.css" type="text/css" rel="stylesheet" />
<link rel="shortcut icon" href="https://us.sunpower.com/sites/sunpower/files/favicon.ico">
<link rel="stylesheet" type="text/css" href={{ url_for('static', filename='css/fontello.css') }}>
<link rel="stylesheet" type="text/css" href={{ url_for('static', filename='css/animation.css') }}>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>

View File

@@ -1,5 +1,9 @@
<div class="navigation_buttons">
<a class="back button" href="{{ context['steps'][context['current_step'] - 2][2] }}">Back</a>
{% if context['hide_back'] %}
{% else %}
<a class="back button" href="{{ context['steps'][context['current_step'] - 2][2] }}">Back</a>
{% endif %}
{% if context['steps']|length > context['current_step'] and not context.get('no_proceed') %}
{% if not form or context['override_form'] %}
<a class="button" href="{{ context['steps'][context['current_step']][2] }}" value="Next">Next</a>

View File

@@ -3,10 +3,11 @@
{% block contents %}
{% if context['site_data_available'] %}
<div class="form_section">
<h3>Summary</h3>
<h3>Summary <small>({{ context['project_name'] }})</small></h3>
{% for warning in context['warning_messages'] %}
<div class="summary_warning">{{ warning.value }}</div>
{% endfor %}
<table class="summary_table" id="summary_table">
<tr>
<td colspan="2" class="table_meta_headers">OUTPUTS</td>
@@ -80,8 +81,13 @@
</tr>
</table>
</div>
{% endif %}
{% include "navigation_buttons.html.jinja" %}
{% if 'SFID' in session %}
<small><i>Sales Force project</i> (<a href="/sales_force_logout">Logout</a>)</small>
{% endif %}
{% endblock %}

View File

@@ -8,7 +8,7 @@ from helix.constants.panel_type import PanelType
from helix.constants.system_type import SystemType
from helix.models.coordinate import Coordinate
from helix.models.panel import PanelData, Panel
from helix.models.sql.inverter_brands import InverterBrand
class UserValues(object):
def __init__(self, store, site):

Binary file not shown.

View File

@@ -30,7 +30,7 @@ class CsvInputValidator(object):
file_validation_chain = [
CsvInputValidator.validate_file_for_panel_types,
CsvInputValidator.validate_for_spacing,
# CsvInputValidator.validate_for_spacing, # disabling this feature for the moment
]
result = self.run_validation_chain(headers, rows, file_validation_chain)
if result:

View File

@@ -1,5 +1,3 @@
from helix.constants.file_validation_error import FileValidationMessage, FileValidationError
class DxfInputValidator(object):
def __init__(self, _):

View File

@@ -7,3 +7,7 @@ mockredispy==2.9.0.11
Flask-Testing==0.4.2
splinter[flask]
pillow==3.3.1
eralchemy==1.1.0
locust==0.8
pylint==1.7.4
pyopenssl==17.5.0

View File

@@ -1,3 +1,5 @@
gunicorn==19.7.1
meinheld==0.6.1
Flask==0.10.1
itsdangerous==0.24
Jinja2==2.8
@@ -18,3 +20,6 @@ invoke==0.13.0
dxfgrabber==0.8.1
rollbar==0.13.11
blinker==1.4
Flask-OAuthlib==0.9.4
boto3==1.4.8
ujson==1.3.5

View File

@@ -1,4 +1,5 @@
from invoke import run, task
import os
@task
@@ -34,9 +35,10 @@ def test_js(ctx):
def update_version(ctx):
run('echo "import os\n\n\ndef version():\n if os.getenv(\'VERSION\'):\n return os.getenv(\'VERSION\')\n return \'$(git describe --tags)\'" > helix/constants/version.py')
@task
def serve(ctx):
run('PYTHONPATH=. SP_DOCGEN_API_KEY=DC97-20AF-567E python helix/main.py')
run('SP_DOCGEN_API_KEY=DC97-20AF-567E gunicorn -c gunicorn_config.py --pythonpath helix main:app')
@task
@@ -52,3 +54,23 @@ def db_migrate(ctx):
@task
def serve_debug(ctx):
run('PYTHONPATH=. FLASK_DEBUG=1 SP_DOCGEN_API_KEY=DC97-20AF-567E python helix/main.py')
@task
def db_diagram(ctx):
db_uri = os.getenv('DATABASE_URL', 'postgres://pivotal:@localhost:5432/pivotal')
run('eralchemy -i "{}" -o documentation/db_schema.png -x temp audit'.format(db_uri))
@task
def classes_diagrams(ctx):
run('pyreverse -k -o calculators-diagram.png helix/calculators')
run('pyreverse -k -o constants-diagram.png helix/constants')
run('pyreverse -k -o forms-diagram.png helix/forms')
run('pyreverse -k -o models-diagram.png helix/models')
run('pyreverse -k -o validators-diagram.png helix/validators')
run('rm -rf packages*.png ; mv classes.*-diagram.png documentation/')
@task
def serve_ssl(ctx):
run('FLASK_DEBUG_SSL=true PORT=8443 PYTHONPATH=. FLASK_DEBUG=1 SP_DOCGEN_API_KEY=DC97-20AF-567E python helix/main.py')

View File

@@ -28,7 +28,7 @@ class BallastAndTrayCountTest(unittest.TestCase):
eq_(result.ballast_count, 0)
eq_(result.link_tray_count, 0)
eq_(result.cross_tray_count, 0)
eq_(result.system_weight, 69.75)
eq_(result.system_weight, 65.87)
eq_(result.needs_anchor, False)
def test_ballast_no_trays(self):
@@ -38,27 +38,27 @@ class BallastAndTrayCountTest(unittest.TestCase):
eq_(result.ballast_count, 4)
eq_(result.link_tray_count, 0)
eq_(result.cross_tray_count, 0)
eq_(result.system_weight, 69.75)
eq_(result.system_weight, 65.87)
eq_(result.needs_anchor, False)
def test_ballast_and_link_trays(self):
self.force_to_resist = 230
result = self.subject.ballast_and_tray_count(self.force_to_resist, self.panel_type, self.ballast_block_weight, self.anchor_count)
eq_(result.ballast_count, 8)
eq_(result.ballast_count, 9)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 0)
eq_(result.system_weight, 72.05)
eq_(result.system_weight, 67.73)
eq_(result.needs_anchor, False)
def test_ballast_and_oscillate_between_0_and_1_link_trays(self):
self.force_to_resist = 210
result = self.subject.ballast_and_tray_count(self.force_to_resist, self.panel_type, self.ballast_block_weight, self.anchor_count)
eq_(result.ballast_count, 7)
eq_(result.ballast_count, 8)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 0)
eq_(result.system_weight, 72.05)
eq_(result.system_weight, 67.73)
eq_(result.needs_anchor, False)
def test_ballast_and_link_tray_and_cross_tray(self):
@@ -68,17 +68,17 @@ class BallastAndTrayCountTest(unittest.TestCase):
eq_(result.ballast_count, 16)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 1)
eq_(result.system_weight, 75.23)
eq_(result.system_weight, 70.88)
eq_(result.needs_anchor, False)
def test_ballast_and_link_tray_and_oscillate_between_0_and_1_cross_trays(self):
self.force_to_resist = 330
result = self.subject.ballast_and_tray_count(self.force_to_resist, self.panel_type, self.ballast_block_weight, self.anchor_count)
eq_(result.ballast_count, 13)
eq_(result.ballast_count, 14)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 0)
eq_(result.system_weight, 72.05)
eq_(result.system_weight, 67.73)
eq_(result.needs_anchor, False)
def test_ballast_and_link_tray_and_2_cross_trays(self):
@@ -88,17 +88,17 @@ class BallastAndTrayCountTest(unittest.TestCase):
eq_(result.ballast_count, 25)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 2)
eq_(result.system_weight, 78.41)
eq_(result.system_weight, 74.03)
eq_(result.needs_anchor, False)
def test_ballast_and_link_tray_and_at_first_2_then_1_cross_trays(self):
self.force_to_resist = 515
result = self.subject.ballast_and_tray_count(self.force_to_resist, self.panel_type, self.ballast_block_weight, self.anchor_count)
eq_(result.ballast_count, 22)
eq_(result.ballast_count, 23)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 1)
eq_(result.system_weight, 75.23)
eq_(result.cross_tray_count, 2)
eq_(result.system_weight, 74.03)
eq_(result.needs_anchor, False)
def test_ballast_and_all_trays_and_anchor(self):
@@ -108,15 +108,15 @@ class BallastAndTrayCountTest(unittest.TestCase):
eq_(result.ballast_count, 35)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 2)
eq_(result.system_weight, 78.41)
eq_(result.system_weight, 74.03)
eq_(result.needs_anchor, True)
def test_ballast_and_all_trays_and_at_first_anchor_then_no_anchor(self):
self.force_to_resist = 675
result = self.subject.ballast_and_tray_count(self.force_to_resist, self.panel_type, self.ballast_block_weight, self.anchor_count)
eq_(result.ballast_count, 30)
eq_(result.ballast_count, 31)
eq_(result.link_tray_count, 1)
eq_(result.cross_tray_count, 2)
eq_(result.system_weight, 78.41)
eq_(result.needs_anchor, False)
eq_(result.system_weight, 74.03)
eq_(result.needs_anchor, True)

View File

@@ -48,23 +48,23 @@ class BallastCalculatorWhenDualTiltAnd128CellTest(unittest.TestCase):
expected_value = {
PanelType.Corner: {
'anchors': [3, 2, 0, 0, 0],
'ballast blocks': [15, 14, 25, 12, 0],
'pressure': ['8.14', '7.74', '12.11', '6.88', '2.12']
'ballast blocks': [14, 13, 25, 12, 0],
'pressure': ['8.00', '7.61', '12.36', '7.16', '2.40']
},
PanelType.NorthSouth: {
'anchors': [3, 2, 0, 0, 0],
'ballast blocks': [7, 8, 22, 10, 0],
'pressure': ['4.90', '5.29', '10.83', '6.05', '2.10']
'ballast blocks': [6, 7, 21, 9, 0],
'pressure': ['4.79', '5.18', '10.72', '5.93', '2.38']
},
PanelType.EastWest: {
'anchors': [3, 2, 0, 0, 0],
'ballast blocks': [8, 8, 22, 10, 0],
'pressure': ['5.25', '5.25', '10.78', '5.97', '2.02']
'ballast blocks': [7, 7, 21, 10, 0],
'pressure': ['5.18', '5.18', '10.71', '6.29', '2.31']
},
PanelType.Middle: {
'anchors': [2, 0, 0, 0, 0],
'ballast blocks': [15, 32, 18, 8, 0],
'pressure': ['8.02', '14.73', '9.13', '5.17', '1.99']
'ballast blocks': [14, 31, 17, 7, 0],
'pressure': ['7.89', '14.66', '9.07', '5.10', '2.29']
},
}
@@ -89,19 +89,19 @@ class BallastCalculatorWhenDualTiltAnd128CellTest(unittest.TestCase):
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=25, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=12.11),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=22, link_tray=2, cross_tray=1, wind_anchors=0,
pressure=10.83),
pressure=12.360800772863891),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=21, link_tray=2, cross_tray=1, wind_anchors=0,
pressure=10.715259768140834),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=2.02),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=15, link_tray=2, cross_tray=1, wind_anchors=3,
pressure=8.14),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0,
pressure=14.73),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=10, link_tray=1, cross_tray=0, wind_anchors=0,
pressure=6.05),
pressure=2.3107513954486905),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=2, cross_tray=1, wind_anchors=3,
pressure=8.001084156290254),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=31, link_tray=2, cross_tray=3, wind_anchors=0,
pressure=14.659635036496352),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=9, link_tray=1, cross_tray=0, wind_anchors=0,
pressure=5.928870759982826),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=44, link_tray=2, cross_tray=4, wind_anchors=0,
pressure=19.65)
pressure=19.907666380420782)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, raw_data),
@@ -120,20 +120,20 @@ class BallastCalculatorWhenDualTiltAnd128CellTest(unittest.TestCase):
self.values.ballast_block_weight.return_value = 20
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=30, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=14.06, fuzzy_wind_zone=True),
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=29, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=13.923703, fuzzy_wind_zone=True),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=25, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=12.06, fuzzy_wind_zone=True),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=1, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=2.41, fuzzy_wind_zone=True),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=10, link_tray=2, cross_tray=1, wind_anchors=4,
pressure=6.18, fuzzy_wind_zone=True, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=37, link_tray=2, cross_tray=4, wind_anchors=0,
pressure=16.75, fuzzy_wind_zone=True),
pressure=12.339897, fuzzy_wind_zone=True),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=2.310751, fuzzy_wind_zone=True),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=9, link_tray=2, cross_tray=1, wind_anchors=4,
pressure=6.047456, fuzzy_wind_zone=True, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=36, link_tray=2, cross_tray=4, wind_anchors=0,
pressure=16.674802, fuzzy_wind_zone=True),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=12, link_tray=2, cross_tray=0, wind_anchors=0,
pressure=6.86, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=7, link_tray=2, cross_tray=0, wind_anchors=3,
pressure=4.93, fuzzy_wind_zone=True)
pressure=7.137190, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=6, link_tray=2, cross_tray=0, wind_anchors=3,
pressure=4.813740, fuzzy_wind_zone=True)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, raw_data),
@@ -155,19 +155,19 @@ class BallastCalculatorWhenDualTiltAnd128CellTest(unittest.TestCase):
expected = [
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1,
ballast=11, link_tray=2, cross_tray=0, pressure=6.49),
ballast=10, link_tray=2, cross_tray=0, pressure=6.376642335766423),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1,
ballast=6, link_tray=1, cross_tray=0, pressure=4.49),
ballast=6, link_tray=1, cross_tray=0, pressure=4.756693860025763),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1,
ballast=0, link_tray=0, cross_tray=0, pressure=2.02),
ballast=0, link_tray=0, cross_tray=0, pressure=2.3107513954486905),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0,
ballast=15, link_tray=2, cross_tray=1, pressure=8.14),
ballast=14, link_tray=2, cross_tray=1, pressure=8.001084156290254),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1,
ballast=17, link_tray=2, cross_tray=2, pressure=8.81),
ballast=16, link_tray=2, cross_tray=1, pressure=8.675476599398884),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1,
ballast=0, link_tray=0, cross_tray=0, pressure=2.10),
ballast=0, link_tray=0, cross_tray=0, pressure=2.3761979390296264),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=2, seismic_anchors=0,
ballast=14, link_tray=2, cross_tray=1, pressure=7.74),
ballast=13, link_tray=2, cross_tray=1, pressure=7.610358522971233),
]
result = self.subject.update_ballast(self.c_p_matrix, self.q_z, panels)
@@ -211,35 +211,37 @@ class BallastCalculatorWhenDualTiltAnd128CellTest(unittest.TestCase):
]
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=25, link_tray=2, cross_tray=2, wind_anchors=0, pressure=12.11),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=22, link_tray=2, cross_tray=1, wind_anchors=0, pressure=10.83),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.02),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=15, link_tray=2, cross_tray=1, wind_anchors=3, pressure=8.14),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0, pressure=14.73),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=10, link_tray=1, cross_tray=0, wind_anchors=0, pressure=6.05),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=14, link_tray=2, cross_tray=1, wind_anchors=2, pressure=7.74)
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=25, link_tray=2, cross_tray=2, wind_anchors=0, pressure=12.360800772863891),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=21, link_tray=2, cross_tray=1, wind_anchors=0, pressure=10.715259768140834),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.3107513954486905),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=2, cross_tray=1, wind_anchors=3, pressure=8.001084156290254),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=31, link_tray=2, cross_tray=3, wind_anchors=0, pressure=14.659635036496352),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=9, link_tray=1, cross_tray=0, wind_anchors=0, pressure=5.928870759982826),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=13, link_tray=2, cross_tray=1, wind_anchors=2, pressure=7.610358522971233)
]
print("===\r\n")
print(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panel_data))
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panel_data), expected_value, decimal=2)
def test_ballast_count_when_base_weight_greater_than_uplift(self):
expected = Panel(wind_zone=4, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=1.99)
expected = Panel(wind_zone=4, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.289848)
assert self.subject.ballast_tray_and_anchor_count(4, PanelType.Middle, 14, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=21, link_tray=2, cross_tray=2, wind_anchors=2, pressure=7.91)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=19, link_tray=2, cross_tray=2, wind_anchors=2, pressure=7.682057)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 14, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=15, link_tray=2, cross_tray=2, wind_anchors=2, pressure=8.02)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=14, link_tray=2, cross_tray=1, wind_anchors=2, pressure=7.894025)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 20, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=24, link_tray=2, cross_tray=3, wind_anchors=2, pressure=7.85)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=22, link_tray=2, cross_tray=3, wind_anchors=2, pressure=7.704719)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 12, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_when_max_system_pressure_is_lower_than_base_weight_pressure(self):
max_system_pressure = 0
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=3, pressure=1.99)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=3, pressure=2.289848)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 12, max_system_pressure, self.c_p_matrix, self.q_z).almost_equal(
expected,

View File

@@ -49,22 +49,22 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
PanelType.Corner: {
'anchors': [3, 2, 0, 0, 0],
'ballast blocks': [2, 5, 20, 10, 0],
'pressure': ['3.48', '5.03', '12.90', '7.62', '2.40']
'pressure': ['3.48', '5.03', '12.91', '7.62', '2.39']
},
PanelType.NorthSouth: {
'anchors': [2, 1, 0, 0, 0],
'ballast blocks': [11, 15, 17, 8, 0],
'pressure': ['8.24', '10.32', '11.35', '6.59', '2.40']
'ballast blocks': [12, 15, 17, 8, 0],
'pressure': ['8.75', '10.30', '11.34', '6.56', '2.37']
},
PanelType.EastWest: {
'anchors': [2, 1, 0, 0, 0],
'ballast blocks': [10, 14, 17, 8, 0],
'pressure': ['7.55', '9.63', '11.18', '6.46', '2.26']
'pressure': ['7.62', '9.69', '11.24', '6.52', '2.28']
},
PanelType.Middle: {
'anchors': [2, 0, 0, 0, 0],
'ballast blocks': [2, 22, 12, 5, 0],
'pressure': ['3.34', '13.83', '8.59', '4.85', '2.26']
'pressure': ['3.34', '13.87', '8.63', '4.85', '2.26']
},
}
@@ -90,15 +90,14 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
# Ballast, link tray, cross tray, anchor count, psf
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=20, link_tray=2, cross_tray=1, wind_anchors=0, pressure=12.90),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=17, link_tray=2, cross_tray=1, wind_anchors=0, pressure=11.35),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.26),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=47, link_tray=2, cross_tray=4, wind_anchors=0, pressure=27.07),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=22, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.82),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=6.59),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=35, link_tray=2, cross_tray=3, wind_anchors=0, pressure=20.79)
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=20, link_tray=2, cross_tray=1, wind_anchors=0, pressure=12.910398406374503),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=17, link_tray=2, cross_tray=1, wind_anchors=0, pressure=11.336414342629482),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.2788844621513946),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=47, link_tray=2, cross_tray=4, wind_anchors=0, pressure=27.082988047808765),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=22, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.874521912350598),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=6.564223107569722),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=35, link_tray=2, cross_tray=3, wind_anchors=0, pressure=20.8049203187251)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels), expected_value, decimal=2)
def test_update_ballast(self):
@@ -116,16 +115,19 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
]
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1, ballast=5, link_tray=0, cross_tray=0, pressure=4.99),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=2, link_tray=0, cross_tray=0, pressure=3.43),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.26),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0, ballast=2, link_tray=1, cross_tray=0, pressure=3.48),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1, ballast=7, link_tray=2, cross_tray=0, pressure=5.94),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.40),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=2, seismic_anchors=0, ballast=5, link_tray=1, cross_tray=0, pressure=5.03)
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1, ballast=5, link_tray=0, cross_tray=0, pressure=4.982729083665339),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=2, link_tray=0, cross_tray=0, pressure=3.408745019920319),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.2788844621513946),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0, ballast=2, link_tray=1, cross_tray=0, pressure=3.4768525896414344),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1, ballast=7, link_tray=2, cross_tray=0, pressure=5.98),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.3728884462151396),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=2, seismic_anchors=0, ballast=5, link_tray=1, cross_tray=0, pressure=5.030637450199203)
]
result = self.subject.update_ballast(self.c_p_matrix, self.q_z, panels)
print("===")
print(result)
print("===")
assert_array_is_close(result, expected_value, decimal=2)
def test_update_ballast_with_fuzzy_wind_zone(self):
@@ -143,13 +145,13 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
]
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1, ballast=9, link_tray=2, cross_tray=0, pressure=7.15, fuzzy_wind_zone=True),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=5, link_tray=0, cross_tray=0, pressure=4.99, fuzzy_wind_zone=True),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.26, fuzzy_wind_zone=True),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0, ballast=9, link_tray=2, cross_tray=0, pressure=7.15, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1, ballast=11, link_tray=2, cross_tray=1, pressure=8.07, fuzzy_wind_zone=True),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.40, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=2, seismic_anchors=0, ballast=11, link_tray=2, cross_tray=0, pressure=8.18, fuzzy_wind_zone=True)
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1, ballast=9, link_tray=2, cross_tray=0, pressure=7.150517928286853, fuzzy_wind_zone=True),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=5, link_tray=0, cross_tray=0, pressure=4.962529880478088, fuzzy_wind_zone=True),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.2788844621513946, fuzzy_wind_zone=True),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0, ballast=9, link_tray=2, cross_tray=0, pressure=7.150517928286853, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1, ballast=11, link_tray=2, cross_tray=1, pressure=8.114382470119523, fuzzy_wind_zone=True),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1, ballast=0, link_tray=0, cross_tray=0, pressure=2.3728884462151396, fuzzy_wind_zone=True),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=2, seismic_anchors=0, ballast=11, link_tray=2, cross_tray=0, pressure=8.186374501992033, fuzzy_wind_zone=True)
]
result = self.subject.update_ballast(self.c_p_matrix, self.q_z, panels)
@@ -192,15 +194,14 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
]
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=20, link_tray=2, cross_tray=1, wind_anchors=0, pressure=12.90),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=17, link_tray=2, cross_tray=1, wind_anchors=0, pressure=11.35),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.26),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=2, link_tray=1, cross_tray=0, wind_anchors=3, pressure=3.48),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=22, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.83),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=6.59),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=5, link_tray=1, cross_tray=0, wind_anchors=2, pressure=5.03)
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=20, link_tray=2, cross_tray=1, wind_anchors=0, pressure=12.910398406374503),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=17, link_tray=2, cross_tray=1, wind_anchors=0, pressure=11.336414342629482),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.2788844621513946),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=2, link_tray=1, cross_tray=0, wind_anchors=3, pressure=3.4768525896414344),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=22, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.874521912350598),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=6.564223107569722),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=5, link_tray=1, cross_tray=0, wind_anchors=2, pressure=5.030637450199203)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels), expected_value, decimal=2)
def test_ballast_count_when_base_weight_greater_than_uplift(self):
@@ -212,11 +213,11 @@ class BallastCalculatorWhenDualTiltAnd96CellTest(unittest.TestCase):
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 14, 100, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=45, link_tray=2, cross_tray=4, wind_anchors=0, pressure=18.87),
Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=45, link_tray=2, cross_tray=4, wind_anchors=0, pressure=18.920438),
decimal=2)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 20, 100, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0, pressure=19.07),
Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0, pressure=19.116474),
decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):

View File

@@ -48,23 +48,23 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
expected_value = {
PanelType.Corner: {
'anchors': [4, 3, 0, 0, 0],
'ballast blocks': [15, 10, 33, 16, 0],
'pressure': ['8.43', '6.38', '15.93', '8.83', '2.12']
'ballast blocks': [14, 10, 32, 16, 0],
'pressure': ['8.26', '6.63', '15.76', '9.08', '2.39']
},
PanelType.NorthSouth: {
'anchors': [4, 3, 0, 0, 0],
'ballast blocks': [5, 2, 28, 14, 0],
'pressure': ['4.22', '2.97', '13.77', '7.90', '2.10']
'ballast blocks': [4, 2, 27, 13, 0],
'pressure': ['4.08', '3.22', '13.63', '7.77', '2.37']
},
PanelType.EastWest: {
'anchors': [4, 3, 0, 0, 0],
'ballast blocks': [6, 3, 28, 14, 0],
'pressure': ['4.58', '3.29', '13.72', '7.86', '2.01']
'ballast blocks': [5, 2, 27, 13, 0],
'pressure': ['4.49', '3.19', '13.63', '7.76', '2.30']
},
PanelType.Middle: {
'anchors': [3, 2, 0, 0, 0],
'ballast blocks': [11, 10, 23, 11, 0],
'pressure': ['6.61', '6.20', '11.59', '6.61', '1.99']
'ballast blocks': [10, 10, 23, 11, 0],
'pressure': ['6.51', '6.51', '11.90', '6.92', '2.28']
},
}
@@ -104,27 +104,28 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
expected_value = {
PanelType.Corner: {
'anchors': [0, 0, 0, 0, 0],
'ballast blocks': [8, 3, 0, 0, 0],
'pressure': ['4.79', '3.11', '2.12', '2.12', '2.12']
'ballast blocks': [7, 3, 0, 0, 0],
'pressure': ['4.68', '3.37', '2.39', '2.39', '2.39']
},
PanelType.NorthSouth: {
'anchors': [0, 0, 0, 0, 0],
'ballast blocks': [3, 0, 0, 0, 0],
'pressure': ['3.08', '2.10', '2.10', '2.10', '2.10']
'pressure': ['3.35', '2.37', '2.37', '2.37', '2.37']
},
PanelType.EastWest: {
'anchors': [0, 0, 0, 0, 0],
'ballast blocks': [4, 0, 0, 0, 0],
'pressure': ['3.32', '2.01', '2.01', '2.01', '2.01']
'ballast blocks': [3, 0, 0, 0, 0],
'pressure': ['3.28', '2.30', '2.30', '2.30', '2.30']
},
PanelType.Middle: {
'anchors': [0, 0, 0, 0, 0],
'ballast blocks': [1, 0, 0, 0, 0],
'pressure': ['2.32', '1.99', '1.99', '1.99', '1.99']
'ballast blocks': [0, 0, 0, 0, 0],
'pressure': ['2.28', '2.28', '2.28', '2.28', '2.28']
},
}
received_table = self.subject.summary_table(self.c_p_matrix, self.q_z)
print("====\r\n", received_table)
eq_(received_table.keys(), expected_value.keys())
for key in expected_value.keys():
received_table[key].pop('warnings')
@@ -143,20 +144,20 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
self.values.ballast_block_weight.return_value = 20
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=33, link_tray=2, cross_tray=3, wind_anchors=0,
pressure=15.93),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=28, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=13.77),
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0,
pressure=15.764460),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=27, link_tray=2, cross_tray=2, wind_anchors=0,
pressure=13.630342),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=2.01),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=15, link_tray=2, cross_tray=1, wind_anchors=4,
pressure=8.43, warnings=[PanelWarnings.MaxPsf]),
pressure=2.299163),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=2, cross_tray=1, wind_anchors=4,
pressure=8.263513, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=40, link_tray=2, cross_tray=4, wind_anchors=0,
pressure=18.68),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=14, link_tray=2, cross_tray=0, wind_anchors=0,
pressure=7.90),
pressure=18.993076),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=13, link_tray=2, cross_tray=0, wind_anchors=0,
pressure=7.767559),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=10, link_tray=2, cross_tray=1, wind_anchors=3,
pressure=6.38),
pressure=6.625349),
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, raw_data),
@@ -178,19 +179,19 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
expected = [
Panel(wind_zone=2, panel_type=PanelType.Corner, wind_anchors=0, seismic_anchors=1,
ballast=18, link_tray=2, cross_tray=1, pressure=9.65),
ballast=17, link_tray=2, cross_tray=1, pressure=9.492135463546356),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1,
ballast=13, link_tray=2, cross_tray=0, pressure=7.50),
ballast=12, link_tray=2, cross_tray=0, pressure=7.358017551755176),
Panel(wind_zone=4, panel_type=PanelType.EastWest, wind_anchors=0, seismic_anchors=1,
ballast=0, link_tray=0, cross_tray=0, pressure=2.01),
ballast=0, link_tray=0, cross_tray=0, pressure=2.299162916291629),
Panel(wind_zone=0, panel_type=PanelType.Corner, wind_anchors=4, seismic_anchors=0,
ballast=15, link_tray=2, cross_tray=1, pressure=8.43),
ballast=14, link_tray=2, cross_tray=1, pressure=8.263512601260127),
Panel(wind_zone=1, panel_type=PanelType.Middle, wind_anchors=0, seismic_anchors=1,
ballast=25, link_tray=2, cross_tray=3, pressure=12.47),
ballast=24, link_tray=2, cross_tray=3, pressure=12.375918091809181),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, wind_anchors=0, seismic_anchors=1,
ballast=0, link_tray=0, cross_tray=0, pressure=2.10),
ballast=0, link_tray=0, cross_tray=0, pressure=2.3677610261026105),
Panel(wind_zone=1, panel_type=PanelType.Corner, wind_anchors=3, seismic_anchors=0,
ballast=10, link_tray=2, cross_tray=1, pressure=6.38),
ballast=10, link_tray=2, cross_tray=1, pressure=6.625348784878488),
]
result = self.subject.update_ballast(self.c_p_matrix, self.q_z, panels)
@@ -234,13 +235,13 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
]
expected_value = [
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=33, link_tray=2, cross_tray=3, wind_anchors=0, pressure=15.93),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=28, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.77),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.01),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=15, link_tray=2, cross_tray=1, wind_anchors=4, pressure=8.43, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=10, link_tray=2, cross_tray=1, wind_anchors=2, pressure=6.20),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=14, link_tray=2, cross_tray=0, wind_anchors=0, pressure=7.90),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=10, link_tray=2, cross_tray=1, wind_anchors=3, pressure=6.38)
Panel(wind_zone=2, panel_type=PanelType.Corner, ballast=32, link_tray=2, cross_tray=3, wind_anchors=0, pressure=15.764460),
Panel(wind_zone=2, panel_type=PanelType.NorthSouth, ballast=27, link_tray=2, cross_tray=2, wind_anchors=0, pressure=13.630342),
Panel(wind_zone=4, panel_type=PanelType.EastWest, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.299163),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=2, cross_tray=1, wind_anchors=4, pressure= 8.263513, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=1, panel_type=PanelType.Middle, ballast=10, link_tray=2, cross_tray=1, wind_anchors=2, pressure=6.513135),
Panel(wind_zone=3, panel_type=PanelType.NorthSouth, ballast=13, link_tray=2, cross_tray=0, wind_anchors=0, pressure=7.767559),
Panel(wind_zone=1, panel_type=PanelType.Corner, ballast=10, link_tray=2, cross_tray=1, wind_anchors=3, pressure=6.625349)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panel_data), expected_value, decimal=2)
@@ -272,28 +273,28 @@ class BallastCalculatorWhenDualTiltAndPSeriesTest(unittest.TestCase):
self.q_z = 18.43072
self.subject = BallastCalculator(self.values)
expected = Panel(wind_zone=0, panel_type=PanelType.NorthSouth, ballast=3, link_tray=0, cross_tray=0, wind_anchors=0, pressure=3.08)
expected = Panel(wind_zone=0, panel_type=PanelType.NorthSouth, ballast=3, link_tray=0, cross_tray=0, wind_anchors=0, pressure=3.350659)
received = self.subject.ballast_tray_and_anchor_count(0, PanelType.NorthSouth, 16, 8, self.c_p_matrix, self.q_z)
assert received.almost_equal(expected, decimal=2)
def test_ballast_count_when_base_weight_greater_than_uplift(self):
expected = Panel(wind_zone=4, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=1.99)
expected = Panel(wind_zone=4, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=0, pressure=2.277252)
assert self.subject.ballast_tray_and_anchor_count(4, PanelType.Middle, 14, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=15, link_tray=2, cross_tray=2, wind_anchors=3, pressure=6.47)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=14, link_tray=2, cross_tray=2, wind_anchors=3, pressure=6.495729)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 14, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=11, link_tray=2, cross_tray=1, wind_anchors=3, pressure=6.61)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=10, link_tray=2, cross_tray=1, wind_anchors=3, pressure=6.513135)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 20, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=18, link_tray=2, cross_tray=2, wind_anchors=3, pressure=6.59)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=17, link_tray=2, cross_tray=2, wind_anchors=3, pressure=6.659545)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 12, 100, self.c_p_matrix, self.q_z).almost_equal(expected, decimal=2)
def test_when_max_system_pressure_is_lower_than_base_weight_pressure(self):
max_system_pressure = 0
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=4, pressure=1.99)
expected = Panel(wind_zone=0, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0, wind_anchors=4, pressure=2.277252)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Middle, 12, max_system_pressure, self.c_p_matrix, self.q_z).almost_equal(
expected,

View File

@@ -54,23 +54,23 @@ class BallastCalculatorWhenSingleTiltAnd128CellTest(unittest.TestCase):
expected_value = {
PanelType.Corner: {
'anchors': [7, 5, 3, 3, 2, 2, 0, 2, 7, 4, 0],
'ballast blocks': [9, 14, 5, 5, 3, 1, 13, 11, 11, 8, 11],
'pressure': ['7.43', '10.31', '4.95', '4.95', '3.79', '2.64', '9.55', '8.49', '8.58', '6.76', '8.40']
'ballast blocks': [10, 15, 5, 6, 3, 1, 13, 11, 12, 9, 11],
'pressure': ['7.89', '10.77', '4.83', '5.41', '3.68', '2.53', '9.53', '8.38', '9.04', '7.23', '8.29']
},
PanelType.NorthSouth: {
'anchors': [6, 5, 3, 2, 0, 1, 0, 2, 6, 4, 0],
'ballast blocks': [14, 6, 1, 15, 19, 13, 12, 6, 12, 1, 10],
'pressure': ['10.13', '5.44', '2.47', '10.71', '13.01', '9.46', '8.89', '5.35', '8.98', '2.47', '7.65']
'pressure': ['10.11', '5.41', '2.45', '10.68', '12.98', '9.44', '8.87', '5.32', '8.96', '2.45', '7.62']
},
PanelType.EastWest: {
'anchors': [5, 4, 2, 2, 1, 0, 0, 2, 6, 1, 0],
'ballast blocks': [11, 7, 2, 1, 15, 21, 12, 6, 11, 14, 6],
'pressure': ['8.49', '6.19', '3.16', '2.58', '10.79', '14.25', '8.98', '5.52', '8.58', '10.22', '5.46']
'ballast blocks': [12, 7, 2, 1, 15, 21, 12, 6, 12, 14, 6],
'pressure': ['8.94', '6.07', '3.05', '2.47', '10.67', '14.12', '8.85', '5.40', '9.03', '10.09', '5.35']
},
PanelType.Middle: {
'anchors': [5, 4, 2, 1, 0, 0, 0, 2, 6, 1, 0],
'ballast blocks': [9, 5, 2, 15, 18, 20, 12, 5, 7, 13, 6],
'pressure': ['7.21', '4.91', '3.02', '10.66', '12.39', '13.63', '8.93', '4.82', '6.06', '9.51', '5.32']
'pressure': ['7.14', '4.84', '2.97', '10.59', '12.32', '13.56', '8.87', '4.75', '5.99', '9.44', '5.27']
},
}
@@ -96,21 +96,21 @@ class BallastCalculatorWhenSingleTiltAnd128CellTest(unittest.TestCase):
expected_value = [
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=1, link_tray=0, cross_tray=0, wind_anchors=2,
pressure=2.64),
pressure=2.532134),
Panel(wind_zone=6, panel_type=PanelType.NorthSouth, ballast=12, link_tray=0, cross_tray=1, wind_anchors=0,
pressure=8.89),
pressure=8.866209),
Panel(wind_zone=7, panel_type=PanelType.EastWest, ballast=6, link_tray=2, cross_tray=0, wind_anchors=2,
pressure=5.52, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=9, link_tray=0, cross_tray=2, wind_anchors=7,
pressure=7.43, warnings=[PanelWarnings.MaxPsf]),
pressure=5.400562, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=10, link_tray=0, cross_tray=2, wind_anchors=7,
pressure=7.891559, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=7, link_tray=1, cross_tray=1, wind_anchors=6,
pressure=6.06, warnings=[PanelWarnings.MaxPsf]),
pressure=5.991164, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.NorthSouth, ballast=1, link_tray=0, cross_tray=0, wind_anchors=4,
pressure=2.47, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=9.55),
pressure=2.446694, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=1, wind_anchors=0,
pressure=9.527003),
Panel(wind_zone=6, panel_type=PanelType.EastWest, ballast=12, link_tray=2, cross_tray=0, wind_anchors=0,
pressure=8.98)
pressure=8.852688)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels),
@@ -169,19 +169,19 @@ class BallastCalculatorWhenSingleTiltAnd128CellTest(unittest.TestCase):
expected_value = [
Panel(wind_zone=7, panel_type=PanelType.Corner, ballast=11, link_tray=0, cross_tray=1,
wind_anchors=2, pressure=8.49, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=2, pressure=8.376295, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=7, panel_type=PanelType.NorthSouth, ballast=6, link_tray=0, cross_tray=0,
wind_anchors=2, pressure=5.35, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=2, pressure=5.323465, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.EastWest, ballast=29, link_tray=2, cross_tray=2,
wind_anchors=0, pressure=18.94),
wind_anchors=0, pressure=18.814946),
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=1, link_tray=0, cross_tray=0,
wind_anchors=2, pressure=2.64),
wind_anchors=2, pressure=2.532134),
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=12, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.93),
wind_anchors=0, pressure=8.867935),
Panel(wind_zone=8, panel_type=PanelType.NorthSouth, ballast=12, link_tray=0, cross_tray=2,
wind_anchors=6, pressure=8.98, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=0,
wind_anchors=0, pressure=9.55)
wind_anchors=6, pressure=8.956827, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=1,
wind_anchors=0, pressure=9.527003)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panel_data),
@@ -213,41 +213,41 @@ class BallastCalculatorWhenSingleTiltAnd128CellTest(unittest.TestCase):
self.q_z = 18.446284
expected = Panel(wind_zone=10, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=0, pressure=1.87)
wind_anchors=0, pressure=1.819845)
assert self.subject.ballast_tray_and_anchor_count(10, PanelType.Middle, 14, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=16, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.47)
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=17, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.810399)
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 14, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=12, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.93)
wind_anchors=0, pressure=8.867935)
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=9, link_tray=0, cross_tray=2,
wind_anchors=7, pressure=7.43, warnings=[PanelWarnings.MaxPsf])
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=10, link_tray=0, cross_tray=2,
wind_anchors=7, pressure=7.891559, warnings=[PanelWarnings.MaxPsf])
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=7, link_tray=1, cross_tray=1,
wind_anchors=6, pressure=6.06, warnings=[PanelWarnings.MaxPsf])
wind_anchors=6, pressure=5.991164, warnings=[PanelWarnings.MaxPsf])
assert self.subject.ballast_tray_and_anchor_count(8, PanelType.Middle, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
def test_ballast_blocks_when_panel_exceeds_max_system_pressure(self):
expected = Panel(wind_zone=5, panel_type=PanelType.Middle, ballast=7, link_tray=1, cross_tray=0,
wind_anchors=1, pressure=4.76)
wind_anchors=1, pressure=4.692301)
assert self.subject.ballast_tray_and_anchor_count(5, PanelType.Middle, 14, 10, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=2,
wind_anchors=7, pressure=7.49, warnings=[PanelWarnings.MaxPsf])
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=0, cross_tray=2,
wind_anchors=7, pressure=7.776488, warnings=[PanelWarnings.MaxPsf])
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 14, 10, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
@@ -255,7 +255,7 @@ class BallastCalculatorWhenSingleTiltAnd128CellTest(unittest.TestCase):
max_system_pressure = 0
expected = Panel(wind_zone=5, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=2, pressure=1.87)
wind_anchors=2, pressure=1.819845)
assert self.subject.ballast_tray_and_anchor_count(5, PanelType.Middle, 12, max_system_pressure,
self.c_p_matrix, self.q_z).almost_equal(
expected,

View File

@@ -54,24 +54,23 @@ class BallastCalculatorWhenSingleTiltAnd96CellTest(unittest.TestCase):
expected_value = {
PanelType.Corner: {
'anchors': [5, 4, 2, 2, 1, 1, 0, 2, 6, 3, 0],
'ballast blocks': [14, 10, 9, 9, 11, 10, 10, 2, 2, 9, 8],
'pressure': ['12.93', '9.79', '9.03', '9.03', '10.56', '9.70', '9.70', '3.60', '3.69', '9.03', '8.18']
'ballast blocks': [14, 10, 9, 10, 11, 10, 11, 2, 2, 9, 8],
'pressure': ['12.81', '9.67', '8.90', '9.67', '10.43', '9.58', '10.34', '3.47', '3.57', '8.90', '8.05']
},
PanelType.NorthSouth: {
'anchors': [5, 4, 2, 2, 0, 1, 0, 1, 5, 3, 0],
'ballast blocks': [5, 2, 5, 5, 15, 7, 9, 13, 3, 2, 7],
'pressure': ['5.79', '3.50', '5.70', '5.70', '13.41', '7.23', '8.84', '11.89', '4.26', '3.41', '7.23']
'pressure': ['5.74', '3.45', '5.74', '5.65', '13.37', '7.17', '8.79', '11.84', '4.22', '3.36', '7.17']
},
PanelType.EastWest: {
'anchors': [4, 3, 1, 1, 1, 0, 0, 1, 5, 1, 0],
'ballast blocks': [6, 6, 10, 9, 8, 16, 9, 13, 3, 7, 4],
'pressure': ['6.79', '6.70', '9.75', '8.99', '8.23', '14.42', '8.99', '12.13', '4.41', '7.46', '5.09']
'pressure': ['6.62', '6.52', '9.57', '8.81', '8.05', '14.24', '8.81', '11.96', '4.24', '7.29', '4.93']
},
PanelType.Middle: {
'anchors': [3, 2, 1, 0, 0, 0, 0, 1, 3, 0, 0],
'ballast blocks': [11, 13, 6, 19, 12, 13, 8, 7, 15, 18, 3],
'pressure': ['10.42', '11.95', '6.52', '16.61', '11.19', '11.95', '8.05', '7.29', '13.56', '15.76',
'4.15']
'ballast blocks': [11, 13, 6, 19, 12, 13, 8, 8, 15, 18, 3],
'pressure': ['10.40', '11.92', '6.49', '16.59', '11.16', '11.92', '8.02', '8.02', '13.54', '15.73', '4.13']
},
}
@@ -96,14 +95,14 @@ class BallastCalculatorWhenSingleTiltAnd96CellTest(unittest.TestCase):
self.values.ballast_block_weight.return_value = 20
expected_value = [
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=25, link_tray=0, cross_tray=2, wind_anchors=0, pressure=21.32),
Panel(wind_zone=6, panel_type=PanelType.NorthSouth, ballast=9, link_tray=0, cross_tray=1, wind_anchors=0, pressure=8.84),
Panel(wind_zone=7, panel_type=PanelType.EastWest, ballast=28, link_tray=2, cross_tray=2, wind_anchors=0, pressure=23.65),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=0, cross_tray=2, wind_anchors=5, pressure=12.93, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=15, link_tray=1, cross_tray=2, wind_anchors=3, pressure=13.56, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.NorthSouth, ballast=2, link_tray=0, cross_tray=0, wind_anchors=3, pressure=3.41, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=10, link_tray=0, cross_tray=0, wind_anchors=0, pressure=9.70),
Panel(wind_zone=6, panel_type=PanelType.EastWest, ballast=9, link_tray=2, cross_tray=0, wind_anchors=0, pressure=8.99)
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=25, link_tray=0, cross_tray=2, wind_anchors=0, pressure=21.200142),
Panel(wind_zone=6, panel_type=PanelType.NorthSouth, ballast=9, link_tray=0, cross_tray=1, wind_anchors=0, pressure=8.791605),
Panel(wind_zone=7, panel_type=PanelType.EastWest, ballast=28, link_tray=2, cross_tray=2, wind_anchors=0, pressure=23.487752),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=0, cross_tray=2, wind_anchors=5, pressure=12.810842, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=15, link_tray=1, cross_tray=2, wind_anchors=3, pressure=13.538805, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.NorthSouth, ballast=2, link_tray=0, cross_tray=0, wind_anchors=3, pressure=3.360677, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=11, link_tray=0, cross_tray=0, wind_anchors=0, pressure=10.337906),
Panel(wind_zone=6, panel_type=PanelType.EastWest, ballast=9, link_tray=2, cross_tray=0, wind_anchors=0, pressure=8.812197)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels), expected_value, decimal=2)
@@ -160,13 +159,13 @@ class BallastCalculatorWhenSingleTiltAnd96CellTest(unittest.TestCase):
]
expected_value = array([
Panel(wind_zone=7, panel_type=PanelType.Corner, ballast=2, link_tray=0, cross_tray=0, wind_anchors=2, pressure=3.60),
Panel(wind_zone=7, panel_type=PanelType.NorthSouth, ballast=13, link_tray=0, cross_tray=1, wind_anchors=1, pressure=11.89),
Panel(wind_zone=9, panel_type=PanelType.EastWest, ballast=22, link_tray=2, cross_tray=1, wind_anchors=0, pressure=18.99),
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=25, link_tray=0, cross_tray=2, wind_anchors=0, pressure=21.32),
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=8.05),
Panel(wind_zone=8, panel_type=PanelType.NorthSouth, ballast=3, link_tray=0, cross_tray=1, wind_anchors=5, pressure=4.26, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=10, link_tray=0, cross_tray=0, wind_anchors=0, pressure=9.70),
Panel(wind_zone=7, panel_type=PanelType.Corner, ballast=2, link_tray=0, cross_tray=0, wind_anchors=2, pressure=3.473933),
Panel(wind_zone=7, panel_type=PanelType.NorthSouth, ballast=13, link_tray=0, cross_tray=1, wind_anchors=1, pressure=11.842260),
Panel(wind_zone=9, panel_type=PanelType.EastWest, ballast=22, link_tray=2, cross_tray=1, wind_anchors=0, pressure=18.819488),
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=25, link_tray=0, cross_tray=2, wind_anchors=0, pressure=21.200142),
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=8, link_tray=1, cross_tray=0, wind_anchors=0, pressure=8.015213),
Panel(wind_zone=8, panel_type=PanelType.NorthSouth, ballast=3, link_tray=0, cross_tray=1, wind_anchors=5, pressure=4.215623, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=11, link_tray=0, cross_tray=0, wind_anchors=0, pressure=10.337906),
])
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels), expected_value, decimal=2)
@@ -175,43 +174,43 @@ class BallastCalculatorWhenSingleTiltAnd96CellTest(unittest.TestCase):
assert self.subject.ballast_tray_and_anchor_count(10, PanelType.Middle, 14, 1000, self.c_p_matrix,
30.155851).almost_equal(
Panel(wind_zone=10, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=0, pressure=1.86),
wind_anchors=0, pressure=1.842977),
decimal=2)
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 14, 1000, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=11, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=7.91),
wind_anchors=0, pressure=7.878697),
decimal=2)
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=8, link_tray=1, cross_tray=0,
wind_anchors=0, pressure=8.05),
wind_anchors=0, pressure=8.015213),
decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=14, link_tray=0, cross_tray=2,
wind_anchors=5, pressure=12.93, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=5, pressure=12.810842, warnings=[PanelWarnings.MaxPsf]),
decimal=2)
assert self.subject.ballast_tray_and_anchor_count(8, PanelType.Middle, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=15, link_tray=1, cross_tray=2,
wind_anchors=3, pressure=13.56, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=3, pressure=13.538805, warnings=[PanelWarnings.MaxPsf]),
decimal=2)
def test_ballast_blocks_when_panel_exceeds_max_system_pressure(self):
assert self.subject.ballast_tray_and_anchor_count(5, PanelType.Middle, 14, 10, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=5, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=1, pressure=1.86),
wind_anchors=1, pressure=1.842977),
decimal=2)
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 14, 10, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=6, pressure=2.08, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=6, pressure=1.948606, warnings=[PanelWarnings.MaxPsf]),
decimal=2)
def test_when_max_system_pressure_is_lower_than_base_weight_pressure(self):
@@ -220,7 +219,7 @@ class BallastCalculatorWhenSingleTiltAnd96CellTest(unittest.TestCase):
assert self.subject.ballast_tray_and_anchor_count(5, PanelType.Middle, 12, max_system_pressure, self.c_p_matrix,
self.q_z).almost_equal(
Panel(wind_zone=5, panel_type=PanelType.Middle, ballast=0, link_tray=0, cross_tray=0,
wind_anchors=1, pressure=1.86),
wind_anchors=1, pressure=1.842977),
decimal=2)
def test_uplift(self):

View File

@@ -55,27 +55,27 @@ class BallastCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
PanelType.Corner: {
'anchors': [7, 5, 3, 3, 2, 1, 0, 2, 7, 4, 0],
'ballast blocks': [5, 11, 3, 4, 2, 15, 13, 9, 7, 6, 11],
'pressure': ['5.13', '8.75', '3.83', '4.43', '3.22', '11.16', '9.86', '7.45', '6.33', '5.73', '8.65']
'pressure': ['5.07', '8.69', '3.77', '4.37', '3.17', '11.10', '9.80', '7.39', '6.37', '5.67', '8.59']
},
PanelType.NorthSouth: {
'anchors': [6, 5, 2, 2, 0, 1, 0, 2, 6, 3, 0],
'ballast blocks': [10, 3, 15, 14, 18, 12, 12, 5, 8, 14, 9],
'pressure': ['8.06', '3.74', '11.07', '10.37', '12.88', '9.17', '9.17', '4.85', '6.85', '10.47', '7.26']
'pressure': ['8.09', '3.78', '11.11', '10.41', '12.82', '9.20', '9.20', '4.89', '6.89', '10.50', '7.30']
},
PanelType.EastWest: {
'anchors': [5, 4, 2, 1, 1, 0, 0, 2, 6, 1, 0],
'ballast blocks': [8, 5, 1, 15, 14, 20, 12, 5, 8, 13, 6],
'pressure': ['6.94', '5.04', '2.56', '11.16', '10.56', '14.18', '9.26', '5.04', '6.94', '9.96', '5.57']
'pressure': ['6.87', '4.97', '2.50', '11.09', '10.49', '14.11', '9.19', '4.97', '6.87', '9.89', '5.51']
},
PanelType.Middle: {
'anchors': [5, 4, 1, 1, 0, 0, 0, 2, 6, 1, 0],
'ballast blocks': [6, 3, 15, 14, 18, 20, 11, 4, 4, 12, 6],
'pressure': ['5.60', '3.69', '11.02', '10.42', '12.83', '14.13', '8.51', '4.29', '4.39', '9.21', '5.43']
'pressure': ['5.59', '3.68', '11.01', '10.41', '12.82', '14.12', '8.60', '4.29', '4.38', '9.20', '5.44']
},
}
received_table = self.subject.summary_table(self.c_p_matrix, self.q_z)
print("===\r\n",received_table)
eq_(received_table.keys(), expected_value.keys())
for key in expected_value.keys():
received_table[key].pop('warnings')
@@ -97,21 +97,21 @@ class BallastCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
expected_value = [
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=15, link_tray=0, cross_tray=1, wind_anchors=1,
pressure=11.16),
pressure=11.101436),
Panel(wind_zone=6, panel_type=PanelType.NorthSouth, ballast=12, link_tray=0, cross_tray=1, wind_anchors=0,
pressure=9.17),
pressure=9.202700),
Panel(wind_zone=7, panel_type=PanelType.EastWest, ballast=5, link_tray=2, cross_tray=0, wind_anchors=2,
pressure=5.04, warnings=[PanelWarnings.MaxPsf]),
pressure=4.967106, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=5, link_tray=0, cross_tray=1, wind_anchors=7,
pressure=5.13, warnings=[PanelWarnings.MaxPsf]),
pressure=5.070833, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=8, panel_type=PanelType.Middle, ballast=4, link_tray=1, cross_tray=1, wind_anchors=6,
pressure=4.39, warnings=[PanelWarnings.MaxPsf]),
pressure=4.380027, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.NorthSouth, ballast=14, link_tray=0, cross_tray=2, wind_anchors=3,
pressure=10.47, warnings=[PanelWarnings.MaxPsf]),
pressure=10.503803, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=0, wind_anchors=0,
pressure=9.86),
pressure=9.800333),
Panel(wind_zone=6, panel_type=PanelType.EastWest, ballast=12, link_tray=2, cross_tray=0, wind_anchors=0,
pressure=9.26)
pressure=9.188528)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panels),
@@ -170,19 +170,19 @@ class BallastCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
expected_value = [
Panel(wind_zone=7, panel_type=PanelType.Corner, ballast=9, link_tray=0, cross_tray=0,
wind_anchors=2, pressure=7.44, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=2, pressure=7.388092, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=7, panel_type=PanelType.NorthSouth, ballast=5, link_tray=0, cross_tray=0,
wind_anchors=2, pressure=4.85, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=2, pressure=4.886296, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=9, panel_type=PanelType.EastWest, ballast=28, link_tray=2, cross_tray=2,
wind_anchors=0, pressure=19.10),
wind_anchors=0, pressure=19.027457),
Panel(wind_zone=5, panel_type=PanelType.Corner, ballast=15, link_tray=0, cross_tray=1,
wind_anchors=1, pressure=11.16),
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=11, link_tray=1, cross_tray=0,
wind_anchors=0, pressure=8.51),
wind_anchors=1, pressure=11.101436),
Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=11, link_tray=1, cross_tray=1,
wind_anchors=0, pressure= 8.601449),
Panel(wind_zone=8, panel_type=PanelType.NorthSouth, ballast=8, link_tray=0, cross_tray=2,
wind_anchors=6, pressure=6.85, warnings=[PanelWarnings.MaxPsf]),
wind_anchors=6, pressure=6.885441, warnings=[PanelWarnings.MaxPsf]),
Panel(wind_zone=6, panel_type=PanelType.Corner, ballast=13, link_tray=0, cross_tray=0,
wind_anchors=0, pressure=9.86)
wind_anchors=0, pressure=9.800333)
]
assert_array_is_close(self.subject.ballast_and_trays_matrix(self.c_p_matrix, self.q_z, panel_data),
@@ -221,22 +221,22 @@ class BallastCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
def test_ballast_and_tray_count_iterates_on_link_and_cross_trays_changing_weight_and_ballast_requirements(self):
panel = self.subject.ballast_tray_and_anchor_count(3, PanelType.NorthSouth, 20, 17, self.c_p_matrix, self.q_z)
assert_almost_equal(panel.pressure, 10.37, 2)
assert_almost_equal(panel.pressure, 10.408820882088209, 2)
def test_individual_ballast_block_uplift_greater_than_base_weight(self):
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=16, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.73)
wind_anchors=0, pressure=8.722061)
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 14, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=11, link_tray=1, cross_tray=0,
wind_anchors=0, pressure=8.51)
expected = Panel(wind_zone=6, panel_type=PanelType.Middle, ballast=11, link_tray=1, cross_tray=1,
wind_anchors=0, pressure=8.601449)
assert self.subject.ballast_tray_and_anchor_count(6, PanelType.Middle, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
def test_ballast_blocks_exceeding_tray_capacity(self):
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=5, link_tray=0, cross_tray=1,
wind_anchors=7, pressure=5.13, warnings=[PanelWarnings.MaxPsf])
wind_anchors=7, pressure=5.070833, warnings=[PanelWarnings.MaxPsf])
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 20, 1000, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)
@@ -253,7 +253,7 @@ class BallastCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
self.q_z).almost_equal(expected, decimal=2)
expected = Panel(wind_zone=0, panel_type=PanelType.Corner, ballast=7, link_tray=0, cross_tray=1,
wind_anchors=7, pressure=5.07, warnings=[PanelWarnings.MaxPsf])
wind_anchors=7, pressure=5.010527, warnings=[PanelWarnings.MaxPsf])
assert self.subject.ballast_tray_and_anchor_count(0, PanelType.Corner, 14, 10, self.c_p_matrix,
self.q_z).almost_equal(expected, decimal=2)

View File

@@ -56,7 +56,7 @@ class CalculatorTest(unittest.TestCase):
self.subject = Calculator(self.values)
expected = [
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=435, weight=170237,
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=436, weight=170698.330000,
start_row=0, size=863, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=130.5, column_count=98)
]
@@ -70,43 +70,43 @@ class CalculatorTest(unittest.TestCase):
self.subject = Calculator(self.values)
expected = [
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=11, weight=23783, start_row=0,
Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=11, weight=23804, start_row=0,
size=192, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=18, column_count=20),
Subarray(subarray_number=2, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=13113,
Subarray(subarray_number=2, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=13271,
start_row=192, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=4, column_count=24),
Subarray(subarray_number=3, origin=Coordinate(0, 0), required_seismic_anchors=110, weight=40406,
Subarray(subarray_number=3, origin=Coordinate(0, 0), required_seismic_anchors=110, weight=40328,
start_row=288, size=312, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=25, column_count=29),
Subarray(subarray_number=4, origin=Coordinate(0, 0), required_seismic_anchors=72, weight=23404,
Subarray(subarray_number=4, origin=Coordinate(0, 0), required_seismic_anchors=71, weight=23324,
start_row=600, size=168, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=22, column_count=13),
Subarray(subarray_number=5, origin=Coordinate(0, 0), required_seismic_anchors=75, weight=35365,
Subarray(subarray_number=5, origin=Coordinate(0, 0), required_seismic_anchors=74, weight=35170,
start_row=768, size=234, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=26, column_count=14),
Subarray(subarray_number=6, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=10897,
Subarray(subarray_number=6, origin=Coordinate(0, 0), required_seismic_anchors=1, weight=11461,
start_row=1002, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=10, column_count=12),
Subarray(subarray_number=7, origin=Coordinate(0, 0), required_seismic_anchors=29, weight=10951,
Subarray(subarray_number=7, origin=Coordinate(0, 0), required_seismic_anchors=28, weight=10865,
start_row=1098, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=16),
Subarray(subarray_number=8, origin=Coordinate(0, 0), required_seismic_anchors=11, weight=10596,
Subarray(subarray_number=8, origin=Coordinate(0, 0), required_seismic_anchors=12, weight=10780,
start_row=1194, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=15, column_count=17.5),
Subarray(subarray_number=9, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=10544,
Subarray(subarray_number=9, origin=Coordinate(0, 0), required_seismic_anchors=0, weight= 10989,
start_row=1290, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=16),
Subarray(subarray_number=10, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4819,
Subarray(subarray_number=10, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4901,
start_row=1386, size=48, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=6),
Subarray(subarray_number=11, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=10857,
Subarray(subarray_number=11, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=10766,
start_row=1434, size=96, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=13, column_count=13),
Subarray(subarray_number=12, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=6330,
Subarray(subarray_number=12, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=6350,
start_row=1530, size=54, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=8, column_count=8),
Subarray(subarray_number=13, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4201,
Subarray(subarray_number=13, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=4239,
start_row=1584, size=48, column_counted_geometrically=False, row_counted_geometrically=False,
row_count=6, column_count=8)
]
@@ -140,15 +140,15 @@ class CalculatorTest(unittest.TestCase):
self.subject = Calculator(self.values)
received_values = self.subject.summary_values()
expected_values = array([
{'label': 'Total System Weight (lbs)', 'value': 2684},
{'label': 'Max PSF', 'value': 8.03},
{'label': 'Avg PSF', 'value': 5.39},
{'label': 'Total System Weight (lbs)', 'value': 2694},
{'label': 'Max PSF', 'value': 7.9},
{'label': 'Avg PSF', 'value': 5.41},
{'label': 'Total Anchors', 'value': 5},
{'label': 'Total Ballast', 'value': 131},
{'label': 'Max Possible System Weight', 'value': 2789.0},
{'label': 'Max System Weight Ballast Block', 'value': 16},
{'label': 'Total Ballast', 'value': 135},
{'label': 'Max Possible System Weight', 'value': 2792.0},
{'label': 'Max System Weight Ballast Block', 'value': 17},
{'label': 'Seismic Anchor Max. Spacing', 'value': 12},
])
assert_array_equal(received_values, expected_values)
@@ -173,13 +173,13 @@ class CalculatorTest(unittest.TestCase):
received_values = self.subject.documentation_summary_values()
expected_values = {
'total_system_weight': 2684,
'max_psf': 8.03,
'ave_psf': 5.39,
'total_system_weight': 2694,
'max_psf': 7.9,
'ave_psf': 5.41,
'total_anchors': 5,
'total_ballast': 131,
'max_possible_system_weight': 2789.0,
'max_system_weight_ballast_block': 16,
'total_ballast': 135,
'max_possible_system_weight': 2792.0,
'max_system_weight_ballast_block': 17,
'seismic_anchor_max_spacing': 12
}
eq_(received_values, expected_values)
@@ -326,6 +326,8 @@ class CalculatorTest(unittest.TestCase):
expected_csv = expected_file.read()
reader = csv.reader(expected_csv.splitlines(), dialect='excel-tab')
expected = array([row for row in reader])
print("EXPECTED")
print(expected)
power_station_1 = PowerStation(
description='1',
@@ -367,6 +369,8 @@ class CalculatorTest(unittest.TestCase):
)
self.site.power_stations = [power_station_1, power_station_2]
print("COMPUTE")
print(self.subject.compute_bom())
assert_array_equal(self.subject.compute_bom(), expected)
def test_documentation_bom(self):
@@ -404,91 +408,90 @@ class CalculatorTest(unittest.TestCase):
self.site.cad_file = csv_content
self.subject = Calculator(self.values)
expected = [
('104813', 50),
('105317', 0),
('106925', 50),
('107538', 0),
('107549', 100),
('107551', 50),
('107586', 100),
('111147', 0),
('114961', 50),
('507985', 0),
('512021', 9),
('512199', 140),
('512200', 3480),
('512510', 196),
('512511', 196),
('512575', 1),
('512660', 2),
('512661', 2),
('512662', 4),
('512663', 2),
('523923', 0),
('512910', 1),
('513007', 50),
('513299', 0),
('513300', 0),
('513301', 0),
('513302', 0),
('513303', 1),
('513304', 0),
('513586', 0),
('513831', 0),
('513832', 0),
('513833', 670),
('513836', 0),
('513843', 262),
('513844', 214),
('514056', 1000),
('514057', 1000),
('514265', 179),
('514435', 0),
('514436', 0),
('514437', 2),
('514438', 2),
('514439', 0),
('514440', 0),
('514477', 2),
('514478', 0),
('523924', 0),
('523921', 1),
('523922', 0),
('514697', 1),
('514698', 1),
('514865', 50),
('515059', 2),
('515063', 4000),
('515928', 261),
('515929', 0),
('516043', 0),
('516045', 0),
('517463', 0),
('517871', 139),
('518058', 2),
('518059', 0),
('518331', 2),
('518477', 275),
('519008', 0),
('520301', 0),
('520302', 0),
('520303', 0),
('520306', 0),
('521031', 2),
('521363', 0),
('521797', 0),
('521798', 0),
('522020', 0),
('805615', 2),
('521794', 196),
('521795', 196),
('anchors', 262),
('ballast', 6786),
('modules', 1726)
]
expected = [('521794', 196),
('521795', 196),
('514056', 1000),
('modules', 1726),
('513843', 263),
('anchors', 263),
('518477', 275),
('513833', 670),
('513844', 214),
('ballast', 6777),
('515928', 261),
('517871', 139),
('514057', 1000),
('515063', 4000),
('512200', 3480),
('514265', 179),
('513303', 1),
('512660', 2),
('512661', 2),
('512662', 4),
('512663', 2),
('518331', 2),
('518058', 2),
('104813', 50),
('107551', 50),
('514865', 50),
('106925', 50),
('523921', 1),
('514438', 2),
('514437', 2),
('512910', 1),
('805615', 2),
('521031', 2),
('512575', 1),
('514698', 1),
('513007', 50),
('114961', 50),
('107549', 100),
('107586', 100),
('512021', 9),
('512199', 140),
('512511', 196),
('512510', 196),
('515929', 0),
('514477', 2),
('515059', 2),
('514697', 1),
('513831', 0),
('513836', 0),
('520301', 0),
('520302', 0),
('520303', 0),
('520306', 0),
('513832', 0),
('514435', 0),
('514436', 0),
('514439', 0),
('514440', 0),
('523922', 0),
('523923', 0),
('523924', 0),
('513299', 0),
('513301', 0),
('514478', 0),
('513300', 0),
('513302', 0),
('513304', 0),
('519008', 0),
('518059', 0),
('105317', 0),
('111147', 0),
('107538', 0),
('516045', 0),
('516043', 0),
('513586', 0),
('507985', 0),
('522020', 0),
('521798', 0),
('521797', 0),
('521363', 0),
('517463', 0)]
print("===")
print(self.subject.documentation_bom())
assert_array_equal(sorted(self.subject.documentation_bom()), sorted(expected))
# Performance Tests

View File

@@ -118,7 +118,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2907,
cable_support_lid: 368,
cable_support: 368,
rear_skirt: -140,
rear_skirt_1_1: -140,
dc_switch_bracket: 2,
front_legs: 23,
back_legs: 23,
@@ -193,10 +193,10 @@ class EbomCalculatorTest(unittest.TestCase):
self.user_values.module_type.return_value = ModuleType.Cell96
expected_output = {
stump: 65,
wire_clip_large: 513,
wire_clip_large: 590,
cable_support_lid: 0,
cable_support: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
rubber_foot: 9,
delta_inverter_leg: 9,
harness_4_string_mf: 4,
@@ -228,10 +228,10 @@ class EbomCalculatorTest(unittest.TestCase):
self.user_values.module_type.return_value = ModuleType.Cell96
expected_output = {
stump: 0,
wire_clip_large: 171,
wire_clip_large: 200,
cable_support_lid: 0,
cable_support: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
rubber_foot: 3,
delta_inverter_leg: 3,
harness_4_string_mf: 2,
@@ -380,7 +380,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 270,
cable_support_lid: 270,
rear_skirt: -103,
rear_skirt_1_1: -103,
ethernet_plug: 7.2,
monitor_power_plug: 1,
sunshade_bolt: 12,
@@ -492,7 +492,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 270,
cable_support_lid: 270,
rear_skirt: -103,
rear_skirt_1_1: -103,
ethernet_plug: 7.2,
monitor_power_plug: 1,
sunshade_bolt: 12,
@@ -604,7 +604,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 270,
cable_support_lid: 270,
rear_skirt: -103,
rear_skirt_1_1: -103,
ethernet_plug: 7.2,
monitor_power_plug: 1,
fuseshade: 14,
@@ -624,13 +624,13 @@ class EbomCalculatorTest(unittest.TestCase):
}]
self.user_values.system_type.return_value = SystemType.singleTilt
self.user_values.module_type.return_value = ModuleType.Cell96
expected_output = {
monitor_power_plug: 1,
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_controller_480_v:1,
}
@@ -644,13 +644,13 @@ class EbomCalculatorTest(unittest.TestCase):
}]
self.user_values.system_type.return_value = SystemType.singleTilt
self.user_values.module_type.return_value = ModuleType.Cell96
expected_output = {
monitor_power_plug: 1,
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_controller_480_v:1,
flat_washer: 4,
channel_nut: 4,
@@ -771,7 +771,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 694,
cable_support_lid: 694,
rear_skirt: -264,
rear_skirt_1_1: -264,
ethernet_plug: 7.2,
monitor_power_plug: 1,
fuseshade: 14,
@@ -922,7 +922,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 270,
cable_support_lid: 270,
rear_skirt: -103,
rear_skirt_1_1: -103,
ethernet_plug: 7.2,
monitor_power_plug: 1,
sunshade_bolt: 12,
@@ -968,7 +968,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2394,
cable_support: 270,
cable_support_lid: 270,
rear_skirt: -103,
rear_skirt_1_1: -103,
ethernet_plug: 7.2,
sunshade_bolt: 12,
sunshade_washer: 12,
@@ -1027,7 +1027,7 @@ class EbomCalculatorTest(unittest.TestCase):
mounting_back_plate: 1,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_power_plug: 1,
}
@@ -1046,7 +1046,7 @@ class EbomCalculatorTest(unittest.TestCase):
mounting_back_plate: 1,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_power_plug: 1,
}
@@ -1055,14 +1055,14 @@ class EbomCalculatorTest(unittest.TestCase):
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0
rear_skirt_1_1: 0
}
expected_output_240_pseries = {
stump: 0,
cable_support_lid: 0,
cable_support: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_controller_240_v: 1,
}
@@ -1116,14 +1116,14 @@ class EbomCalculatorTest(unittest.TestCase):
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_controller_480_v: 1,
ethernet_plug: 2,
}
assert_dictionary_equal(self.subject.compute_ebom(), expected_output)
def test_computes_ebom_with_power_monitor_SMA_brand(self):
def test_computes_ebom_with_power_monitor_SMA_brand(self):
self.user_values.inverter_brands.return_value = [{'inverter_brand_id':1}]
self.user_values.power_monitors.return_value = [{
'monitor_id': 'foo',
@@ -1131,13 +1131,13 @@ class EbomCalculatorTest(unittest.TestCase):
}]
self.user_values.system_type.return_value = SystemType.singleTilt
self.user_values.module_type.return_value = ModuleType.Cell96
expected_output = {
monitor_power_plug: 1,
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt: 0,
rear_skirt_1_1: 0,
monitor_controller_480_v:1,
flat_washer: 4,
channel_nut: 4,
@@ -1152,7 +1152,3 @@ class EbomCalculatorTest(unittest.TestCase):
}
assert_dictionary_equal(self.subject.compute_ebom(), expected_output)

View File

@@ -99,7 +99,7 @@ class MechanicalBomCalculatorWhenSingleTilt96CellTest(unittest.TestCase):
cross_tray: 5,
rubber_foot: 4.4,
front_skirt: 24,
rear_skirt: 44,
rear_skirt_1_1: 44,
leading_tray: 25,
ballast: 192,
anchor: 15, # 12 + the 3 seismic anchors

View File

@@ -54,8 +54,8 @@ class SummaryValuesTest(unittest.TestCase):
{'label': 'Avg PSF', 'value': 10.06},
{'label': 'Total Anchors', 'value': 7},
{'label': 'Total Ballast', 'value': 8},
{'label': 'Max Possible System Weight', 'value': 1568.0},
{'label': 'Max System Weight Ballast Block', 'value': 14},
{'label': 'Max Possible System Weight', 'value': 1563.0},
{'label': 'Max System Weight Ballast Block', 'value': 16},
{'label': 'Seismic Anchor Max. Spacing', 'value': seismic_interval}
]
eq_(result, expected)
@@ -83,8 +83,8 @@ class SummaryValuesTest(unittest.TestCase):
'ave_psf': 10.06,
'total_anchors': 7,
'total_ballast': 8,
'max_possible_system_weight': 1568.0,
'max_system_weight_ballast_block': 14,
'max_possible_system_weight': 1563.0,
'max_system_weight_ballast_block': 16,
'seismic_anchor_max_spacing': 10
}
@@ -111,7 +111,7 @@ class SummaryValuesTest(unittest.TestCase):
result = self.subject.find_max_system_weight(panels, self.c_p_matrix, self.q_z, self.ballast_calculator)
assert_almost_equal(result, (1568, 14), decimal=0)
assert_almost_equal(result, (1563, 16), decimal=0)
def test_find_max_system_weight_does_not_modify_panels_list(self):
panels = [

View File

@@ -1,8 +1,8 @@
HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
'21EEE0 *U593 C 1 1 12.9 20 2 1 - 1 0 0 0
'21EECC *U824 C 2 1 11.35 17 2 1 - 2 0 0 0
'21EEB8 *U824 E 3 1 2.26 0 1 - - 3 0 0 0
'21EEE0 *U593 C 1 1 12.91 20 2 1 - 1 0 0 0
'21EECC *U824 C 2 1 11.34 17 2 1 - 2 0 0 0
'21EEB8 *U824 E 3 1 2.28 0 1 - - 3 0 0 0
'21EEA4 *U593 A 1 1 5.55 6 2 - 2 4 0 0 0
'21E940 *U171 B 4 1 13.83 22 1 2 - 5 0 0 0
'21E940 *U171 B 4 1 13.87 22 1 2 - 5 0 0 0
'21E92C *U824 D 1 1 7.1 9 2 - - 6 0 0 0
'21E918 *U593 B 1 1 9.8 14 2 1 1 7 0 0 0
1 HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
2 '21EEE0 *U593 C 1 1 12.9 12.91 20 2 1 - 1 0 0 0
3 '21EECC *U824 C 2 1 11.35 11.34 17 2 1 - 2 0 0 0
4 '21EEB8 *U824 E 3 1 2.26 2.28 0 1 - - 3 0 0 0
5 '21EEA4 *U593 A 1 1 5.55 6 2 - 2 4 0 0 0
6 '21E940 *U171 B 4 1 13.83 13.87 22 1 2 - 5 0 0 0
7 '21E92C *U824 D 1 1 7.1 9 2 - - 6 0 0 0
8 '21E918 *U593 B 1 1 9.8 14 2 1 1 7 0 0 0

View File

@@ -1,7 +1,7 @@
Part # Description Total
512200 CLIP, WIRE FORMED, CABLE MANAGEMENT, INSIDE, 352MM ^ 2 3480
513833 TRAY, LINK, HELIX ROOF 670
513843 PLATE, ANCHOR, HELIX ROOF 262
513843 PLATE, ANCHOR, HELIX ROOF 263
513844 TRAY, OPTIONAL BALLAST, HELIX ROOF 214
514056 BASE, CHASSIS, DUAL TILT, HELIX ROOF 1000
514057 PLATFORM, CHASSIS, DUAL TILT, HELIX ROOF 1000
@@ -12,6 +12,6 @@ Part # Description Total
518477 WASHER, FLAT, 3/8, 1.00 OD, 18-8 SS 275
521794 DEFLECTOR, LH, HELIX ROOF V1.1 196
521795 DEFLECTOR, RH, HELIX ROOF V1.1 196
Contractor Supplied Ballast Blocks 6786
TBD Anchors 262
Contractor Supplied Ballast Blocks 6777
TBD Anchors 263
TBD Modules 1726
1 Part # Description Total
2 512200 CLIP, WIRE FORMED, CABLE MANAGEMENT, INSIDE, 352MM ^ 2 3480
3 513833 TRAY, LINK, HELIX ROOF 670
4 513843 PLATE, ANCHOR, HELIX ROOF 262 263
5 513844 TRAY, OPTIONAL BALLAST, HELIX ROOF 214
6 514056 BASE, CHASSIS, DUAL TILT, HELIX ROOF 1000
7 514057 PLATFORM, CHASSIS, DUAL TILT, HELIX ROOF 1000
12 518477 WASHER, FLAT, 3/8, 1.00 OD, 18-8 SS 275
13 521794 DEFLECTOR, LH, HELIX ROOF V1.1 196
14 521795 DEFLECTOR, RH, HELIX ROOF V1.1 196
15 Contractor Supplied Ballast Blocks 6786 6777
16 TBD Anchors 262 263
17 TBD Modules 1726

View File

@@ -14,7 +14,7 @@
513299 COMBINER BOX, AC, 4 INPUT, NO AUX, W/ CONNECTOR 3
513303 COMBINER BOX, AC, 2 INPUT, NO AUX, W/ CONNECTOR 1
513833 TRAY, LINK, HELIX ROOF 670
513843 PLATE, ANCHOR, HELIX ROOF 262
513843 PLATE, ANCHOR, HELIX ROOF 263
513844 TRAY, OPTIONAL BALLAST, HELIX ROOF 214
514056 BASE, CHASSIS, DUAL TILT, HELIX ROOF 1000
514057 PLATFORM, CHASSIS, DUAL TILT, HELIX ROOF 1000
@@ -42,6 +42,6 @@
523922 INVERTER, SMA, STP, 24000TL-US-10 (SPR-24000m-3-H), AFCI, CONNECTORIZED, UTX XL REV D DC CONNECTORS 1
523923 INVERTER, SMA, STP, 12000TL-US-10 (SPR-12000m-3-H), AFCI, CONNECTORIZED, UTX XL REV D DC CONNECTORS 3
523924 INVERTER, SMA, STP, 15000TL-US-10 (SPR-15000m-3-H), AFCI, CONNECTORIZED, UTX XL REV D DC CONNECTORS 9
Contractor Supplied Ballast Blocks 6786
TBD Anchors 262
Contractor Supplied Ballast Blocks 6777
TBD Anchors 263
TBD Modules 1726
Can't render this file because it contains an unexpected character in line 28 and column 30.

View File

@@ -1,20 +1,20 @@
HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
'40EC39 *U5399 H 1 8 14.37 16 - 1 - 1 0 0 0
'40EC22 *U5402 H 3 8 12.89 14 2 1 - 2 0 0 0
'40EC0B *U5402 H 1 8 14.37 16 - 1 - 3 0 0 0
'40EBF4 *U5402 H 1 8 14.37 16 - 1 - 4 0 0 0
'40EBDD *U5402 H 3 8 12.89 14 2 1 - 5 0 0 0
'40EBC6 *U5402 H 3 8 12.89 14 2 1 - 6 0 0 0
'40EBAF *U5402 H 3 8 12.89 14 2 1 - 7 0 0 0
'40EB98 *U5399 H 1 8 14.37 16 - 1 - 8 0 0 0
'40EB81 *U5401 H 2 8 12.65 14 - 1 - 9 0 0 0
'40E8B8 *U5401 H 2 8 12.65 14 - 1 - 10 0 0 0
'40E8A1 *U5401 I 2 8 1.89 0 - - 2 11 0 0 0
'40E88A *U5400 H 4 8 10.42 11 1 1 - 12 0 0 0
'40E873 *U5400 H 4 8 10.42 11 1 1 - 13 0 0 0
'40E85C *U5400 H 4 8 10.42 11 1 1 - 14 0 0 0
'40E845 *U5400 H 4 8 10.42 11 1 1 - 15 0 0 0
'40E82E *U5400 H 4 8 10.42 11 1 1 - 16 0 0 0
'40E817 *U5400 H 4 8 10.42 11 1 1 - 17 0 0 0
'40E800 *U5401 H 2 8 12.65 14 - 1 - 18 0 0 0
'40E7E9 *U5401 I 2 8 1.89 0 - - 2 19 0 0 0
'40EC39 *U5399 H 1 8 14.24 16 - 1 - 1 0 0 0
'40EC22 *U5402 H 3 8 12.72 14 2 1 - 2 0 0 0
'40EC0B *U5402 H 1 8 14.24 16 - 1 - 3 0 0 0
'40EBF4 *U5402 H 1 8 14.24 16 - 1 - 4 0 0 0
'40EBDD *U5402 H 3 8 12.72 14 2 1 - 5 0 0 0
'40EBC6 *U5402 H 3 8 12.72 14 2 1 - 6 0 0 0
'40EBAF *U5402 H 3 8 12.72 14 2 1 - 7 0 0 0
'40EB98 *U5399 H 1 8 14.24 16 - 1 - 8 0 0 0
'40EB81 *U5401 H 2 8 12.6 14 - 1 - 9 0 0 0
'40E8B8 *U5401 H 2 8 12.6 14 - 1 - 10 0 0 0
'40E8A1 *U5401 I 2 8 1.84 0 - - 2 11 0 0 0
'40E88A *U5400 H 4 8 10.4 11 1 1 - 12 0 0 0
'40E873 *U5400 H 4 8 10.4 11 1 1 - 13 0 0 0
'40E85C *U5400 H 4 8 10.4 11 1 1 - 14 0 0 0
'40E845 *U5400 H 4 8 10.4 11 1 1 - 15 0 0 0
'40E82E *U5400 H 4 8 10.4 11 1 1 - 16 0 0 0
'40E817 *U5400 H 4 8 10.4 11 1 1 - 17 0 0 0
'40E800 *U5401 H 2 8 12.6 14 - 1 - 18 0 0 0
'40E7E9 *U5401 I 2 8 1.84 0 - - 2 19 0 0 0
1 HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
2 '40EC39 *U5399 H 1 8 14.37 14.24 16 - 1 - 1 0 0 0
3 '40EC22 *U5402 H 3 8 12.89 12.72 14 2 1 - 2 0 0 0
4 '40EC0B *U5402 H 1 8 14.37 14.24 16 - 1 - 3 0 0 0
5 '40EBF4 *U5402 H 1 8 14.37 14.24 16 - 1 - 4 0 0 0
6 '40EBDD *U5402 H 3 8 12.89 12.72 14 2 1 - 5 0 0 0
7 '40EBC6 *U5402 H 3 8 12.89 12.72 14 2 1 - 6 0 0 0
8 '40EBAF *U5402 H 3 8 12.89 12.72 14 2 1 - 7 0 0 0
9 '40EB98 *U5399 H 1 8 14.37 14.24 16 - 1 - 8 0 0 0
10 '40EB81 *U5401 H 2 8 12.65 12.6 14 - 1 - 9 0 0 0
11 '40E8B8 *U5401 H 2 8 12.65 12.6 14 - 1 - 10 0 0 0
12 '40E8A1 *U5401 I 2 8 1.89 1.84 0 - - 2 11 0 0 0
13 '40E88A *U5400 H 4 8 10.42 10.4 11 1 1 - 12 0 0 0
14 '40E873 *U5400 H 4 8 10.42 10.4 11 1 1 - 13 0 0 0
15 '40E85C *U5400 H 4 8 10.42 10.4 11 1 1 - 14 0 0 0
16 '40E845 *U5400 H 4 8 10.42 10.4 11 1 1 - 15 0 0 0
17 '40E82E *U5400 H 4 8 10.42 10.4 11 1 1 - 16 0 0 0
18 '40E817 *U5400 H 4 8 10.42 10.4 11 1 1 - 17 0 0 0
19 '40E800 *U5401 H 2 8 12.65 12.6 14 - 1 - 18 0 0 0
20 '40E7E9 *U5401 I 2 8 1.89 1.84 0 - - 2 19 0 0 0

View File

@@ -27,7 +27,7 @@ Part # Description Total
513832 TRAY, FOLLOWING, HELIX ROOF 210
513833 TRAY, LINK, HELIX ROOF 1271
513836 SPOILER, SINGLE TILT, HELIX ROOF 1632
513843 PLATE, ANCHOR, HELIX ROOF 414
513843 PLATE, ANCHOR, HELIX ROOF 416
513844 TRAY, OPTIONAL BALLAST, HELIX ROOF 274
514265 FOOT, RECYCLED RUBBER, HELIX ROOF 230
514435 HARNESS, DC COMBINATION, W/ FUSE, 3 STRING, FEMALES TO MALE, HELIX 4
@@ -43,13 +43,13 @@ Part # Description Total
514865 BOLT, HH, 3/8-16 X 1/2, 18-8 SS 350
515059 ASSY, WHIP TRAY W/FUSE CLIPS, INVERTER, HELIX 16
515928 FRONT SKIRT, HELIX ROOF 197
515929 REAR SKIRT, HELIX ROOF 1469
516043 AC SWITCH, CONNECTORIZED, HELIX ROOF 2
516045 AC SPLICE BOX, CONNECTORIZED, HELIX ROOF 1
517871 TRAY, LEADING, HELIX ROOF, RIVETED VERSION 210
518058 CONNECTOR, ETHERNET, PLUG, RJ-45, WEATHERPROOF, SHIELDED 15
518331 MOUNTING BACK PLATE, INVERTER/PANEL BOARD, HELIX ROOF/TRACKER 22
518477 WASHER, FLAT, 3/8, 1.00 OD, 18-8 SS 425
520301 REAR SKIRT, HELIX ROOF V1.1 1469
521031 WASHER, FLAT, M10 X 20MM OD, SS 10
521794 DEFLECTOR, LH, HELIX ROOF V1.1 171
521795 DEFLECTOR, RH, HELIX ROOF V1.1 171
@@ -57,6 +57,6 @@ Part # Description Total
523922 INVERTER, SMA, STP, 24000TL-US-10 (SPR-24000m-3-H), AFCI, CONNECTORIZED, UTX XL REV D DC CONNECTORS 3
523924 INVERTER, SMA, STP, 15000TL-US-10 (SPR-15000m-3-H), AFCI, CONNECTORIZED, UTX XL REV D DC CONNECTORS 6
805615 SCREW, HEXAGONAL HEAD, M10X20, SS A2 10
Contractor Supplied Ballast Blocks 10150
TBD Anchors 414
Contractor Supplied Ballast Blocks 10448
TBD Anchors 416
TBD Modules 1632
Can't render this file because it contains an unexpected character in line 10 and column 30.

View File

@@ -1,3 +1,3 @@
HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
'40EC39 *U5399 A 1 1 2.4 0 2 - 1 1 0 0 0
'40EC22 *U5402 A 1 1 2.4 0 2 - 1 2 0 0 0
'40EC39 *U5399 A 1 1 2.39 0 2 - 1 1 0 0 0
'40EC22 *U5402 A 1 1 2.39 0 2 - 1 2 0 0 0

1073
test/fixtures/irvine_fortune_result.txt vendored Normal file

File diff suppressed because it is too large Load Diff

169
test/fixtures/wind data_hdt.txt vendored Normal file
View File

@@ -0,0 +1,169 @@
HANDLE BLOCKNAME WIND POS SUBARRAY PSF BAL LTRAY XTRAY ANC ID XCOORD YCOORD ANGLE
'1A66A5 *U5350 B 1 3 2.76 1 2 - 1 1 13906.282 -20190.065 4.124
'1A667F *U5350 B 1 3 2.76 1 2 - 1 2 13818.27 -20196.412 4.124
'1A6633 *U5350 B 1 2 2.76 1 2 - 1 3 13647.543 -20208.723 4.124
'1A6529 *U5425 B 3 3 2.26 0 1 - 1 4 13910.752 -20252.064 4.124
'1A6503 *U5425 B 3 3 2.26 0 1 - 1 5 13822.741 -20258.411 4.124
'1A64B7 *U5425 B 3 2 2.26 0 1 - 1 6 13652.014 -20270.722 4.124
'1A63AD *U5425 B 3 3 2.26 0 1 - 1 7 13915.223 -20314.063 4.124
'1A6387 *U5425 B 3 3 2.26 0 1 - 1 8 13827.212 -20320.41 4.124
'1A633B *U5425 B 3 2 2.26 0 1 - 1 9 13656.484 -20332.721 4.124
'1A6231 *U5425 C 3 3 6.36 11 1 1 - 10 13919.694 -20376.062 4.124
'1A620B *U5425 C 3 3 6.36 11 1 1 - 11 13831.682 -20382.409 4.124
'1A61BF *U5425 C 3 2 6.36 11 1 1 - 12 13660.955 -20394.72 4.124
'1A60B5 *U5425 C 3 3 6.36 11 1 1 - 13 13924.164 -20438.061 4.124
'1A608F *U5425 C 3 3 6.36 11 1 1 - 14 13836.153 -20444.408 4.124
'1A6043 *U5425 C 3 2 6.36 11 1 1 - 15 13665.426 -20456.719 4.124
'1A5F39 *U5425 C 3 3 6.36 11 1 1 - 16 13928.635 -20500.06 4.124
'1A5F13 *U5425 C 3 3 6.36 11 1 1 - 17 13840.624 -20506.407 4.124
'1A5EC7 *U5425 C 3 2 6.36 11 1 1 - 18 13669.896 -20518.718 4.124
'1A5DBD *U5425 C 3 3 6.36 11 1 1 - 19 13933.106 -20562.059 4.124
'1A5D97 *U5425 C 3 3 6.36 11 1 1 - 20 13845.094 -20568.406 4.124
'1A5D4B *U5425 C 3 2 6.36 11 1 1 - 21 13674.367 -20580.717 4.124
'1A5C41 *U5425 C 3 3 6.36 11 1 1 - 22 13937.577 -20624.058 4.124
'1A5C1B *U5425 C 3 3 6.36 11 1 1 - 23 13849.565 -20630.405 4.124
'1A5BCF *U5425 C 3 2 6.36 11 1 1 - 24 13678.838 -20642.716 4.124
'1A5AC5 *U5425 C 3 3 6.36 11 1 1 - 25 13942.047 -20686.057 4.124
'1A5A9F *U5425 C 3 3 6.36 11 1 1 - 26 13854.036 -20692.404 4.124
'1A5A53 *U5425 C 3 2 6.36 11 1 1 - 27 13683.309 -20704.715 4.124
'1A5949 *U5425 C 3 3 6.36 11 1 1 - 28 13946.518 -20748.056 4.124
'1A5923 *U5425 C 3 3 6.36 11 1 1 - 29 13858.506 -20754.403 4.124
'1A58D7 *U5425 C 3 2 6.36 11 1 1 - 30 13687.779 -20766.714 4.124
'1A57CD *U5425 C 3 3 6.36 11 1 1 - 31 13950.989 -20810.056 4.124
'1A57A7 *U5425 C 3 3 6.36 11 1 1 - 32 13862.977 -20816.402 4.124
'1A575B *U5425 C 3 2 6.36 11 1 1 - 33 13692.25 -20828.713 4.124
'1A5651 *U5425 C 3 3 6.36 11 1 1 - 34 13955.459 -20872.055 4.124
'1A562B *U5425 C 3 3 6.36 11 1 1 - 35 13867.448 -20878.401 4.124
'1A55DF *U5425 C 3 2 6.36 11 1 1 - 36 13696.721 -20890.712 4.124
'1A54D5 *U5425 C 3 3 6.36 11 1 1 - 37 13959.93 -20934.054 4.124
'1A54AF *U5425 C 3 3 6.36 11 1 1 - 38 13871.918 -20940.4 4.124
'1A5463 *U5425 C 3 2 6.36 11 1 1 - 39 13701.191 -20952.711 4.124
'1A5359 *U5350 C 1 3 8.35 16 2 1 - 40 13964.401 -20996.053 4.124
'1A5333 *U5350 C 1 3 8.35 16 2 1 - 41 13876.389 -21002.399 4.124
'1A52E7 *U5350 C 1 2 8.35 16 2 1 - 42 13705.662 -21014.71 4.124
'1A52C1 *U5424 B 2 2 2.4 0 2 - 1 43 13559.532 -20215.069 4.124
'1A529B *U5424 B 2 2 2.4 0 2 - 1 44 13471.52 -20221.416 4.124
'1A5275 *U5424 B 2 2 2.4 0 2 - 1 45 13383.509 -20227.762 4.124
'1A524F *U5424 B 2 2 2.4 0 2 - 1 46 13295.497 -20234.108 4.124
'1A5229 *U5350 B 1 2 2.76 1 2 - 1 47 13207.486 -20240.455 4.124
'1A51DD *U5350 B 1 1 2.76 1 2 - 1 48 13031.463 -20253.148 4.124
'1A51B7 *U5424 B 2 1 2.4 0 2 - 1 49 12943.451 -20259.494 4.124
'1A5191 *U5424 B 2 1 2.4 0 2 - 1 50 12855.44 -20265.84 4.124
'1A516B *U5350 B 1 1 2.76 1 2 - 1 51 12767.428 -20272.187 4.124
'1A5145 *U5349 B 4 2 5.58 9 2 - - 52 13564.002 -20277.068 4.124
'1A511F *U5349 B 4 2 5.58 9 2 - - 53 13475.991 -20283.415 4.124
'1A50F9 *U5349 B 4 2 5.58 9 2 - - 54 13387.979 -20289.761 4.124
'1A50D3 *U5349 B 4 2 5.58 9 2 - - 55 13299.968 -20296.107 4.124
'1A50AD *U5425 B 3 2 2.26 0 1 - 1 56 13211.956 -20302.454 4.124
'1A5061 *U5425 B 3 1 2.26 0 1 - 1 57 13035.933 -20315.147 4.124
'1A503B *U5349 B 4 1 5.58 9 2 - - 58 12947.922 -20321.493 4.124
'1A5015 *U5349 B 4 1 5.58 9 2 - - 59 12859.91 -20327.839 4.124
'1A4FEF *U5425 B 3 1 2.26 0 1 - 1 60 12771.899 -20334.186 4.124
'1A4FC9 *U5349 B 4 2 5.58 9 2 - - 61 13568.473 -20339.067 4.124
'1A4FA3 *U5349 B 4 2 5.58 9 2 - - 62 13480.461 -20345.414 4.124
'1A4F7D *U5349 C 4 2 3.71 4 - - - 63 13392.45 -20351.76 4.124
'1A4F57 *U5349 C 4 2 3.71 4 - - - 64 13304.439 -20358.106 4.124
'1A4F31 *U5425 C 3 2 6.36 11 1 1 - 65 13216.427 -20364.453 4.124
'1A4EE5 *U5425 B 3 1 2.26 0 1 - 1 66 13040.404 -20377.146 4.124
'1A4EBF *U5349 B 4 1 5.58 9 2 - - 67 12952.393 -20383.492 4.124
'1A4E99 *U5349 B 4 1 5.58 9 2 - - 68 12864.381 -20389.838 4.124
'1A4E73 *U5425 B 3 1 2.26 0 1 - 1 69 12776.37 -20396.185 4.124
'1A4E4D *U5349 C 4 2 3.71 4 - - - 70 13572.944 -20401.066 4.124
'1A4E27 *U5349 C 4 2 3.71 4 - - - 71 13484.932 -20407.413 4.124
'1A4E01 *U5349 C 4 2 3.71 4 - - - 72 13396.921 -20413.759 4.124
'1A4DDB *U5349 C 4 2 3.71 4 - - - 73 13308.909 -20420.105 4.124
'1A4DB5 *U5425 C 3 2 6.36 11 1 1 - 74 13220.898 -20426.452 4.124
'1A4D69 *U5425 B 3 1 2.26 0 1 - 1 75 13044.875 -20439.145 4.124
'1A4D43 *U5349 B 4 1 5.58 9 2 - - 76 12956.863 -20445.491 4.124
'1A4D1D *U5349 B 4 1 5.58 9 2 - - 77 12868.852 -20451.837 4.124
'1A4CF7 *U5425 B 3 1 2.26 0 1 - 1 78 12780.84 -20458.184 4.124
'1A4CD1 *U5349 C 4 2 3.71 4 - - - 79 13577.414 -20463.065 4.124
'1A4CAB *U5349 C 4 2 3.71 4 - - - 80 13489.403 -20469.412 4.124
'1A4C85 *U5349 C 4 2 3.71 4 - - - 81 13401.391 -20475.758 4.124
'1A4C5F *U5349 C 4 2 3.71 4 - - - 82 13313.38 -20482.104 4.124
'1A4C39 *U5425 C 3 2 6.36 11 1 1 - 83 13225.368 -20488.451 4.124
'1A4BED *U5425 B 3 1 2.26 0 1 - 1 84 13049.345 -20501.144 4.124
'1A4BC7 *U5349 B 4 1 5.58 9 2 - - 85 12961.334 -20507.49 4.124
'1A4BA1 *U5349 B 4 1 5.58 9 2 - - 86 12873.322 -20513.836 4.124
'1A4B7B *U5425 B 3 1 2.26 0 1 - 1 87 12785.311 -20520.183 4.124
'1A4B55 *U5349 C 4 2 3.71 4 - - - 88 13581.885 -20525.064 4.124
'1A4B2F *U5349 C 4 2 3.71 4 - - - 89 13493.874 -20531.411 4.124
'1A4B09 *U5349 C 4 2 3.71 4 - - - 90 13405.862 -20537.757 4.124
'1A4AE3 *U5349 C 4 2 3.71 4 - - - 91 13317.851 -20544.103 4.124
'1A4ABD *U5425 C 3 2 6.36 11 1 1 - 92 13229.839 -20550.45 4.124
'1A4A71 *U5425 B 3 1 2.26 0 1 - 1 93 13053.816 -20563.143 4.124
'1A4A4B *U5349 B 4 1 5.58 9 2 - - 94 12965.805 -20569.489 4.124
'1A4A25 *U5349 B 4 1 5.58 9 2 - - 95 12877.793 -20575.836 4.124
'1A49FF *U5425 B 3 1 2.26 0 1 - 1 96 12789.782 -20582.182 4.124
'1A49D9 *U5349 B 4 2 5.58 9 2 - - 97 13586.356 -20587.063 4.124
'1A49B3 *U5349 B 4 2 5.58 9 2 - - 98 13498.344 -20593.41 4.124
'1A498D *U5349 B 4 2 5.58 9 2 - - 99 13410.333 -20599.756 4.124
'1A4967 *U5349 B 4 2 5.58 9 2 - - 100 13322.321 -20606.102 4.124
'1A4941 *U5425 B 3 2 2.26 0 1 - 1 101 13234.31 -20612.449 4.124
'1A48F5 *U5425 B 3 1 2.26 0 1 - 1 102 13058.287 -20625.142 4.124
'1A48CF *U5349 B 4 1 5.58 9 2 - - 103 12970.275 -20631.488 4.124
'1A48A9 *U5349 B 4 1 5.58 9 2 - - 104 12882.264 -20637.835 4.124
'1A4883 *U5425 B 3 1 2.26 0 1 - 1 105 12794.252 -20644.181 4.124
'1A485D *U5349 B 4 2 5.58 9 2 - - 106 13590.826 -20649.062 4.124
'1A4837 *U5349 B 4 2 5.58 9 2 - - 107 13502.815 -20655.409 4.124
'1A4811 *U5349 B 4 2 5.58 9 2 - - 108 13414.803 -20661.755 4.124
'1A47EB *U5349 B 4 2 5.58 9 2 - - 109 13326.792 -20668.101 4.124
'1A47C5 *U5425 B 3 2 2.26 0 1 - 1 110 13238.78 -20674.448 4.124
'1A4779 *U5425 B 3 1 2.26 0 1 - 1 111 13062.757 -20687.141 4.124
'1A4753 *U5349 B 4 1 5.58 9 2 - - 112 12974.746 -20693.487 4.124
'1A472D *U5349 B 4 1 5.58 9 2 - - 113 12886.735 -20699.834 4.124
'1A4707 *U5425 B 3 1 2.26 0 1 - 1 114 12798.723 -20706.18 4.124
'1A46E1 *U5349 B 4 2 5.58 9 2 - - 115 13595.297 -20711.061 4.124
'1A46BB *U5349 B 4 2 5.58 9 2 - - 116 13507.286 -20717.408 4.124
'1A4695 *U5349 B 4 2 5.58 9 2 - - 117 13419.274 -20723.754 4.124
'1A466F *U5349 B 4 2 5.58 9 2 - - 118 13331.263 -20730.101 4.124
'1A4649 *U5425 B 3 2 2.26 0 1 - 1 119 13243.251 -20736.447 4.124
'1A45FD *U5425 B 3 1 2.26 0 1 - 1 120 13067.228 -20749.14 4.124
'1A45D7 *U5349 B 4 1 5.58 9 2 - - 121 12979.217 -20755.486 4.124
'1A45B1 *U5349 B 4 1 5.58 9 2 - - 122 12891.205 -20761.833 4.124
'1A458B *U5425 B 3 1 2.26 0 1 - 1 123 12803.194 -20768.179 4.124
'1A4565 *U5349 B 4 2 5.58 9 2 - - 124 13599.768 -20773.06 4.124
'1A453F *U5349 B 4 2 5.58 9 2 - - 125 13511.756 -20779.407 4.124
'1A4519 *U5349 B 4 2 5.58 9 2 - - 126 13423.745 -20785.753 4.124
'1A44F3 *U5349 B 4 2 5.58 9 2 - - 127 13335.733 -20792.1 4.124
'1A44CD *U5425 B 3 2 2.26 0 1 - 1 128 13247.722 -20798.446 4.124
'1A4481 *U5425 B 3 1 2.26 0 1 - 1 129 13071.699 -20811.139 4.124
'1A445B *U5349 B 4 1 5.58 9 2 - - 130 12983.687 -20817.485 4.124
'1A4435 *U5349 B 4 1 5.58 9 2 - - 131 12895.676 -20823.832 4.124
'1A440F *U5425 B 3 1 2.26 0 1 - 1 132 12807.664 -20830.178 4.124
'1A43E9 *U5349 B 4 2 5.58 9 2 - - 133 13604.238 -20835.059 4.124
'1A43C3 *U5349 B 4 2 5.58 9 2 - - 134 13516.227 -20841.406 4.124
'1A439D *U5349 B 4 2 5.58 9 2 - - 135 13428.215 -20847.752 4.124
'1A4377 *U5349 B 4 2 5.58 9 2 - - 136 13340.204 -20854.099 4.124
'1A4351 *U5425 B 3 2 2.26 0 1 - 1 137 13252.192 -20860.445 4.124
'1A4305 *U5425 B 3 1 2.26 0 1 - 1 138 13076.17 -20873.138 4.124
'1A42DF *U5349 B 4 1 5.58 9 2 - - 139 12988.158 -20879.484 4.124
'1A42B9 *U5349 B 4 1 5.58 9 2 - - 140 12900.147 -20885.831 4.124
'1A4293 *U5425 B 3 1 2.26 0 1 - 1 141 12812.135 -20892.177 4.124
'1A426D *U5349 B 4 2 5.58 9 2 - - 142 13608.709 -20897.058 4.124
'1A4247 *U5349 B 4 2 5.58 9 2 - - 143 13520.698 -20903.405 4.124
'1A4221 *U5349 B 4 2 5.58 9 2 - - 144 13432.686 -20909.751 4.124
'1A41FB *U5349 B 4 2 5.58 9 2 - - 145 13344.675 -20916.098 4.124
'1A41D5 *U5425 B 3 2 2.26 0 1 - 1 146 13256.663 -20922.444 4.124
'1A4189 *U5425 B 3 1 2.26 0 1 - 1 147 13080.64 -20935.137 4.124
'1A4163 *U5349 B 4 1 5.58 9 2 - - 148 12992.629 -20941.483 4.124
'1A413D *U5349 B 4 1 5.58 9 2 - - 149 12904.617 -20947.83 4.124
'1A4117 *U5425 B 3 1 2.26 0 1 - 1 150 12816.606 -20954.176 4.124
'1A40F1 *U5349 B 4 2 5.58 9 2 - - 151 13613.18 -20959.057 4.124
'1A40CB *U5349 B 4 2 5.58 9 2 - - 152 13525.168 -20965.404 4.124
'1A40A5 *U5349 B 4 2 5.58 9 2 - - 153 13437.157 -20971.75 4.124
'1A407F *U5349 B 4 2 5.58 9 2 - - 154 13349.145 -20978.097 4.124
'1A4059 *U5425 B 3 2 2.26 0 1 - 1 155 13261.134 -20984.443 4.124
'1A400D *U5425 B 3 1 2.26 0 1 - 1 156 13085.111 -20997.136 4.124
'1A3FE7 *U5349 B 4 1 5.58 9 2 - - 157 12997.099 -21003.482 4.124
'1A3FC1 *U5349 B 4 1 5.58 9 2 - - 158 12909.088 -21009.829 4.124
'1A3F9B *U5425 B 3 1 2.26 0 1 - 1 159 12821.076 -21016.175 4.124
'1A3F75 *U5424 B 2 2 2.4 0 2 - 1 160 13617.65 -21021.056 4.124
'1A3F4F *U5424 B 2 2 2.4 0 2 - 1 161 13529.639 -21027.403 4.124
'1A3F29 *U5424 B 2 2 2.4 0 2 - 1 162 13441.628 -21033.749 4.124
'1A3F03 *U5424 B 2 2 2.4 0 2 - 1 163 13353.616 -21040.096 4.124
'1A3EDD *U5350 B 1 2 2.76 1 2 - 1 164 13265.605 -21046.442 4.124
'1A3E91 *U5350 B 1 1 2.76 1 2 - 1 165 13089.582 -21059.135 4.124
'1A3E6B *U5424 B 2 1 2.4 0 2 - 1 166 13001.57 -21065.481 4.124
'1A3E45 *U5424 B 2 1 2.4 0 2 - 1 167 12913.559 -21071.828 4.124
'1A3E1F *U5350 B 1 1 2.76 1 2 - 1 168 12825.547 -21078.174 4.124

View File

@@ -171,11 +171,11 @@ class ArraySummaryTest(unittest.TestCase):
def test_shows_summary_values(self):
self.fill_in_site_characterization_data()
self.upload_csv_file(file='test/fixtures/input_single_tilt_csv_for_bom.csv')
self.assert_summary_values_column_data(1, 'Total System Weight (lbs)', '216,074')
self.assert_summary_values_column_data(2, 'Max PSF', '9.11')
self.assert_summary_values_column_data(3, 'Avg PSF', '5.05')
self.assert_summary_values_column_data(4, 'Total Anchors', '421')
self.assert_summary_values_column_data(5, 'Total Ballast', '10,150')
self.assert_summary_values_column_data(1, 'Total System Weight (lbs)', '217,409')
self.assert_summary_values_column_data(2, 'Max PSF', '9.36')
self.assert_summary_values_column_data(3, 'Avg PSF', '5.08')
self.assert_summary_values_column_data(4, 'Total Anchors', '422')
self.assert_summary_values_column_data(5, 'Total Ballast', '10,448')
def test_shows_seismic_placement_interval(self):
self.fill_in_site_characterization_data(system_type=SystemType.dualTilt)
@@ -212,26 +212,26 @@ class ArraySummaryTest(unittest.TestCase):
eq_(response.content_type, "application/json")
data = json.loads(response.data.decode())
expected = [
{'data': {'ballast': 7, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 1, 'panel_type': 2, 'psf': 4.94, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 2, 'panel_type': 2, 'psf': 3.12, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 3, 'panel_type': 1, 'psf': 6.47, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 4, 'panel_type': 1, 'psf': 6.47, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 7, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 1, 'panel_type': 2, 'psf': 4.91, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 2, 'panel_type': 2, 'psf': 3.1, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 3, 'panel_type': 1, 'psf': 6.48, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 4, 'panel_type': 1, 'psf': 6.48, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 5, 'panel_type': 1, 'psf': 4.57, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 6, 'panel_type': 2, 'psf': 3.12, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 6, 'panel_type': 2, 'psf': 3.1, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 7, 'panel_type': 4, 'psf': 2.26, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 8, 'panel_type': 3, 'psf': 2.98, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 9, 'panel_type': 3, 'psf': 2.98, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 8, 'panel_type': 3, 'psf': 3.0, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 9, 'panel_type': 3, 'psf': 3.0, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 10, 'panel_type': 4, 'psf': 2.26, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 11, 'panel_type': 2, 'psf': 3.12, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 12, 'panel_type': 3, 'psf': 2.98, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 13, 'panel_type': 3, 'psf': 2.98, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 14, 'panel_type': 3, 'psf': 2.98, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 11, 'panel_type': 2, 'psf': 3.1, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 12, 'panel_type': 3, 'psf': 3.0, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 13, 'panel_type': 3, 'psf': 3.0, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 2, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 14, 'panel_type': 3, 'psf': 3.0, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 15, 'panel_type': 1, 'psf': 4.57, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 16, 'panel_type': 1, 'psf': 4.57, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 17, 'panel_type': 1, 'psf': 4.57, 'seismic_anchors': 0, 'subarray': 7, 'wind_anchors': 0, 'wind_zones': 'B'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 18, 'panel_type': 1, 'psf': 6.47, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 19, 'panel_type': 3, 'psf': 4.48, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 20, 'panel_type': 1, 'psf': 6.47, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5}
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 18, 'panel_type': 1, 'psf': 6.48, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 6, 'cross_trays': 0, 'link_trays': 1, 'panel_id': 19, 'panel_type': 3, 'psf': 4.5, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5},
{'data': {'ballast': 11, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 20, 'panel_type': 1, 'psf': 6.48, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 0, 'wind_zones': 'A'}, 'height': 1, 'width': 1.5}
]
# Removing these keys as they get calculated differently in CircleCI
@@ -290,28 +290,28 @@ class ArraySummaryTest(unittest.TestCase):
"status": "success",
"error": None,
"panel_data": [
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.08, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 1, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 2, 'panel_id': 1}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.04, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 3, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 2, 'seismic_anchors': 0, 'panel_id': 2}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.08, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 1, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 3}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.08, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 1, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 4}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.04, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 3, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 2, 'seismic_anchors': 0, 'panel_id': 5}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.04, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 3, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 2, 'seismic_anchors': 0, 'panel_id': 6}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.04, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 3, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 2, 'seismic_anchors': 0, 'panel_id': 7}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 2.08, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 1, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 8}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.89, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 2, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 9}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.89, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 2, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 10}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.89, 'wind_zones': 'I', 'ballast': 0, 'panel_type': 2, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 11}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 12}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 13}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 14}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 15}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 16}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.86, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 4, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 17}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.89, 'wind_zones': 'H', 'ballast': 0, 'panel_type': 2, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 18}},
{'x': 0, 'y': 0, 'height': 1, 'width': 1, 'data': {'subarray': 8, 'psf': 1.89, 'wind_zones': 'I', 'ballast': 0, 'panel_type': 2, 'cross_trays': 0, 'wind_anchors': 1, 'link_trays': 0, 'seismic_anchors': 0, 'panel_id': 19}},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 1, 'panel_type': 1, 'psf': 1.95, 'seismic_anchors': 2, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 2, 'panel_type': 3, 'psf': 1.88, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 3, 'panel_type': 1, 'psf': 1.95, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 4, 'panel_type': 1, 'psf': 1.95, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 5, 'panel_type': 3, 'psf': 1.88, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 6, 'panel_type': 3, 'psf': 1.88, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 2, 'panel_id': 7, 'panel_type': 3, 'psf': 1.88, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 8, 'panel_type': 1, 'psf': 1.95, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 9, 'panel_type': 2, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 10, 'panel_type': 2, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 11, 'panel_type': 2, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'I'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 12, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 13, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 14, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 15, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 16, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 17, 'panel_type': 4, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 18, 'panel_type': 2, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'H'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
{'data': {'ballast': 0, 'cross_trays': 0, 'link_trays': 0, 'panel_id': 19, 'panel_type': 2, 'psf': 1.84, 'seismic_anchors': 0, 'subarray': 8, 'wind_anchors': 1, 'wind_zones': 'I'}, 'height': 1, 'width': 1, 'x': 0.0, 'y': 0.0},
],
"subarray_data": [
{"subarray": 8, "weight": 972, "required_seismic_anchors": 0},
{"subarray": 8, "weight": 932, "required_seismic_anchors": 0},
],
}
received_result = flask.json.loads(result.data)
@@ -367,9 +367,10 @@ class ArraySummaryTest(unittest.TestCase):
expected_result = [
{"subarray": 7, "weight": 2253, "required_seismic_anchors": 4},
{"subarray": 8, "weight": 673, "required_seismic_anchors": 0},
{"subarray": 8, "weight": 674, "required_seismic_anchors": 0},
]
received_result = flask.json.loads(result.data)
eq_(received_result['subarray_data'], expected_result)
eq_(result.content_type, "application/json")
@@ -410,7 +411,7 @@ class ArraySummaryTest(unittest.TestCase):
"error": SeismicAnchorValidationError.TooFewAnchors.value,
"panel_data": None,
"subarray_data": [
{"subarray": 1, "weight": 2739, "required_seismic_anchors": 6},
{"subarray": 1, "weight": 2722, "required_seismic_anchors": 6},
],
}
eq_(flask.json.loads(result.data), expected_result)

View File

@@ -36,7 +36,7 @@ class BomIntegrationTest(unittest.TestCase):
expected = [
["512200", "CLIP, WIRE FORMED, CABLE MANAGEMENT, INSIDE, 352MM ^ 2", "510"],
["513833", "TRAY, LINK, HELIX ROOF", "78"], # TODO: should be 74 for part perfect bom
["513833", "TRAY, LINK, HELIX ROOF", "78"],
["513843", "PLATE, ANCHOR, HELIX ROOF", "86"],
["513844", "TRAY, OPTIONAL BALLAST, HELIX ROOF", "35"],
["514056", "BASE, CHASSIS, DUAL TILT, HELIX ROOF", "147"],
@@ -48,7 +48,7 @@ class BomIntegrationTest(unittest.TestCase):
["518477", "WASHER, FLAT, 3/8, 1.00 OD, 18-8 SS", "100"],
["521794", "DEFLECTOR, LH, HELIX ROOF V1.1", "30"],
["521795", "DEFLECTOR, RH, HELIX ROOF V1.1", "30"],
["Contractor Supplied", "Ballast Blocks", "608"],
["Contractor Supplied", "Ballast Blocks", "610"],
["TBD", "Anchors", "86"],
["TBD", "Modules", "252"]
]

View File

@@ -149,6 +149,8 @@ class FullUserFlowTest(unittest.TestCase):
self.advance_n_times(1)
self.browser.visit('/download/')
self.browser.click_link_by_partial_text('Download AutoCAD import file')
print("===")
print(self.browser.html)
eq_(self.browser.html, csv_content)
eq_(self.browser._response.headers['Content-Disposition'], 'attachment; filename=test_project_name_result.txt')

35
test/load/locustfile.py Normal file
View File

@@ -0,0 +1,35 @@
from locust import HttpLocust, TaskSet, task
class UserBehavior(TaskSet):
@task(1)
def wizard_sequence(self):
# files => enctype=multipart/form-data
self.client.post('/site_characterization/', files={
'project_name': (None, 'Amazon Fresno'),
'system_type': (None, '0'),
'module_type': (None, '96 Cell'),
'building_height': (None, '30'),
'building_width': (None, '600'),
'building_length': (None, '1500'),
'building_parapet_height': (None, '0'),
'wind_speed': (None, '110'),
'exposure_category': (None, 'C'),
'exposure_category_transition_distance': (None, '0'),
'ballast_block_weight': (None, '14.0'),
'max_system_pressure': (None, '12.0'),
'anchor_type': (None, 'OMG PowerGrip Plus'),
'design_spectral_response': (None, '0'),
'importance_factor': (None, '1'),
})
self.client.get('/array_summary/')
self.client.post('/array_summary/', files={'dxf_upload': open('Fresno First Half.dxf', 'rb')})
class WebsiteUser(HttpLocust):
task_set = UserBehavior
min_wait = 500
max_wait = 5000
# locust -f locustfile.py --host=http://localhost:5000

View File

@@ -103,4 +103,4 @@ class CsvInputValidatorTest(unittest.TestCase):
self.user_values.module_system_constants().panel_spacing = (88.24, 62.0)
with open('test/fixtures/invalid_too_close.txt', 'r', newline='') as csv_file:
cad_input = csv_file.read()
self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelsTooClose , None)
#self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelsTooClose , None)