1 Commits

Author SHA1 Message Date
GotPPay
3eb3463d50 value edit; test fix 2017-11-27 20:27:56 +01:00
62 changed files with 212 additions and 1972 deletions

View File

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

View File

@@ -1,6 +1,3 @@
[![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)
@@ -34,22 +31,6 @@
| 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
@@ -66,7 +47,7 @@ SFDC_SECRET_ACCESS_KEY="..."
- Log in the docker container
```docker exec -ti helix bash```
```docker exec -t -i helix /bin/bash```
- Run this commands inside the docker conatiner
@@ -386,9 +367,3 @@ 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)

51
circle.yml Normal file
View File

@@ -0,0 +1,51 @@
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.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

BIN
dump.rdb

Binary file not shown.

View File

@@ -1,13 +0,0 @@
"""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,43 +234,24 @@ 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
x_spacing = (x * x_spacing_cos_rotation) - (y * y_spacing_sin_rotation)
y_spacing = (x * x_spacing_sin_rotation) + (y * y_spacing_cos_rotation)
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))
coordinate = Coordinate(node.coordinate.x + x_spacing, node.coordinate.y + y_spacing, node.coordinate.rotation)
if coordinate.x < 0 or coordinate.y < 0:
continue
# Optimization for `direction = GraphDirection((x, y))`
direction = graph_directions_cache[(x, y)]
direction = GraphDirection((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)
@@ -436,7 +417,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
@@ -457,7 +438,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
@@ -529,7 +510,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,6 +2,9 @@ import io
import dxfgrabber
from helix.constants.file_validation_error import FileValidationMessage
from helix.models.dxf.dxf_error import OldDxfFormatException
class DXFService(object):
"""
@@ -48,7 +51,6 @@ 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

@@ -1,26 +0,0 @@
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,6 +5,7 @@ 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), self.subarrays).compute_ebom()
ebom_parts_list = EbomCalculator(self.values, ceil(row_count), ceil(column_count), parts_list.get(module)).compute_ebom()
add_parts_to_list(parts_list, ebom_parts_list)

View File

@@ -1,3 +1,4 @@
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, panels = None):
def __init__(self, user_values, row_count, column_count, modules_count = 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,14 +30,6 @@ 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 = {}
@@ -46,14 +38,25 @@ class EbomCalculator(object):
monitors = self.values.power_monitors()
module_type = self.values.module_type()
system_type = self.values.system_type()
is_delta = self.resolve_is_delta()
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
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']
@@ -88,18 +91,15 @@ class EbomCalculator(object):
for monitor in monitors:
if monitor['power_source'][0] == 'Switch Gear/External':
add_parts_to_list(part_list, {proper_monitor_controller: 1}, 1)
if (is_delta):
add_parts_to_list(part_list, {ethernet_plug: 2},1)
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, {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), is_delta)
cable_supports = self.calculate_cable_supports(panel_board_counts, len(standalone_inverters))
add_parts_to_list(part_list, {cable_support: 1, cable_support_lid: 1}, cable_supports)
add_parts_to_list(part_list, {rear_skirt_1_1: -1}, ceil(cable_supports*.38))
add_parts_to_list(part_list, {rear_skirt: -1}, ceil(cable_supports*.38))
dependent_part_list = {}
@@ -121,37 +121,21 @@ 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, is_delta):
# There are some tests that don't have panels
if is_delta and self.panels is None:
def calculate_cable_supports(self, panel_board_counts, standalone_inverter_count):
if sum(panel_board_counts) == 0:
return 0
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)
if self.values.system_type() == SystemType.dualTilt:
dimension1 = self.column_count
dimension2 = self.row_count
else:
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)
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
from helix.constants.parts import link_tray, cross_tray, ballast, cross_tray_1_1, leading_tray
from helix.constants.system_type import SystemType

View File

@@ -1,5 +1,6 @@
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,3 +1,4 @@
import copy
from enum import Enum
from helix.constants.system_type import SystemType

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
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,14 +130,8 @@ class NodeQuadTree():
self.nodeList = toKeep
# Return a list of all possible nodes that can be near this point
# 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)
def retrieve(self, nearPoint):
retNodes = list(self.nodeList)
if self.quads[0] is not None:
index = self.getIndex(nearPoint)

View File

@@ -1,26 +1,22 @@
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) {
// $('#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>');
}
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>');
}
});
$("#dxf_upload").change((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>');
}
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>');
}
});
};

View File

@@ -8,16 +8,13 @@ import AutoUpload from './auto_upload';
$(document).ready(function () {
AutoUpload();
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;
}
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;
});

View File

@@ -1,16 +0,0 @@
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,10 +6,8 @@ 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
@@ -36,20 +34,6 @@ 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 = [
@@ -83,10 +67,6 @@ 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'))
@@ -129,9 +109,6 @@ 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()
@@ -159,7 +136,6 @@ 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
@@ -177,9 +153,6 @@ 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)
@@ -226,7 +199,6 @@ 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(),
@@ -456,70 +428,6 @@ 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)
@@ -549,13 +457,8 @@ def enum():
def main():
host = '0.0.0.0'
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)
port = int(os.getenv('PORT', 5000))
app.run(host=host, port=port, debug=bool(os.getenv("FLASK_DEBUG", False)))
@app.route("/fail-test")

View File

