Wed, 31 Jan 2018 14:34:23 +0200
Reworked web front, problems are now sorted by category as well as line number
ldcheck.py | file | annotate | diff | comparison | revisions | |
static/error.svg | file | annotate | diff | comparison | revisions | |
static/favicon.ico | file | annotate | diff | comparison | revisions | |
static/notice.svg | file | annotate | diff | comparison | revisions | |
static/style.css | file | annotate | diff | comparison | revisions | |
static/warning.svg | file | annotate | diff | comparison | revisions | |
templates/webfront.html | file | annotate | diff | comparison | revisions | |
tests/quadrilaterals.py | file | annotate | diff | comparison | revisions | |
testsuite.py | file | annotate | diff | comparison | revisions | |
webfront.py | file | annotate | diff | comparison | revisions |
--- a/ldcheck.py Wed Jan 24 23:14:01 2018 +0200 +++ b/ldcheck.py Wed Jan 31 14:34:23 2018 +0200 @@ -44,10 +44,6 @@ if (library_path / path).is_file() ] -def hairline_score(smallest_angle): - from math import log10 - return max(0, -log10(smallest_angle)) - class Model: def __init__(self, body): self.body = body
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/error.svg Wed Jan 31 14:34:23 2018 +0200 @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg id="svg6361" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" sodipodi:docname="process-stop.svg" xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" height="48px" sodipodi:version="0.32" width="48px" xmlns:cc="http://web.resource.org/cc/" xmlns:xlink="http://www.w3.org/1999/xlink" sodipodi:docbase="/home/jimmac/gfx/ximian/tango-icon-theme/scalable/actions" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs id="defs3"> + <radialGradient id="radialGradient21650" gradientUnits="userSpaceOnUse" cy="36.75" cx="25.125" gradientTransform="matrix(1 0 0 .59524 -2.3007e-15 14.875)" r="15.75"> + <stop id="stop21646" offset="0"/> + <stop id="stop21648" stop-opacity="0" offset="1"/> + </radialGradient> + <linearGradient id="linearGradient2057" y2="47.374" gradientUnits="userSpaceOnUse" x2="53.57" gradientTransform="translate(0 -2)" y1="12.504" x1="15.737"> + <stop id="stop11782" stop-color="#ff8b8b" offset="0"/> + <stop id="stop11784" stop-color="#ec1b1b" offset="1"/> + </linearGradient> + <linearGradient id="linearGradient4987" y2="37.96" gradientUnits="userSpaceOnUse" x2="41.048" gradientTransform="translate(0 -2)" y1="20.105" x1="23.996"> + <stop id="stop4983" stop-color="#c00" offset="0"/> + <stop id="stop4985" stop-color="#b30000" offset="1"/> + </linearGradient> + <radialGradient id="radialGradient2239" gradientUnits="userSpaceOnUse" cy="33.302" cx="24.302" gradientTransform="matrix(1.694 -5.7757e-16 5.7757e-16 1.694 -16.865 -25.111)" r="12.302"> + <stop id="stop9649" stop-color="#fff" offset="0"/> + <stop id="stop9651" stop-color="#dbdbdb" offset="1"/> + </radialGradient> + <radialGradient id="radialGradient2254" gradientUnits="userSpaceOnUse" cy="10.666" cx="16.75" gradientTransform="matrix(4.155 -2.9792e-24 3.2557e-24 3.1987 -52.846 -23.509)" r="21.25"> + <stop id="stop2250" stop-color="#fff" offset="0"/> + <stop id="stop2252" stop-color="#fff" stop-opacity="0" offset="1"/> + </radialGradient> + <linearGradient id="linearGradient2262" y2="35.052" gradientUnits="userSpaceOnUse" x2="24.302" gradientTransform="translate(0 -2)" y1="15.802" x1="21.75"> + <stop id="stop2258" stop-color="#ff0202" offset="0"/> + <stop id="stop2260" stop-color="#ff9b9b" offset="1"/> + </linearGradient> + </defs> + <sodipodi:namedview id="base" bordercolor="#666666" pagecolor="#ffffff" showgrid="false" borderopacity="0.15294118" showguides="true"/> + <metadata id="metadata4"> + <rdf:RDF> + <cc:Work rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> + <dc:title>Stop</dc:title> + <dc:date>2005-10-16</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Andreas Nilsson</dc:title> + </cc:Agent> + </dc:creator> + <dc:subject> + <rdf:Bag> + <rdf:li>stop</rdf:li> + <rdf:li>halt</rdf:li> + <rdf:li>error</rdf:li> + </rdf:Bag> + </dc:subject> + <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/"/> + <dc:contributor> + <cc:Agent> + <dc:title>Jakub Steiner</dc:title> + </cc:Agent> + </dc:contributor> + </cc:Work> + <cc:License rdf:about="http://creativecommons.org/licenses/by-sa/2.0/"> + <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/> + <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/> + <cc:requires rdf:resource="http://web.resource.org/cc/Notice"/> + <cc:requires rdf:resource="http://web.resource.org/cc/Attribution"/> + <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/> + <cc:requires rdf:resource="http://web.resource.org/cc/ShareAlike"/> + </cc:License> + </rdf:RDF> + </metadata> + <g id="layer1"> + <path id="path21642" sodipodi:rx="15.75" sodipodi:ry="9.375" style="color:#000000" sodipodi:type="arc" d="m40.875 36.75a15.75 9.375 0 1 1 -31.5 0 15.75 9.375 0 1 1 31.5 0z" fill-rule="evenodd" opacity=".63068" transform="matrix(1.1738 0 0 0.6 -5.2659 19.575)" sodipodi:cy="36.75" sodipodi:cx="25.125" fill="url(#radialGradient21650)"/> + <path id="path9480" d="m15.591 0.49192h17.085l12.822 13.094v17.894l-12.649 12.017h-17.43l-12.925-12.839 0.0004-17.194 13.097-12.972z" fill-rule="evenodd" sodipodi:nodetypes="ccccccccc" stroke="#860000" fill="url(#linearGradient4987)"/> + <path id="path9482" opacity=".81319" d="m16.021 1.5003h16.228l12.247 12.423v17.114l-11.858 11.451h-16.768l-12.361-12.279 0.0001-16.363 12.512-12.346z" sodipodi:nodetypes="ccccccccc" stroke="url(#linearGradient2057)" fill="none"/> + <path id="path2241" opacity=".28977" d="m15.688 0.75l-12.938 12.812v17l2.9375 2.907c16.762 0.057 16.478-13.019 39.562-11.875v-7.906l-12.688-12.938h-16.874z" fill-rule="evenodd" sodipodi:nodetypes="cccccccc" fill="url(#radialGradient2254)"/> + <path id="path2787" stroke-linejoin="round" d="m16.767 10.5l-4.267 4.267 7.535 7.535-7.535 7.535 4.267 4.268 7.535-7.536 7.535 7.536 4.268-4.268-7.536-7.535 7.536-7.535-4.268-4.267-7.535 7.535-7.535-7.535z" fill-rule="evenodd" stroke="url(#linearGradient2262)" stroke-linecap="round" fill="url(#radialGradient2239)"/> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/notice.svg Wed Jan 31 14:34:23 2018 +0200 @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg id="svg1306" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="48px" width="48px" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs id="defs1308"> + <radialGradient id="radialGradient3976" gradientUnits="userSpaceOnUse" cy="40" cx="23.857" gradientTransform="matrix(1 0 0 .5 0 20)" r="17.143"> + <stop id="stop4128" offset="0"/> + <stop id="stop4130" stop-opacity="0" offset="1"/> + </radialGradient> + <linearGradient id="linearGradient3980" y2="-8.5627" gradientUnits="userSpaceOnUse" x2="20.065" y1="53.836" x1="43.936"> + <stop id="stop2481" stop-color="#ffe69b" offset="0"/> + <stop id="stop2483" stop-color="#fff" offset="1"/> + </linearGradient> + </defs> + <metadata id="metadata1311"> + <rdf:RDF> + <cc:Work rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> + <dc:creator> + <cc:Agent> + <dc:title>Rodney Dawes</dc:title> + </cc:Agent> + </dc:creator> + <dc:contributor> + <cc:Agent> + <dc:title>Jakub Steiner, Garrett LeSage</dc:title> + </cc:Agent> + </dc:contributor> + <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/"/> + <dc:title/> + </cc:Work> + <cc:License rdf:about="http://creativecommons.org/licenses/by-sa/2.0/"> + <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/> + <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/> + <cc:requires rdf:resource="http://web.resource.org/cc/Notice"/> + <cc:requires rdf:resource="http://web.resource.org/cc/Attribution"/> + <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/> + <cc:requires rdf:resource="http://web.resource.org/cc/ShareAlike"/> + </cc:License> + </rdf:RDF> + </metadata> + <g id="layer2"> + <path id="path6548" opacity=".6" style="color:#000000" d="m41 40a17.143 8.5714 0 1 1 -34.286 0 17.143 8.5714 0 1 1 34.286 0z" transform="matrix(1.0706 0 0 .525 -.89276 22.5)" display="block" fill="url(#radialGradient3976)"/> + </g> + <g id="layer1"> + <g id="g4006"> + <path id="path1314" d="m46.857 23.929c0 12.9-10.457 23.357-23.357 23.357s-23.357-10.457-23.357-23.357 10.457-23.357 23.357-23.357 23.357 10.457 23.357 23.357z" transform="matrix(.92049 0 0 .92049 2.3685 .97408)" stroke="#204a87" stroke-width="1.0864" fill="#3465a4"/> + <path id="path3560" opacity=".34659" d="m49.902 26.635c0 13.25-10.741 23.991-23.991 23.991s-23.991-10.741-23.991-23.991 10.741-23.991 23.991-23.991 23.991 10.741 23.991 23.991z" fill-opacity="0" transform="matrix(.85448 0 0 .85448 1.86 .24062)" stroke="url(#linearGradient3980)" stroke-width="1.1703"/> + </g> + </g> + <g id="layer3" fill="#fff"> + <path id="path3684" opacity=".25" d="m24 3c-11.046 0-20 8.954-20 20 0 1.6861 0.23214 3.3108 0.625 4.875 3.204 4.7933 13.254-0.12014 20.219-5.5938 8.008-6.2931 17.842 8.3593 19.125 1.4375 0.008-0.239 0.031-0.478 0.031-0.719 0-11.046-8.954-20-20-20z"/> + <g id="g13674" transform="translate(2.8959 -3.6973)"> + <path id="text3246" d="m21.104 13.697c-0.86423 0.01942-1.5754 0.30821-2.1336 0.86638-0.55819 0.55821-0.84699 1.2694-0.86638 2.1336 0.01939 0.86424 0.30819 1.5754 0.86638 2.1336 0.55818 0.55821 1.2694 0.847 2.1336 0.86638 0.86422-0.01938 1.5754-0.30817 2.1336-0.86638 0.55818-0.55817 0.84697-1.2694 0.86638-2.1336-0.01941-0.8642-0.3082-1.5754-0.86638-2.1336-0.5582-0.55817-1.2694-0.84696-2.1336-0.86638z"/> + <path id="path13678" d="m15.104 21.697v3h2c0.55228 0 1 0.44772 1 1v9c0 0.55228-0.44772 1-1 1h-2v3h12v-3h-2c-0.55228 0-1-0.44772-1-1v-13h-9z"/> + </g> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/style.css Wed Jan 31 14:34:23 2018 +0200 @@ -0,0 +1,81 @@ + +body +{ + font-family: sans-serif; + margin: 0; +} + +.ldraw-code +{ + font-family: monospace; +} + +.problems-list +{ + list-style: none; +} + +.problems-list li +{ + padding: 5px; + border: 1px solid black; + border-radius: 10px; + margin-bottom: 3px; +} + +.problems-list li.problem-error +{ + background-color: #b44; + border-color: red; + color: white; +} + +.problems-list li.problem-warning +{ + background-color: #fc6; + border-color: #830; + color: black; +} + +.problems-list li.problem-notice +{ + background-color: #def; + border-color: #024; + color: black; +} + +.problem-icon +{ + float: left; + margin-right: 10px; +} + +.top-form +{ + background: white; + background: linear-gradient( + to bottom, + #cceecf 0%, + #cceecf 85%, + #163 100% + ); + padding-bottom: 2vh; + padding-top: 2vh; + padding-left: 5%; + padding-right: 5%; + text-align: center; +} + +.report-container +{ + background: linear-gradient( + to bottom, + #ccc 0, + #fff 1vh, + #fff 100% + ); + min-height: 10vh; + padding-left: 5%; + padding-right: 5%; + padding-top: 2vh; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/warning.svg Wed Jan 31 14:34:23 2018 +0200 @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48" + height="48" + version="1.1" + id="svg998" + sodipodi:docname="warning.svg" + inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> + <metadata + id="metadata1002"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1049" + id="namedview1000" + showgrid="false" + inkscape:zoom="3.1267378" + inkscape:cx="-74.508859" + inkscape:cy="4.4248979" + inkscape:window-x="1920" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg998" /> + <defs + id="defs982"> + <radialGradient + id="a" + gradientUnits="userSpaceOnUse" + cy="41.4" + cx="24.29" + gradientTransform="matrix(1 0 0 .3526 0 26.8)" + r="21.1"> + <stop + offset="0" + id="stop977" /> + <stop + stop-opacity="0" + offset="1" + id="stop979" /> + </radialGradient> + </defs> + <ellipse + opacity=".41" + rx="21.1" + ry="7.44" + cy="41.4" + cx="24.29" + fill="url(#a)" + id="ellipse984" /> + <circle + cy="21.4" + cx="24.3" + r="21.27" + fill="#914900" + id="circle986" /> + <circle + r="20" + cy="21.31" + stroke="#fcaf3e" + cx="24.44" + fill="#f57900" + id="circle988" + style="fill:#f5ca00;fill-opacity:1" /> + <path + style="fill:#000000;fill-opacity:1" + inkscape:connector-curvature="0" + id="path990" + d="m 21.46,10.39 c -0.13,0 -0.23,0.16 -0.23,0.37 L 22.3,25.2 c 0,0.21 0.11,0.38 0.23,0.38 h 3.57 c 0.13,0 0.23,-0.17 0.23,-0.38 L 27.4,10.76 c 0,-0.21 -0.11,-0.37 -0.23,-0.37 h -5.7" /> + <path + style="fill:#ffffff;fill-opacity:0.21000001" + inkscape:connector-curvature="0" + id="path992" + d="m 43.68,20.48 c 0,10.83 -6.1,-4.31 -18.67,0.39 C 12.28,25.59 4.43,31.31 4.43,20.48 4.43,9.65 13.23,0.86 24.06,0.86 c 10.83,0 19.62,8.79 19.62,19.62" /> + <circle + style="fill:#000000;fill-opacity:1" + id="circle994" + r="2.3" + cx="24.360001" + cy="30.200001" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/webfront.html Wed Jan 31 14:34:23 2018 +0200 @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="icon" href="../static/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <title>LDraw part verification tool</title> +</head> +<body> +<div class="top-form"> + <h1>Check your part here</h1> + <form method="post" enctype="multipart/form-data"> + <input type="file" name="file"/> + <input type="submit" /> + </form> +</div> +<div class="report-container"> + +{% if report %} + <ul class="problems-list"> + {% for problem in report['problems'] %} + <li class="problem-{{problem['type']}}"> + <img + src="static/{{problem['type']}}.svg" + width="32" height="32" + class="problem-icon" + /> + Line {{problem['line-number']}}: {{problem['message']}} + <br /> + <span class="ldraw-code">{{problem['ldraw-code']}}</span> + </li> + {% endfor %} + </ul> +{% endif %} +{% if report and not report['problems'] %} +No problems whatsoever. +{% endif %} + +</div> +</body> +</html>
--- a/tests/quadrilaterals.py Wed Jan 24 23:14:01 2018 +0200 +++ b/tests/quadrilaterals.py Wed Jan 31 14:34:23 2018 +0200 @@ -1,5 +1,5 @@ from math import radians -from testsuite import warning, error +from testsuite import warning, error, notice from geometry import * def sign_consistency(container): @@ -58,12 +58,26 @@ if cross_product(b - a, c - a).length() < 1e-5: yield error(element, 'collinearity-error') +def hairline_score(smallest_angle): + from math import log10 + return max(0, -log10(smallest_angle)) + +def hairline_test(model): + for element in model.body: + if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: + smallest_angle = element.geometry.smallest_angle + if smallest_angle < radians(0.5): + yield notice(element, 'hairline-warning', + smallest_angle = smallest_angle, + ) + manifest = { 'tests': { 'skew': skew_test, 'concave': concave_test, 'bowtie': bowtie_test, 'collinearity': collinear_test, + 'hairline': hairline_test, }, 'messages': { 'skew-error': lambda skew_angle: @@ -77,5 +91,9 @@ 'concave-error': 'concave quadrilateral', 'self-intersecting': 'bowtie quadrilateral', 'collinearity-error': 'collinear polygon', + 'hairline-warning': lambda smallest_angle: + str.format('hairline polygon (smallest angle {})', + degree_rep(smallest_angle), + ), }, }
--- a/testsuite.py Wed Jan 24 23:14:01 2018 +0200 +++ b/testsuite.py Wed Jan 31 14:34:23 2018 +0200 @@ -14,6 +14,9 @@ def error(bad_object, error_name, **args): return report_element(bad_object, 'error', error_name, args) +def notice(bad_object, error_name, **args): + return report_element(bad_object, 'notice', error_name, args) + def test_discovery(): ''' Finds all test modules and yields their names. @@ -67,22 +70,31 @@ warn(str.format('Module {} does not have a manifest', module_name)) return test_suite +def problem_key(problem): + problem_hierarchy = ['error', 'warning', 'notice'] + return (problem_hierarchy.index(problem['type']), problem['line-number']) + def check_model(model, test_suite = None): if not test_suite: test_suite = load_tests() - report = [] + problems = [] line_numbers = { element: (i, i + 1 + model.body_offset) for i, element in enumerate(model.body) } for test_name, test_function in test_suite['tests'].items(): - problems = test_function(model) - for problem in problems: + for problem in test_function(model): problem['body-index'], problem['line-number'] \ = line_numbers[problem['object']] del problem['object'] - report.append(problem) - return report + problems.append(problem) + return { + 'passed': not any( + problem['type'] == 'error' + for problem in problems + ), + 'problems': sorted(problems, key = problem_key), + } def problem_text(problem, test_suite): message = test_suite['messages'][problem['name']] @@ -91,8 +103,8 @@ return message def format_report_html(report, model, test_suite): - result = [] - for problem in report: + messages = [] + for problem in report['problems']: ldraw_code = model.body[problem['body-index']].textual_representation() message = str.format( '<li class="{problem_type}">{model_name}:{line_number}:' @@ -103,21 +115,20 @@ message = problem_text(problem, test_suite), ldraw_code = ldraw_code, ) - result.append((problem['line-number'], message)) - return '\n'.join( - problem[1] - for problem in sorted(result) - ) + messages.append(message) + return '\n'.join(messages) def format_report(report, model, test_suite): import colorama colorama.init() - result = [] - for problem in report: + messages = [] + for problem in report['problems']: if problem['type'] == 'error': text_colour = colorama.Fore.LIGHTRED_EX elif problem['type'] == 'warning': text_colour = colorama.Fore.LIGHTYELLOW_EX + elif problem['type'] == 'notice': + text_colour = colorama.Fore.LIGHTBLUE_EX else: text_colour = '' ldraw_code = model.body[problem['body-index']].textual_representation() @@ -132,11 +143,8 @@ colour_reset = colorama.Fore.RESET, ldraw_code = ldraw_code, ) - result.append((problem['line-number'], message)) - return '\n'.join( - problem[1] - for problem in sorted(result) - ) + messages.append(message) + return '\n'.join(messages) if __name__ == '__main__': from pprint import pprint
--- a/webfront.py Wed Jan 24 23:14:01 2018 +0200 +++ b/webfront.py Wed Jan 31 14:34:23 2018 +0200 @@ -1,7 +1,8 @@ +#!/usr/bin/env python3 from flask import Flask, render_template, redirect, request from ldcheck import load_config, load_colours, find_ldconfig_ldr_paths from ldcheck import read_ldraw -from testsuite import load_tests, check_model, format_report_html +from testsuite import load_tests, check_model, problem_text app = Flask('LDCheck') @@ -12,7 +13,6 @@ if 'file' not in request.files or not request.files['file'].filename: return redirect(request.url) file = request.files['file'] - print(type(file)) config = load_config('ldcheck.cfg') for ldconfig_ldr_path in find_ldconfig_ldr_paths(config): with ldconfig_ldr_path.open() as ldconfig_ldr: @@ -24,18 +24,28 @@ ) test_suite = load_tests() report = check_model(model, test_suite) - return str.format( - '<!doctype html><html><body><ul>{report}</ul></body></html>', - report = format_report_html(report, model, test_suite) - ) - return ''' - <!doctype html> - <title>Upload new File</title> - <h1>Upload new File</h1> - <form method=post enctype=multipart/form-data> - <p><input type=file name=file> - <input type=submit value=Upload> - </form> - ''' + + # Amend human-readable messages into the report + for problem in report['problems']: + object = model.body[problem['body-index']] + problem['message'] = problem_text(problem, test_suite) + problem['ldraw-code'] = object.textual_representation() + else: + report = None + return render_template('webfront.html', + report = report, + ) -app.run() +@app.route('/static/<path:path>') +def static_file(path): + from flask import send_from_directory + from os import path + return send_from_directory(path.join('static', path)) + +if __name__ == '__main__': + from argparse import ArgumentParser + parser = ArgumentParser() + parser.add_argument('-p', '--port', type = int, default = 5000) + parser.add_argument('-d', '--debug', action = 'store_true') + args = parser.parse_args() + app.run(port = args.port, debug = args.debug)