# HG changeset patch # User Santeri Piippo # Date 1517402063 -7200 # Node ID 75f44d3063da27c1e6cfe5b8aab63ab0893e28aa # Parent 02e7e1d73ebbe04ceed70ed734bee52cb3f72c1c Reworked web front, problems are now sorted by category as well as line number diff -r 02e7e1d73ebb -r 75f44d3063da ldcheck.py --- 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 diff -r 02e7e1d73ebb -r 75f44d3063da static/error.svg --- /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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Stop + 2005-10-16 + + + Andreas Nilsson + + + + + stop + halt + error + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + diff -r 02e7e1d73ebb -r 75f44d3063da static/favicon.ico Binary file static/favicon.ico has changed diff -r 02e7e1d73ebb -r 75f44d3063da static/notice.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 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + Rodney Dawes + + + + + Jakub Steiner, Garrett LeSage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 02e7e1d73ebb -r 75f44d3063da static/style.css --- /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; +} diff -r 02e7e1d73ebb -r 75f44d3063da static/warning.svg --- /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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff -r 02e7e1d73ebb -r 75f44d3063da templates/webfront.html --- /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 @@ + + + + + + LDraw part verification tool + + +
+

Check your part here

+
+ + +
+
+
+ +{% if report %} + +{% endif %} +{% if report and not report['problems'] %} +No problems whatsoever. +{% endif %} + +
+ + diff -r 02e7e1d73ebb -r 75f44d3063da tests/quadrilaterals.py --- 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), + ), }, } diff -r 02e7e1d73ebb -r 75f44d3063da testsuite.py --- 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( '
  • {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 diff -r 02e7e1d73ebb -r 75f44d3063da webfront.py --- 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( - '', - report = format_report_html(report, model, test_suite) - ) - return ''' - - Upload new File -

    Upload new File

    -
    -

    - -

    - ''' + + # 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/') +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)