@@ -25,7 +25,6 @@ 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:
@@ -34,15 +33,9 @@ class GraphNodeStore(list):
self.quadTree.insert(node)
del self[:]
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
possibilities = self.quadTree.retrieve(coordinate)
for node in possibilities:
if self.distance_squared(node, coordinate) <= variance_square:
if self.distance_squared(node, coordinate) <= self.variance ** 2:
return node
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
else:
return None

View File

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

View File

@@ -1,118 +0,0 @@
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

View File

@@ -1,85 +0,0 @@
/*
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?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');
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');
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?24283821#fontello') format('svg');
src: url('../font/fontello.svg?10976371#fontello') format('svg');
}
}
*/
@@ -69,5 +69,4 @@
.icon-info:before { content: '\e80b'; } /* '' */
.icon-close:before { content: '\e80c'; } /* '' */
.icon-sunpower-logo:before { content: '\e80d'; } /* '' */
.icon-upload-cloud:before { content: '\e80e'; } /* '' */
.icon-spin6:before { content: '\e839'; } /* '' */
.icon-upload-cloud:before { content: '\e80e'; } /* '' */

View File

@@ -1023,21 +1023,3 @@ 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) 2017 by original authors @ fontello.com</metadata>
<metadata>Copyright (C) 2016 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,8 +35,6 @@
<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: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -73,18 +73,15 @@
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
$(document).ready(function () {
(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;
}
(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;
});
/***/ }),
@@ -24733,32 +24730,28 @@
"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) {
// $('#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>');
}
});
$("#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>');
}
});
$("#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>');
}
});
$("#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>');
}
});
};
exports.default = AutoUpload;

View File

@@ -1,15 +1,6 @@
{% 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,10 +1,6 @@
{% 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>
@@ -24,14 +20,6 @@
<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,7 +8,6 @@
<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,9 +1,5 @@
<div class="navigation_buttons">
{% if context['hide_back'] %}
{% else %}
<a class="back button" href="{{ context['steps'][context['current_step'] - 2][2] }}">Back</a>
{% endif %}
<a class="back button" href="{{ context['steps'][context['current_step'] - 2][2] }}">Back</a>
{% 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,11 +3,10 @@
{% block contents %}
{% if context['site_data_available'] %}
<div class="form_section">
<h3>Summary <small>({{ context['project_name'] }})</small></h3>
<h3>Summary</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>
@@ -81,13 +80,8 @@
</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):

View File

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

View File

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

View File

@@ -7,7 +7,3 @@ 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,5 +1,3 @@
gunicorn==19.7.1
meinheld==0.6.1
Flask==0.10.1
itsdangerous==0.24
Jinja2==2.8
@@ -20,6 +18,3 @@ 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.35

View File

@@ -1,5 +1,4 @@
from invoke import run, task
import os
@task
@@ -35,10 +34,9 @@ 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('SP_DOCGEN_API_KEY=DC97-20AF-567E gunicorn -c gunicorn_config.py --pythonpath helix main:app')
run('PYTHONPATH=. SP_DOCGEN_API_KEY=DC97-20AF-567E python helix/main.py')
@task
@@ -54,23 +52,3 @@ 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

@@ -118,7 +118,7 @@ class EbomCalculatorTest(unittest.TestCase):
wire_clip_large: 2907,
cable_support_lid: 368,
cable_support: 368,
rear_skirt_1_1: -140,
rear_skirt: -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: 590,
wire_clip_large: 513,
cable_support_lid: 0,
cable_support: 0,
rear_skirt_1_1: 0,
rear_skirt: 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: 200,
wire_clip_large: 171,
cable_support_lid: 0,
cable_support: 0,
rear_skirt_1_1: 0,
rear_skirt: 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_1_1: -103,
rear_skirt: -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_1_1: -103,
rear_skirt: -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_1_1: -103,
rear_skirt: -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_1_1: 0,
rear_skirt: 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_1_1: 0,
rear_skirt: 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_1_1: -264,
rear_skirt: -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_1_1: -103,
rear_skirt: -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_1_1: -103,
rear_skirt: -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_1_1: 0,
rear_skirt: 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_1_1: 0,
rear_skirt: 0,
monitor_power_plug: 1,
}
@@ -1055,14 +1055,14 @@ class EbomCalculatorTest(unittest.TestCase):
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt_1_1: 0
rear_skirt: 0
}
expected_output_240_pseries = {
stump: 0,
cable_support_lid: 0,
cable_support: 0,
rear_skirt_1_1: 0,
rear_skirt: 0,
monitor_controller_240_v: 1,
}
@@ -1116,13 +1116,14 @@ class EbomCalculatorTest(unittest.TestCase):
stump: 0,
cable_support: 0,
cable_support_lid: 0,
rear_skirt_1_1: 0,
rear_skirt: 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',
@@ -1130,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_1_1: 0,
rear_skirt: 0,
monitor_controller_480_v:1,
flat_washer: 4,
channel_nut: 4,
@@ -1151,3 +1152,7 @@ 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_1_1: 44,
rear_skirt: 44,
leading_tray: 25,
ballast: 192,
anchor: 15, # 12 + the 3 seismic anchors

View File

@@ -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
Can't render this file because it contains an unexpected character in line 10 and column 30.

File diff suppressed because it is too large Load Diff

View File

@@ -1,169 +0,0 @@
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

@@ -1,35 +0,0 @@
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)