merge with upstream
This commit is contained in:
@@ -88,8 +88,6 @@ class EbomCalculator(object):
|
|||||||
for monitor in monitors:
|
for monitor in monitors:
|
||||||
if monitor['power_source'][0] == 'Switch Gear/External':
|
if monitor['power_source'][0] == 'Switch Gear/External':
|
||||||
add_parts_to_list(part_list, {proper_monitor_controller: 1}, 1)
|
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:
|
if is_delta:
|
||||||
clips_amount = inverter_count * self.row_count * 1.15
|
clips_amount = inverter_count * self.row_count * 1.15
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ from helix.calculators.subarray_helper import extract_subarray
|
|||||||
|
|
||||||
from helix.constants.global_constants import minimum_racking_capacity
|
from helix.constants.global_constants import minimum_racking_capacity
|
||||||
from helix.constants.panel_type import PanelType
|
from helix.constants.panel_type import PanelType
|
||||||
|
from helix.constants.file_validation_error import FileValidationMessage,FileValidationException
|
||||||
from helix.models.subarray import Subarray
|
from helix.models.subarray import Subarray
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SeismicCalculator(object):
|
class SeismicCalculator(object):
|
||||||
def __init__(self, values, graph_repository):
|
def __init__(self, values, graph_repository):
|
||||||
self.values = values
|
self.values = values
|
||||||
@@ -37,13 +39,20 @@ class SeismicCalculator(object):
|
|||||||
more_anchors_needed = True
|
more_anchors_needed = True
|
||||||
perimeter_covered = sds < 1.0
|
perimeter_covered = sds < 1.0
|
||||||
anchor_threshold = 0
|
anchor_threshold = 0
|
||||||
|
was_rung_empty = False
|
||||||
while more_anchors_needed:
|
while more_anchors_needed:
|
||||||
rung = graph.pop_rung()
|
rung = graph.pop_rung()
|
||||||
interval = int(self.seismic_anchor_interval())
|
interval = int(self.seismic_anchor_interval())
|
||||||
nodes_since_last_anchor = interval
|
nodes_since_last_anchor = interval
|
||||||
if len(rung) == 0:
|
if len(rung) == 0:
|
||||||
|
if was_rung_empty:
|
||||||
|
# detected an infinite loop
|
||||||
|
# something is wrong with the input file
|
||||||
|
# probably panels overlapping
|
||||||
|
raise FileValidationException(FileValidationMessage.PanelsTooClose.value)
|
||||||
graph.reset()
|
graph.reset()
|
||||||
anchor_threshold += 1
|
anchor_threshold += 1
|
||||||
|
was_rung_empty = True
|
||||||
continue
|
continue
|
||||||
while more_anchors_needed and interval >= 0:
|
while more_anchors_needed and interval >= 0:
|
||||||
for node in rung:
|
for node in rung:
|
||||||
|
|||||||
@@ -49,3 +49,7 @@ class FileValidationError(object):
|
|||||||
if self.__class__ != other.__class__:
|
if self.__class__ != other.__class__:
|
||||||
return False
|
return False
|
||||||
return self.row_number == other.row_number and self.validation_message == other.validation_message
|
return self.row_number == other.row_number and self.validation_message == other.validation_message
|
||||||
|
|
||||||
|
class FileValidationException(Exception):
|
||||||
|
def __init__(self, message):
|
||||||
|
self.message = message
|
||||||
|
|||||||
164
helix/main.py
164
helix/main.py
@@ -1,9 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
from urllib.parse import urlparse
|
||||||
import rollbar
|
import rollbar
|
||||||
import rollbar.contrib.flask
|
import rollbar.contrib.flask
|
||||||
from flask import Flask, request, make_response, session, render_template, \
|
from flask import Flask, request, make_response, session, render_template, \
|
||||||
redirect, url_for
|
redirect, url_for, jsonify
|
||||||
from flask import got_request_exception
|
from flask import got_request_exception
|
||||||
from flask.ext import assets
|
from flask.ext import assets
|
||||||
from flask_oauthlib.client import OAuth
|
from flask_oauthlib.client import OAuth
|
||||||
@@ -16,6 +17,7 @@ from helix.Services.dxf_service import DXFService
|
|||||||
from helix.api.api import api
|
from helix.api.api import api
|
||||||
from helix.calculators.calculator import Calculator
|
from helix.calculators.calculator import Calculator
|
||||||
from helix.constants import redis_constant, sql_constant
|
from helix.constants import redis_constant, sql_constant
|
||||||
|
from helix.constants.file_validation_error import FileValidationError, FileValidationException
|
||||||
from helix.constants.inverter_type import InverterType
|
from helix.constants.inverter_type import InverterType
|
||||||
from helix.constants.system_type import SystemType
|
from helix.constants.system_type import SystemType
|
||||||
from helix.csv_builder import CsvBuilder
|
from helix.csv_builder import CsvBuilder
|
||||||
@@ -184,6 +186,44 @@ def summary():
|
|||||||
return render_template('site_summary.html.jinja', context=context)
|
return render_template('site_summary.html.jinja', context=context)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_dxf_file(session_manager, file_contents, filename=None):
|
||||||
|
errors = []
|
||||||
|
user_values = session_manager.user_values()
|
||||||
|
validator = FileValidator(user_values)
|
||||||
|
calculator = Calculator(user_values, calculate_panel_data=False)
|
||||||
|
|
||||||
|
extension = os.path.splitext(filename)[1] or 'dxf'
|
||||||
|
extension = extension.split('.')[-1]
|
||||||
|
|
||||||
|
validation_error = validator.validate(file_contents, FileType.AuroraDxf, extension=extension)
|
||||||
|
if validation_error:
|
||||||
|
error_msg = validation_error.format_error_message()
|
||||||
|
errors.append(error_msg)
|
||||||
|
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(),
|
||||||
|
calculator.L_B() * 12,
|
||||||
|
DXFHelper(),
|
||||||
|
SubarrayValidator())
|
||||||
|
csv = CsvBuilder().build_cad_output(dxf_data['panels'])
|
||||||
|
session_manager.save_uploaded_file(csv, dxf_file_name=filename)
|
||||||
|
|
||||||
|
buildings = dxf_data['buildings']
|
||||||
|
session_manager.save_buildings_polygons(buildings)
|
||||||
|
session_manager.save_is_drawing_inaccurate(dxf_data['is_panel_drawing_inaccurate'])
|
||||||
|
|
||||||
|
return True, errors
|
||||||
|
except DXFError as error:
|
||||||
|
errors.append(error.message)
|
||||||
|
except OldDxfFormatException as error:
|
||||||
|
errors.append(error.message)
|
||||||
|
return False, errors
|
||||||
|
|
||||||
|
|
||||||
@app.route("/array_summary/", methods=['GET', 'POST'])
|
@app.route("/array_summary/", methods=['GET', 'POST'])
|
||||||
def array_summary():
|
def array_summary():
|
||||||
"""This endpoint allows you to upload a file.
|
"""This endpoint allows you to upload a file.
|
||||||
@@ -202,8 +242,7 @@ def array_summary():
|
|||||||
validator = FileValidator(session_manager.user_values())
|
validator = FileValidator(session_manager.user_values())
|
||||||
file = request.files['file_upload']
|
file = request.files['file_upload']
|
||||||
file_contents = validator.obtain_stream(file)
|
file_contents = validator.obtain_stream(file)
|
||||||
validation_error = validator.validate(file_contents, file,
|
validation_error = validator.validate(file_contents, FileType.Csv, file)
|
||||||
FileType.Csv)
|
|
||||||
if not validation_error:
|
if not validation_error:
|
||||||
session_manager.save_uploaded_file(file_contents,
|
session_manager.save_uploaded_file(file_contents,
|
||||||
cad_file_name=file.filename)
|
cad_file_name=file.filename)
|
||||||
@@ -215,36 +254,13 @@ def array_summary():
|
|||||||
elif 'dxf_upload' in request.files and request.files['dxf_upload'].filename:
|
elif 'dxf_upload' in request.files and request.files['dxf_upload'].filename:
|
||||||
file = request.files['dxf_upload']
|
file = request.files['dxf_upload']
|
||||||
user_values = session_manager.user_values()
|
user_values = session_manager.user_values()
|
||||||
calculator = Calculator(user_values, calculate_panel_data=False)
|
|
||||||
validator = FileValidator(user_values)
|
validator = FileValidator(user_values)
|
||||||
file_contents = validator.obtain_stream(file)
|
file_contents = validator.obtain_stream(file)
|
||||||
validation_error = validator.validate(file_contents, file,
|
success, errors = handle_dxf_file(session_manager, file_contents, filename=file.filename)
|
||||||
FileType.AuroraDxf)
|
if success:
|
||||||
if validation_error:
|
return redirect(url_for('array_summary'))
|
||||||
error_msg = validation_error.format_error_message()
|
|
||||||
array_form.dxf_upload.errors.append(error_msg)
|
|
||||||
else:
|
else:
|
||||||
try:
|
array_form.dxf_upload.errors.extend(errors)
|
||||||
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(),
|
|
||||||
calculator.L_B() * 12,
|
|
||||||
DXFHelper(),
|
|
||||||
SubarrayValidator())
|
|
||||||
csv = CsvBuilder().build_cad_output(dxf_data['panels'])
|
|
||||||
session_manager.save_uploaded_file(csv, dxf_file_name=file.filename)
|
|
||||||
|
|
||||||
buildings = dxf_data['buildings']
|
|
||||||
session_manager.save_buildings_polygons(buildings)
|
|
||||||
session_manager.save_is_drawing_inaccurate(dxf_data['is_panel_drawing_inaccurate'])
|
|
||||||
|
|
||||||
return redirect(url_for('array_summary'))
|
|
||||||
except DXFError as error:
|
|
||||||
array_form.dxf_upload.errors.append(error.message)
|
|
||||||
except OldDxfFormatException as error:
|
|
||||||
array_form.dxf_upload.errors.append(error.message)
|
|
||||||
elif context['csv_available']:
|
elif context['csv_available']:
|
||||||
return redirect(url_for('power_station_configuration'))
|
return redirect(url_for('power_station_configuration'))
|
||||||
else:
|
else:
|
||||||
@@ -256,39 +272,81 @@ def array_summary():
|
|||||||
context['dxf_file_name'] = ''
|
context['dxf_file_name'] = ''
|
||||||
if context['site_data_available'] and context['csv_available']:
|
if context['site_data_available'] and context['csv_available']:
|
||||||
user_values = session_manager.user_values()
|
user_values = session_manager.user_values()
|
||||||
calculator = Calculator(user_values)
|
try:
|
||||||
system_type = user_values.system_type()
|
calculator = Calculator(user_values)
|
||||||
module_type = user_values.module_type()
|
system_type = user_values.system_type()
|
||||||
project_presenter = ProjectPresenter(system_type, module_type)
|
module_type = user_values.module_type()
|
||||||
|
project_presenter = ProjectPresenter(system_type, module_type)
|
||||||
|
|
||||||
context['wind_zones'] = system_type.system_constants().wind_zones
|
context['wind_zones'] = system_type.system_constants().wind_zones
|
||||||
context['summary_table'] = calculator.summary_table()
|
context['summary_table'] = calculator.summary_table()
|
||||||
context['minimum_array_sizes'] = calculator.minimum_array_sizes()
|
context['minimum_array_sizes'] = calculator.minimum_array_sizes()
|
||||||
context['seismic_anchors'] = calculator.subarray_summary()
|
context['seismic_anchors'] = calculator.subarray_summary()
|
||||||
context['summary_values'] = calculator.summary_values()
|
context['summary_values'] = calculator.summary_values()
|
||||||
|
|
||||||
panels = calculator.get_computed_csv_columns()
|
panels = calculator.get_computed_csv_columns()
|
||||||
context['panel_array'] = project_presenter.get_panel_data(panels,
|
context['panel_array'] = project_presenter.get_panel_data(panels,
|
||||||
calculator.subarrays,
|
calculator.subarrays,
|
||||||
project_presenter.get_max_y(
|
project_presenter.get_max_y(
|
||||||
calculator.buildings_for_drawing,
|
calculator.buildings_for_drawing,
|
||||||
panels))
|
panels))
|
||||||
context['buildings'] = project_presenter.get_buildings(calculator.buildings_for_drawing)
|
context['buildings'] = project_presenter.get_buildings(calculator.buildings_for_drawing)
|
||||||
context['override_form'] = True
|
context['override_form'] = True
|
||||||
context['cad_file_name'] = session_manager.site.cad_file_name or 'Upload System Text Data'
|
context['cad_file_name'] = session_manager.site.cad_file_name or 'Upload System Text Data'
|
||||||
context['dxf_file_name'] = session_manager.site.dxf_file_name or 'Upload System DXF'
|
context['dxf_file_name'] = session_manager.site.dxf_file_name or 'Upload System DXF'
|
||||||
context['is_drawing_inaccurate'] = session_manager.user_values().is_panel_drawing_inaccurate()
|
context['is_drawing_inaccurate'] = session_manager.user_values().is_panel_drawing_inaccurate()
|
||||||
context['inaccurate_drawing_warning'] = 'The subarrays in this design are not parallel to each other, \
|
context['inaccurate_drawing_warning'] = 'The subarrays in this design are not parallel to each other, \
|
||||||
and the graphical representation on this page may not be accurate.'
|
and the graphical representation on this page may not be accurate.'
|
||||||
|
|
||||||
|
except FileValidationException as error:
|
||||||
|
# when calculator is about to enter infinte loop
|
||||||
|
# it throws an exception - it is supplied wrong data
|
||||||
|
context['site_data_available'] = False
|
||||||
|
context['csv_available'] = False
|
||||||
|
context['no_proceed'] = True
|
||||||
|
context['cad_file_name'] = ''
|
||||||
|
context['dxf_file_name'] = ''
|
||||||
|
context['infinite_loop_detection_message'] = error.message
|
||||||
|
|
||||||
elif not context['site_data_available']:
|
elif not context['site_data_available']:
|
||||||
context['no_proceed'] = True
|
context['no_proceed'] = True
|
||||||
|
|
||||||
|
if is_sfdc_session() and 'dxf_link_loaded' not in session:
|
||||||
|
context['javascripts'].append('auto_dxf_load')
|
||||||
|
|
||||||
db_session.close()
|
db_session.close()
|
||||||
return render_template('array_summary.html.jinja', context=context, form=array_form)
|
return render_template('array_summary.html.jinja', context=context, form=array_form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/load_dxf/", methods=['GET', 'POST'])
|
||||||
|
def load_dxf_file():
|
||||||
|
if 'dxf_link' not in session:
|
||||||
|
errors = ['DXF link not found']
|
||||||
|
response = jsonify({'errors': errors})
|
||||||
|
response.status_code = 404
|
||||||
|
return response
|
||||||
|
|
||||||
|
dxf_link = session['dxf_link']
|
||||||
|
response = requests.get(dxf_link)
|
||||||
|
if response.status_code == 200:
|
||||||
|
file_contents = response.content.decode('utf-8')
|
||||||
|
filename = urlparse(dxf_link).path.strip('/')
|
||||||
|
|
||||||
|
db_session = sql_constant.sql_session_maker()
|
||||||
|
session_manager = SessionManager(session, redis_constant.redis_store, db_session)
|
||||||
|
|
||||||
|
success, errors = handle_dxf_file(session_manager, file_contents, filename=filename)
|
||||||
|
session['dxf_link_loaded'] = True
|
||||||
|
if success:
|
||||||
|
return jsonify({'status': 'success'})
|
||||||
|
else:
|
||||||
|
errors = ['Unable to download DXF file from Sales Force ({})'.format(response.status_code)]
|
||||||
|
|
||||||
|
response = jsonify({'errors': errors})
|
||||||
|
response.status_code = 400
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/power_station_configuration/", methods=['GET', 'POST'])
|
@app.route("/power_station_configuration/", methods=['GET', 'POST'])
|
||||||
def power_station_configuration():
|
def power_station_configuration():
|
||||||
db_session = sql_constant.sql_session_maker()
|
db_session = sql_constant.sql_session_maker()
|
||||||
@@ -485,6 +543,7 @@ def sales_force_authorized():
|
|||||||
|
|
||||||
data = sf_tasks.get_site_characterization_from_sales_force(session, resp['instance_url'])
|
data = sf_tasks.get_site_characterization_from_sales_force(session, resp['instance_url'])
|
||||||
if data:
|
if data:
|
||||||
|
session['dxf_link'] = data['dxf_link']
|
||||||
session_manager.save_form_submission(data)
|
session_manager.save_form_submission(data)
|
||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
else:
|
else:
|
||||||
@@ -492,7 +551,6 @@ def sales_force_authorized():
|
|||||||
|
|
||||||
|
|
||||||
# FIXME
|
# FIXME
|
||||||
from flask import jsonify
|
|
||||||
@app.route("/export-sfdc")
|
@app.route("/export-sfdc")
|
||||||
def export_sfdc():
|
def export_sfdc():
|
||||||
if not is_sfdc_session():
|
if not is_sfdc_session():
|
||||||
@@ -515,6 +573,8 @@ def get_sales_force_token(token=None):
|
|||||||
def sales_force_logout():
|
def sales_force_logout():
|
||||||
session.pop('SFID', None)
|
session.pop('SFID', None)
|
||||||
session.pop('sales_force_token', None)
|
session.pop('sales_force_token', None)
|
||||||
|
session.pop('dxf_link', None)
|
||||||
|
session.pop('dxf_link_loaded', None)
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
# End of Sales Force Integration
|
# End of Sales Force Integration
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ a.back {
|
|||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: 0 5px;
|
margin: 2px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|||||||
@@ -59,3 +59,35 @@ h1 {
|
|||||||
.spacer {
|
.spacer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.msg-container {
|
||||||
|
background-color: $off-white;
|
||||||
|
border: 1px solid $medium-border-color;
|
||||||
|
margin: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
.msg-container li {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -832,6 +832,41 @@ h1 {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* line 63 */
|
||||||
|
.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;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* line 84 */
|
||||||
|
.msg-container {
|
||||||
|
background-color: #FDFDFD;
|
||||||
|
border: 1px solid #D8D8D8;
|
||||||
|
margin: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* line 91 */
|
||||||
|
.msg-container li {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* line 3 */
|
/* line 3 */
|
||||||
.navigation_header {
|
.navigation_header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1023,21 +1058,3 @@ table .right_border_cell {
|
|||||||
margin-bottom: 25px;
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
22
helix/static/javascripts/auto_dxf_load.js
Normal file
22
helix/static/javascripts/auto_dxf_load.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$("#sf_msg_container").empty();
|
||||||
|
$("#sf_msg_container").show();
|
||||||
|
$('#sf-spinner-panel').css('width', '100%'); // Workaround for Safari issue
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/load_dxf/'
|
||||||
|
}).done(function(r) {
|
||||||
|
console.log(r)
|
||||||
|
$("#sf_msg_container").append('<li class="">DXF from Sales Force loaded successfully.</li>');
|
||||||
|
setTimeout(function() { window.location.reload() }, 500);
|
||||||
|
}).fail(function(r) {
|
||||||
|
// console.log(r.status)
|
||||||
|
r.responseJSON.errors.forEach((msg) => {
|
||||||
|
$("#sf_msg_container").append('<li class="error_message">' + msg + '</li>');
|
||||||
|
})
|
||||||
|
}).always(function() {
|
||||||
|
$('#sf-spinner-panel').css('width', '0%'); // Workaround for Safari issue
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,6 +10,16 @@
|
|||||||
<i class="icon-spin6 animate-spin"></i>
|
<i class="icon-spin6 animate-spin"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if 'SFID' in session %}
|
||||||
|
<ul id="sf_msg_container" class="msg-container" style="display:none;">
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="sf-spinner-panel" class="spinner-panel">
|
||||||
|
<p>Loading DXF file from Sales Force. Please wait, this may take a while.</p>
|
||||||
|
<i class="icon-spin6 animate-spin"></i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not context['csv_available'] %}
|
{% if not context['csv_available'] %}
|
||||||
<form action="" method="post" enctype="multipart/form-data">
|
<form action="" method="post" enctype="multipart/form-data">
|
||||||
{{ form.csrf_token }}
|
{{ form.csrf_token }}
|
||||||
@@ -33,6 +43,11 @@
|
|||||||
<span id="error_message_txt" class="error_message centered_error">{{ field.errors[0] }}</span>
|
<span id="error_message_txt" class="error_message centered_error">{{ field.errors[0] }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if context['infinite_loop_detection_message'] %}
|
||||||
|
<span id="error_message_txt_inf_loop" class="error_message centered_error">{{ context['infinite_loop_detection_message'] }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -100,13 +100,13 @@ class FileValidator(object):
|
|||||||
finally:
|
finally:
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def validate(self, stream, file, expected):
|
def validate(self, stream, expected, file=None, extension=None):
|
||||||
"""Validates the uploaded file by extension
|
"""Validates the uploaded file by extension
|
||||||
and content
|
and content
|
||||||
|
|
||||||
Arguments;
|
Arguments;
|
||||||
stream (string): File content
|
stream (string): File content
|
||||||
file (FileObject): A file object provided from the ui
|
file (FileObject): A file object provided from the ui. Used only to get the file extension.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -114,7 +114,8 @@ class FileValidator(object):
|
|||||||
file_type = self.identify_file_type(stream)
|
file_type = self.identify_file_type(stream)
|
||||||
assert file_type == expected
|
assert file_type == expected
|
||||||
validator = file_type.validator(self.values)
|
validator = file_type.validator(self.values)
|
||||||
extension = self.obtain_extension(file)
|
if extension is None:
|
||||||
|
extension = self.obtain_extension(file)
|
||||||
file_type.valid_mapping(extension)
|
file_type.valid_mapping(extension)
|
||||||
return validator.validate(stream)
|
return validator.validate(stream)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
|
|||||||
@@ -1118,7 +1118,6 @@ class EbomCalculatorTest(unittest.TestCase):
|
|||||||
cable_support_lid: 0,
|
cable_support_lid: 0,
|
||||||
rear_skirt_1_1: 0,
|
rear_skirt_1_1: 0,
|
||||||
monitor_controller_480_v: 1,
|
monitor_controller_480_v: 1,
|
||||||
ethernet_plug: 2,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_dictionary_equal(self.subject.compute_ebom(), expected_output)
|
assert_dictionary_equal(self.subject.compute_ebom(), expected_output)
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ class FileValidatorTest(unittest.TestCase):
|
|||||||
with open('test/fixtures/input_single_tilt.csv', 'r',
|
with open('test/fixtures/input_single_tilt.csv', 'r',
|
||||||
newline='') as file:
|
newline='') as file:
|
||||||
cad_input = file.read()
|
cad_input = file.read()
|
||||||
eq_(self.subject.validate(cad_input, fake_file, FileType.Csv), None)
|
eq_(self.subject.validate(cad_input, FileType.Csv, fake_file), None)
|
||||||
|
|
||||||
def test_unknown_files_are_invalid(self):
|
def test_unknown_files_are_invalid(self):
|
||||||
fake_file = MagicMock()
|
fake_file = MagicMock()
|
||||||
type(fake_file).filename = PropertyMock(return_value="Hi")
|
type(fake_file).filename = PropertyMock(return_value="Hi")
|
||||||
result = self.subject.validate("Hi", fake_file, FileType.Unknown)
|
result = self.subject.validate("Hi", FileType.Unknown, fake_file)
|
||||||
self.should_have_error(result,
|
self.should_have_error(result,
|
||||||
FileValidationMessage.UnknownFileUploaded, None)
|
FileValidationMessage.UnknownFileUploaded, None)
|
||||||
|
|
||||||
@@ -62,8 +62,7 @@ class FileValidatorTest(unittest.TestCase):
|
|||||||
fake_file.read.return_value = open(fname, "rb").read()
|
fake_file.read.return_value = open(fname, "rb").read()
|
||||||
fake_file.filename.return_value = "expected_dual_tilt_pseries_image.png"
|
fake_file.filename.return_value = "expected_dual_tilt_pseries_image.png"
|
||||||
stream = self.subject.obtain_stream(fake_file)
|
stream = self.subject.obtain_stream(fake_file)
|
||||||
self.should_have_error(self.subject.validate(stream, fake_file,
|
self.should_have_error(self.subject.validate(stream, FileType.AuroraDxf, fake_file),
|
||||||
FileType.AuroraDxf),
|
|
||||||
FileValidationMessage.ExpectedDxfFile,
|
FileValidationMessage.ExpectedDxfFile,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user