From 65ffbc66e98b3c8e45277675548a4b95f62fc65e Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Tue, 27 Dec 2016 17:00:13 -0500 Subject: [PATCH 1/8] added passap deco and reworked interface --- bin/knittingtools-chkconfig | 62 + handlers/actions.py | 52 +- logging.conf | 23 + modules/pcgenerator.py | 378 +- server.py | 95 +- templates/calculator.html | 4 +- templates/pcgenerator.html | 67 +- test/deco card good one.svg | 17191 ++++++++++++++++++++++++++++++++++ test/houndstooth.svg | 1 + test/houndstooth.txt | 4 + test/test.py | 10 + 11 files changed, 17625 insertions(+), 262 deletions(-) create mode 100755 bin/knittingtools-chkconfig create mode 100644 logging.conf create mode 100644 test/deco card good one.svg create mode 100644 test/houndstooth.svg create mode 100644 test/houndstooth.txt create mode 100644 test/test.py diff --git a/bin/knittingtools-chkconfig b/bin/knittingtools-chkconfig new file mode 100755 index 0000000..4bf7c8d --- /dev/null +++ b/bin/knittingtools-chkconfig @@ -0,0 +1,62 @@ +#!/bin/bash +# +# chkconfig: 2345 75 35 +# description: knittingtools + +SERVICE=knittingtools + +# Source function library. +. /etc/init.d/functions + +lockfile=/var/lock/${SERVICE}.lock +procname=/path/to/knittingtools/venv/bin/python +scriptname=/path/to/knittingtools/server.py +pidfile=/var/run/${SERVICE}.pid + +RETVAL=0 +prog="knittingtools" + +start () { + if [ -f "$pidfile" ]; then + checkpid `cat $pidfile` && return 0 + fi + echo -n $"Starting $prog: " + $procname $scriptname 1>/dev/null 2>&1 & + RETVAL=$? + PID=$! + echo $PID + echo $PID >$pidfile + echo + [ $RETVAL -eq 0 ] && touch $lockfile + return $RETVAL +} + +stop () { + RETVAL=0 + if [ -f "${pidfile}" ]; then + echo -n $"Stopping $prog: " + killproc -p ${pidfile} $procname + RETVAL=$? + echo + fi + [ $RETVAL -eq 0 ] && rm -f $lockfile + return $RETVAL +} + +restart () { + stop + start +} + +# See how we were called. +case "$1" in + start|stop|restart) + $1 ;; + status) + status -p ${pidfile} $procname ;; + condrestart) + test ! -f $lockfile || restart ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart}" + exit 2 +esac diff --git a/handlers/actions.py b/handlers/actions.py index e85e58f..449ede8 100644 --- a/handlers/actions.py +++ b/handlers/actions.py @@ -1,5 +1,5 @@ import cgi -from os import curdir +import os import sys import traceback @@ -7,8 +7,8 @@ from modules.pcgenerator import PCGenerator def pcgenerator_get(handler): - f = open("{}/templates/{}".format( - curdir, + f = open("{}/../templates/{}".format( + os.path.dirname(os.path.realpath(__file__)), "pcgenerator.html")) try: @@ -21,6 +21,8 @@ def pcgenerator_get(handler): except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() + handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) + handler.wfile.write( "

Aw, snap! We seem to have a problem.

") handler.wfile.write( @@ -30,7 +32,6 @@ def pcgenerator_get(handler): "beebell on Ravelry. " "It will be helpful if you include the pattern you uploaded to help me " "diagnose the issue.") - handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) finally: f.close() @@ -43,21 +44,19 @@ def pcgenerator_post(handler): query=cgi.parse_multipart(handler.rfile, pdict) upfilecontent = query.get('upfile') - if len(upfilecontent[0]) > 2500: + if len(upfilecontent[0]) > 4000: handler.send_response(302) handler.send_header('Content-type', 'text/html') handler.end_headers() handler.wfile.write("Sorry. Your file cannot exceed 2500 bytes!") else: - horz_repeat = query.get('horz') + machine_type = query.get('machine') vert_repeat = query.get('vert') - cell_height = query.get('rowheight') - cell_width = query.get('colwidth') + generator = PCGenerator( + handler, upfilecontent[0], - float(cell_height[0]), - float(cell_width[0]), - int(horz_repeat[0]), + machine_type[0], int(vert_repeat[0])) result = generator.generate() @@ -70,15 +69,27 @@ def pcgenerator_post(handler): return except ValueError as e: + exc_type, exc_value, exc_traceback = sys.exc_info() + handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) + handler.send_response(302) handler.send_header('Content-type', 'text/html') handler.end_headers() handler.wfile.write( - "

Aw, snap!

") + "

Aw, snap! We seem to have a problem.

") + handler.wfile.write( + repr(traceback.format_exception(exc_type, exc_value,exc_traceback))) handler.wfile.write(e) + handler.wfile.write( + "

Please report this error via private message to " + "beebell on Ravelry. " + "It will be helpful if you include the pattern you uploaded to help me " + "diagnose the issue.") except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() + handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) + handler.send_response(302) handler.send_header('Content-type', 'text/html') handler.end_headers() @@ -91,12 +102,11 @@ def pcgenerator_post(handler): "beebell on Ravelry. " "It will be helpful if you include the pattern you uploaded to help me " "diagnose the issue.") - handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) def calculator_get(handler): - f = open("{}/templates/{}".format( - curdir, + f = open("{}/../templates/{}".format( + os.path.dirname(os.path.realpath(__file__)), "calculator.html")) try: @@ -109,6 +119,8 @@ def calculator_get(handler): except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() + handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) + handler.wfile.write( "

Aw, snap! We seem to have a problem.

") handler.wfile.write( @@ -118,15 +130,14 @@ def calculator_get(handler): "beebell on Ravelry. " "It will be helpful if you include the pattern you uploaded to help me " "diagnose the issue.") - handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) finally: f.close() def index_get(handler): - f = open("{}/templates/{}".format( - curdir, - "index.html")) + f = open("{}/../templates/{}".format( + os.path.dirname(os.path.realpath(__file__)), + "index.html")) try: handler.send_response(200) @@ -138,6 +149,8 @@ def index_get(handler): except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() + handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) + handler.wfile.write( "

Aw, snap! We seem to have a problem.

") handler.wfile.write( @@ -147,7 +160,6 @@ def index_get(handler): "beebell on Ravelry. " "It will be helpful if you include the pattern you uploaded to help me " "diagnose the issue.") - handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback)) finally: f.close() diff --git a/logging.conf b/logging.conf new file mode 100644 index 0000000..bb15fac --- /dev/null +++ b/logging.conf @@ -0,0 +1,23 @@ +[loggers] +keys=root + +[handlers] +keys=fileHandler + +[formatters] +keys=fileFormatter + +[logger_root] +level=DEBUG +handlers=fileHandler + +[handler_fileHandler] +args=('/var/log/knittingtools.log', 'a') +backupCount: 7 +class=logging.handlers.RotatingFileHandler +formatter=fileFormatter +level=DEBUG + +[formatter_fileFormatter] +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s +datefmt= diff --git a/modules/pcgenerator.py b/modules/pcgenerator.py index b32520d..8ab7d47 100644 --- a/modules/pcgenerator.py +++ b/modules/pcgenerator.py @@ -1,70 +1,130 @@ import svgwrite -# default values -# number of overlapping blank rows at the top of the card -blank_rows = 2 +# machine specs +specs = { + '12-stitch-br-sr': { + 'card_width': 142, + 'blank_rows': 2, + 'row_height': 5.0, + 'stitch_width': 9.0, + 'pattern_hole_radius': 3.5, + 'pattern_hole_xoffset': 22.5, + 'clip_hole_radius': 3.5, + 'clip_hole_xoffset': 5.0, + 'clip_hole_yoffset': 5.0, + 'tractor_hole_radius': 3.0, + 'tractor_hole_xoffset': 12.5, + 'tractor_hole_yoffset': 2.5, + 'stitches': 12, + 'corner_offset': 2, + }, + '24-stitch-br-sr': { + 'card_width': 142, + 'blank_rows': 2, + 'row_height': 5.0, + 'stitch_width': 4.5, + 'pattern_hole_radius': 3.5, + 'pattern_hole_xoffset': 17.5, + 'clip_hole_radius': 3.5, + 'clip_hole_xoffset': 5.0, + 'clip_hole_yoffset': 5.0, + 'tractor_hole_radius': 3.0, + 'tractor_hole_xoffset': 12.0, + 'tractor_hole_yoffset': 2.5, + 'stitches': 24, + 'corner_offset': 2, + }, + '40-stitch-deco': { + 'card_width': 242, + 'blank_rows': 3, + 'row_height': (305.0 / 58.0), + 'stitch_width': 5.0, + 'pattern_hole_radius': 3.0, + 'pattern_hole_xoffset': 22.0, + 'clip_hole_radius': 3.0, + 'clip_hole_xoffset': 2.0, + 'clip_hole_yoffset': 5.5, + 'tractor_hole_radius': 2.5, + 'tractor_hole_xoffset': 12.5, + 'tractor_hole_yoffset': 5.5, + 'stitches': 40, + 'corner_offset': 0, + }, +} -# width of the side margin in mm -side_margin = 17.0 -# height of one row on the card in mm -row_height = 5.0 +class Layout: -# width of one stitch on the card in mm -stitch_width = 4.5 -#stitch_width = 9.0 + def __init__(self, machine_id, stitches, rows, horz_repeat, vert_repeat): -# radius of a pattern hole in mm -pattern_hole_radius = 3.5 + # total width of the cut card + self.card_width = specs[machine_id]['card_width'] -# radius of a clip hole in mm -clip_hole_radius = 3.0 + # number of overlapping blank rows at the top of the card + self.blank_rows = specs[machine_id]['blank_rows'] -# radius of a sprocket hole in mm -sprocket_hole_radius = 3.5 + # height of one row on the card in mm + self.row_height = specs[machine_id]['row_height'] -# drawing stroke width -stroke_width='.1' + # width of one stitch on the card in mm + self.stitch_width = specs[machine_id]['stitch_width'] -# fill color -fill_color = 'white' + # radius of a pattern hole in mm + self.pattern_hole_radius = specs[machine_id]['pattern_hole_radius'] -# stroke_color -stroke_color = 'black' + # offset of the first pattern hole from the left edge of the card in mm + self.pattern_hole_xoffset = specs[machine_id]['pattern_hole_xoffset'] -card_width = 0 -card_height = 0 -card_rows = 0 -card_stitches = 0 + # radius of a clip hole in mm + self.clip_hole_radius = specs[machine_id]['clip_hole_radius'] -class PCGenerator: + # offset of a clip hole from the left/right edge of the card in mm + self.clip_hole_xoffset = specs[machine_id]['clip_hole_xoffset'] - def __init__(self, data, cell_height, cell_width, horz_repeat, vert_repeat): - global row_height - global stitch_width + # offset of a clip hole from the top/bottom edges of the card in mm + self.clip_hole_yoffset = specs[machine_id]['clip_hole_yoffset'] - self.data = data.split() - self.horz_repeat = horz_repeat - self.vert_repeat = vert_repeat - row_height = cell_height - stitch_width = cell_width + # radius of a tractor hole in mm + self.tractor_hole_radius = specs[machine_id]['tractor_hole_radius'] - def generate(self): - global card_rows - global card_stitches - global card_width - global card_height + # offset of a tractor hole from the left/right edge of the card in mm + self.tractor_hole_xoffset = specs[machine_id]['tractor_hole_xoffset'] - card_rows = len(self.data) - card_stitches = len(self.data[0]) - if card_rows > 200 or card_stitches > 30: + # offset of a tractor hole from the top/bottom edge of the card in mm + self.tractor_hole_yoffset = specs[machine_id]['tractor_hole_yoffset'] + + self.corner_offset = specs[machine_id]['corner_offset'] + + self.card_stitches = stitches + self.card_rows = rows + + if self.card_rows > 200 or self.card_stitches > 30: raise ValueError( "Your pattern seems to exceed 200 rows and/or 30 stitches. " "Are you sure you uploaded the right text file?") - card_width = (side_margin * 2) + (card_stitches * self.horz_repeat * stitch_width) - card_height = ((blank_rows * 2) + (card_rows * self.vert_repeat)) * row_height + self.horz_repeat = horz_repeat + self.vert_repeat = vert_repeat + + self.card_height = ((self.blank_rows * 2) + (self.card_rows * self.vert_repeat)) * self.row_height + + +class PCGenerator: + + def __init__(self, handler, data, machine_id, vert_repeat): + + self.handler = handler + self.data = data.split() + self.layout = Layout( + machine_id, + len(self.data[0]), + len(self.data), + specs[machine_id]['stitches'] / len(self.data[0]), + vert_repeat + ) + + def generate(self): diagram = self.create_card() @@ -72,7 +132,7 @@ class PCGenerator: self.draw_pattern(diagram, self.data, objects) self.draw_blank_lines(diagram, objects) self.draw_clip_holes(diagram, objects) - self.draw_sprocket_holes(diagram, objects) + self.draw_tractor_holes(diagram, objects) # sort the list to optimize cutting sorted_objects = sorted(objects, key=lambda x: (float(x.attribs['cy']), float(x.attribs['cx']))) @@ -82,163 +142,133 @@ class PCGenerator: return diagram.tostring() def create_card(self): - global card_width - global card_height - + diagram = svgwrite.Drawing( "punchcard.svg", size=( - '{0}mm'.format(card_width), - '{0}mm'.format(card_height)), + '{0}mm'.format(self.layout.card_width), + '{0}mm'.format(self.layout.card_height)), viewBox=( - '0 0 {0} {1}'.format(card_width, card_height)), + '0 0 {0} {1}'.format(self.layout.card_width, self.layout.card_height)), preserveAspectRatio='none') - shape_points = [ - (2, 0), - (card_width-2, 0), - (card_width-1, 1), - (card_width-1, 20), - (card_width, 22), - (card_width, card_height-22), - (card_width-1, card_height-20), - (card_width-1, card_height-1), - (card_width-2, card_height), - (2, card_height), - (1, card_height-1), - (1, card_height-20), - (0, card_height-22), - (0, 22), - (1, 20), - (1, 1)] diagram.add(diagram.polygon( - points=shape_points, - fill=fill_color, - stroke=stroke_color, - stroke_width=stroke_width)) + points=self.get_card_shape(), + fill='white', + stroke='black', + stroke_width=.1)) return diagram def draw_pattern(self, diagram, lines, objects): - global card_rows - global card_stitches - global fill_color - global pattern_hole_radius - global row_color - global row_height - global side_margin - global stitch_width - global stroke_color - global stroke_width - + # main body of card - yoffset = 10.0 + (row_height / 2) - for row_repeat in range(self.vert_repeat): - for rows in range(card_rows): - xoffset = side_margin + (stitch_width / 2) - for stitch_repeat in range(self.horz_repeat): - for stitches in range(card_stitches): + yoffset = (self.layout.blank_rows * self.layout.row_height) + (self.layout.row_height / 2) + for row_repeat in range(self.layout.vert_repeat): + for rows in range(self.layout.card_rows): + xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2) + for stitch_repeat in range(self.layout.horz_repeat): + for stitches in range(self.layout.card_stitches): if lines[rows][stitches].upper() == 'X': objects.append(diagram.circle( center=(xoffset, yoffset), - fill=fill_color, - r = (pattern_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - xoffset += stitch_width - yoffset += row_height + fill='white', + r = (self.layout.pattern_hole_radius / 2), + stroke='black', + stroke_width=.1)) + xoffset += self.layout.stitch_width + yoffset += self.layout.row_height def draw_blank_lines(self, diagram, objects): - global blank_rows - global card_stitches - global fill_color - global pattern_hole_radius - global row_height - global side_margin - global stitch_width - global stroke_color - global stroke_width - + # blank rows at top - yoffset = row_height / 2 - for rows in range(blank_rows): - xoffset = side_margin + (stitch_width / 2) - for stitch_repeat in range(self.horz_repeat): - for stitches in range(card_stitches): + yoffset = self.layout.row_height / 2 + for rows in range(self.layout.blank_rows): + xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2) + for stitch_repeat in range(self.layout.horz_repeat): + for stitches in range(self.layout.card_stitches): objects.append(diagram.circle( center=(xoffset, yoffset), - fill=fill_color, - r = (pattern_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - xoffset += stitch_width - yoffset += row_height + fill='white', + r = (self.layout.pattern_hole_radius / 2), + stroke='black', + stroke_width=.1)) + xoffset += self.layout.stitch_width + yoffset += self.layout.row_height # blank rows at bottom - yoffset = (card_height - (row_height * blank_rows)) + (row_height / 2) - for rows in range(blank_rows): - xoffset = side_margin + (stitch_width / 2) - for stitch_repeat in range(self.horz_repeat): - for stitches in range(card_stitches): + yoffset = (self.layout.card_height - (self.layout.row_height * self.layout.blank_rows)) + (self.layout.row_height / 2) + for rows in range(self.layout.blank_rows): + xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2) + for stitch_repeat in range(self.layout.horz_repeat): + for stitches in range(self.layout.card_stitches): objects.append(diagram.circle( center=(xoffset, yoffset), - fill=fill_color, - r = (pattern_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - xoffset += stitch_width - yoffset += row_height + fill='white', + r = (self.layout.pattern_hole_radius / 2), + stroke='black', + stroke_width=.1)) + xoffset += self.layout.stitch_width + yoffset += self.layout.row_height def draw_clip_holes(self, diagram, objects): - global card_height - global clip_hole_radius - global fill_color - global row_height - global side_margin - global stitch_width - global stroke_color - global stroke_width - - left_xoffset = side_margin + (stitch_width / 2) - 6.0 - right_xoffset = (card_width - side_margin - (stitch_width / 2)) + 6.0 - yoffset = row_height / 2 - while yoffset < card_height: - # clip holes on left + self.draw_side_holes( + diagram, + objects, + self.layout.clip_hole_xoffset, + self.layout.clip_hole_yoffset, + self.layout.clip_hole_radius) + + def draw_tractor_holes(self, diagram, objects): + + self.draw_side_holes( + diagram, + objects, + self.layout.tractor_hole_xoffset, + self.layout.tractor_hole_yoffset, + self.layout.tractor_hole_radius) + + def draw_side_holes(self, diagram, objects, xoffset, yoffset, radius): + + left_xoffset = xoffset + (radius / 2) + right_xoffset = self.layout.card_width - left_xoffset + + while yoffset < self.layout.card_height: + # holes on left objects.append(diagram.circle( center=(left_xoffset, yoffset), - fill=fill_color, - r = (clip_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - # clip holes on right + fill='white', + r = (radius / 2), + stroke='black', + stroke_width=.1)) + # holes on right objects.append(diagram.circle( center=(right_xoffset, yoffset), - fill=fill_color, - r = (clip_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - yoffset += row_height + fill='white', + r = (radius / 2), + stroke='black', + stroke_width=.1)) + yoffset += self.layout.row_height - def draw_sprocket_holes(self, diagram, objects): - - left_xoffset = 6.5 - right_xoffset = card_width - 6.5 - yoffset = row_height - for row_repeat in range(self.vert_repeat): - for rows in range(((card_rows * self.vert_repeat) + (blank_rows * 2)) / 2): - # sprocket holes on left - objects.append(diagram.circle( - center=(left_xoffset, yoffset), - fill=fill_color, - r = (sprocket_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - # sprocket holes on left - objects.append(diagram.circle( - center=(right_xoffset, yoffset), - fill=fill_color, - r = (sprocket_hole_radius / 2), - stroke=stroke_color, - stroke_width=stroke_width)) - yoffset += (row_height * 2) + def get_card_shape(self): + + corner_radius = self.layout.corner_offset + 1 + + return [ + (corner_radius, 0), + (self.layout.card_width - corner_radius, 0), + (self.layout.card_width - self.layout.corner_offset, self.layout.corner_offset), + (self.layout.card_width - self.layout.corner_offset, 20), + (self.layout.card_width, 22), + (self.layout.card_width, self.layout.card_height - 22), + (self.layout.card_width - self.layout.corner_offset, self.layout.card_height - 20), + (self.layout.card_width - self.layout.corner_offset, self.layout.card_height - 1), + (self.layout.card_width - corner_radius, self.layout.card_height), + (corner_radius, self.layout.card_height), + ( self.layout.corner_offset, self.layout.card_height - self.layout.corner_offset), + ( self.layout.corner_offset, self.layout.card_height - 20), + (0, self.layout.card_height - 22), + (0, 22), + ( self.layout.corner_offset, 20), + ( self.layout.corner_offset, 1)] diff --git a/server.py b/server.py index d89b62c..8cae9f6 100644 --- a/server.py +++ b/server.py @@ -1,9 +1,14 @@ +import logging +import logging.config +import os +import sys +import traceback + from BaseHTTPServer import BaseHTTPRequestHandler from BaseHTTPServer import HTTPServer from handlers import actions - pcgenerator_actions = { 'get': actions.pcgenerator_get, 'post': actions.pcgenerator_post } @@ -23,44 +28,86 @@ template_map = { '/pcgenerator/': pcgenerator_actions, '/calculator': calculator_actions, '/calculator/': calculator_actions, - '/': index_actions, - '/index': index_actions } + '/knittingtools': index_actions, + '/knittingtools/': index_actions, + '/index': index_actions, + '/index/': index_actions, + '/': index_actions} +logging.config.fileConfig("{0}/{1}".format(os.path.dirname(os.path.realpath(__file__)), 'logging.conf')) + +logger = logging.getLogger('root') class MyHandler(BaseHTTPRequestHandler): - def do_GET(self): - print self.path - actions = template_map.get(self.path, None) - if actions is None: - self.send_response(404) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write('Not found!') + def handle_not_found(self): + self.send_response(404) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write( + "

Aw, snap! We seem to have a problem.

") + self.wfile.write('The request resource was not found on this server.') - actions['get'](self) + def do_GET(self): + try: + actions = template_map.get(self.path, None) + if actions is None: + self.handle_not_found() + return + + actions['get'](self) + except Exception: + exc_type, exc_value, exc_traceback = sys.exc_info() + self.log_error("%s %s\n" % ( + exc_type, + exc_value)) + logger.debug("path=%s %s", + self.path, + repr(traceback.format_exception(exc_type, exc_value,exc_traceback))) def do_POST(self): - print self.path - actions = template_map.get(self.path, None) - if actions is None: - self.send_response(404) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write('Not found!') + try: + actions = template_map.get(self.path, None) + if actions is None: + self.handle_not_found() + return - actions['post'](self) + actions['post'](self) + except Exception: + exc_type, exc_value, exc_traceback = sys.exc_info() + self.log_error("%s %s\n" % ( + exc_type, + exc_value)) + logger.debug("path=%s %s", + self.path, + repr(traceback.format_exception(exc_type, exc_value, exc_traceback))) + def log_request(self, code=None, size=None): + logger.info("%s - - [%s] %s %s %s" % ( + self.headers.get('X-Forwarded-For', None) or self.address_string(), + self.log_date_time_string(), + self.requestline, + code or '-', + size or '-')) + + def log_error(self, *args): + logger.error(args) + + def log_message(self, *args): + logger.info("%s - - [%s] %s" % ( + self.headers.get('X-Forwarded-For', None) or self.address_string(), + self.log_date_time_string(), + args)) def main(): try: - server = HTTPServer(('', 8088), MyHandler) - print 'started httpserver...' + logger.info("Starting server...") + server = HTTPServer(('', 8080), MyHandler) server.serve_forever() except KeyboardInterrupt: - print '^C received, shutting down server' + logger.info("Stopping server...") except Exception: - print sys.exc_info()[0] + logger.error(sys.exc_info()[0]) finally: server.socket.close() diff --git a/templates/calculator.html b/templates/calculator.html index 6f30658..f2c4a8f 100644 --- a/templates/calculator.html +++ b/templates/calculator.html @@ -28,11 +28,12 @@

Knitting Gauge Calculator

- <Back> + [Back To Tools Home] [Back To My Blog]

This app will help you calculate stitches and rows when your gauge differs from the gauge specified on the pattern.

The forms are reactive. I.e., when you enter a value in any field, the other fields will automatically be adjusted for the value you entered. For example, if you enter inches in the "Imperial" column, the equivalent value in centimeters will automatically displayed in the "Metric" column. +

IMPORTANT!!! This app is currently considered Beta. As such, there may be bugs. If you see something that doesn't look right, please report the issue using the instructions at the bottom of this page.


@@ -198,6 +199,7 @@

After you compute the number of stitches or rows required for your gauge, consult the pattern to see if knitting a different size will produce the desired results.

For example, if the pattern instructs you to cast on 80 stitches for a size medium but your gauge requires you to cast on 90 stitches to achieve the same width, look at the pattern to see if the next larger size specifies a cast on that's closer to 90 stitches. Knitting a larger size at a smaller gauge (or smaller size at a larger gauge) may produce the same result. +

Bugs & Disclaimers

If you have questions or feedback, please contact beebell on Ravelry via private message or log at issue on GitHub.

diff --git a/templates/pcgenerator.html b/templates/pcgenerator.html index 778dba2..19f8bdf 100644 --- a/templates/pcgenerator.html +++ b/templates/pcgenerator.html @@ -24,16 +24,19 @@

Punchcard Generator

- <Back> + [Back To Tools Home] [Back To My Blog]
+

NOTICE: I'm in the process of testing a new version... things might be unstable for a bit... +

This app will allow you to upload a small text file that contains your pattern represented as a sequence of rows with X's for punched holes and any other character except space for unpunched holes. The program will generate an SVG file that can be imported into the software you use to cut material with your die cutter. +

IMPORTANT!!! This app is currently considered Beta. As such, there may be bugs. If you see something that doesn't look right, please report the issue using the instructions at the bottom of this page.

TL;DR

  • Select the file to upload.
  • -
  • Enter your punchcard row height and column width in millimeters.
  • -
  • Enter horizontal and vertical repeats if you need them.
  • +
  • Select your machine type.
  • +
  • Enter the # vertical repeats if you need them. The # horizontal repeats will be automatically calculated based on the machine type.
  • Click the Upload button.
In a few seconds, you should see a dialog prompting you for where you want to save your file. If you need more detailed instructions, keep reading below. @@ -43,31 +46,27 @@ method="POST" enctype="multipart/form-data" action="/pcgenerator" - onfocus="x.value=horz.value; y.value=vert.value" - oninput="x.value=horz.value; y.value=vert.value"> + onfocus="y.value=vert.value" + oninput="y.value=vert.value"> - + - - + + - - - - - - - - - - + @@ -95,9 +94,11 @@ ---x-----x-- -

By default, the generator will do 1 horizontal repeat and 1 vertical repeat. I.e., if your file contains 36 rows with 24 characters per row, the generated file will produce a 24 row x 36 stitch card. You can increase the width and/or length by adjusting the horizontal and/or vertical repeat before clicking the upload button. +

The generator will determine the number of horizontal repeats based on the number of characters in your first row and the type of machine. I.e., if row 1 contains 4 characters and you select a 12-stitch machine, the generator will automatically do 3 horizontal repeats: 4 * 3 = 12. -

What this means is that you can design a simple 4x4 card and use repeats to make the card as wide or as long as it needs to be in order to work properly. For example, the following text file will generate a 24 row x 48 stitch card if you specify 6 for the horizontal repeat and 12 for the vertical repeat: +

By default, the generator will do 1 vertical repeat. I.e., if your file contains 36 rows, the generated file will produce a 36 row card. You can increase the length by adjusting the vertical repeat before clicking the upload button. + +

What this means is that you can design a simple 4x4 card and use repeats to make the card as wide or as long as it needs to be in order to work properly. For example, the following text file will generate a 24 row x 48 stitch card if you select a 24-stitch machine and specify 12 for the vertical repeat:

 	x-x-
@@ -106,29 +107,9 @@
 	-x-x
 		
-

You'll need to specify the height and width of 1 row and 1 column on the punchcard. You can determine the correct values by measuring the size of the squares on a card that works with your knitting machine. If your card doesn't have squares, you can measure the distance between the centers of two adjacent holes. For reference: - -

-

Select file to upload:
 

(maximum file size 2500 characters)

(maximum file size 4000 characters)
Height of 1 punchcard row (mm):Select machine type:
 
+ +
Width of 1 punchcard column (mm):
# Horizontal repeats (up to 12): - -
# Vertical repeats (up to 12): + # Vertical repeats (up to 24):
- - - - - - - - - - - - - - - -
Knitting MachineRow Height
(mm)
Column Width
(mm)
Brother 24-Stitch Standard54.5
Brother 12-Stitch Bulky59
-

The resulting SVG file can be imported into any application that understands SVG. You can also display the SVG in some browsers. Neither the text file or resulting SVG will be saved on this site. Be sure to keep them in a safe location so they don't get lost. +

Bugs & Disclaimers

This program has only been tested using Sure Cuts A Lot on a Pazzles Creative Mighty. YMMV!

If you have questions or feedback, please contact beebell on Ravelry via private message or log at issue on GitHub.

diff --git a/test/deco card good one.svg b/test/deco card good one.svg new file mode 100644 index 0000000..2635ab5 --- /dev/null +++ b/test/deco card good one.svg @@ -0,0 +1,17191 @@ + + + + + + circle + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.06350295;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="circle9580" + cx="204.1756" + cy="1052.3622" + r="6.1690359" /> + + +" + inkscape:color="rgb(0,0,255)" /> + +" + inkscape:color="rgb(0,0,255)" /> + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'Segoe Script';-inkscape-font-specification:'Segoe Script, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="54.349121" + y="448.54489" + id="text32225" + sodipodi:linespacing="125%">56 + + + 0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16 + 18 + 20 + 22 + 24 + 26 + 28 + 30 + 32 + 34 + 36 + 38 + 40 + 42 + 44 + 46 + 48 + 50 + 52 + 54 + 56 + 58 + 60 + 62 + 64 + 66 + 68 + 70 + 72 + 74 + 76 + 78 + 80 + 82 + 84 + 86 + 88 + 90 + + 92 + 94 + 96 + 98 + 100 + 102 + 104 + 106 + 108 + 110 + 0 + 4 + 8 + 12 + 16 + 20 + 24 + 28 + 32 + 36 + 40 + 44 + 48 + 52 + 56 + 52 + 56 + 60 + 64 + 68 + 72 + 76 + 80 + 84 + 88 + 92 + 96 + 0 + 4 + 8 + 12 + 16 + 20 + 24 + 28 + 32 + 36 + 40 + 44 + 48 + 60 + 64 + 68 + 72 + 76 + 80 + 84 + 88 + 92 + 96 + +Deco Cards +Deco Cards + + + + text + sodipodi:linespacing="125%" + id="text32345" + y="897.60223" + x="53.172119" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'Segoe Script';-inkscape-font-specification:'Segoe Script, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve">52 + diff --git a/test/houndstooth.svg b/test/houndstooth.svg new file mode 100644 index 0000000..3777dca --- /dev/null +++ b/test/houndstooth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/houndstooth.txt b/test/houndstooth.txt new file mode 100644 index 0000000..d72f22b --- /dev/null +++ b/test/houndstooth.txt @@ -0,0 +1,4 @@ +-x-- +-xxx +xxx- +--x- \ No newline at end of file diff --git a/test/test.py b/test/test.py new file mode 100644 index 0000000..ebc41d6 --- /dev/null +++ b/test/test.py @@ -0,0 +1,10 @@ +from pcgenerator import PCGenerator +pattern='-x--\n-xxx\nxxx-\n--x-\n' +machine = '12-stitch-br-sr' +#machine = '24-stitch-br-sr' +#machine = '40-stitch-deco' +generator = PCGenerator(None, pattern, machine, 10) +result = generator.generate() +text_file = open("{}.svg".format(machine), "w") +text_file.write(result) +text_file.close() From aa548f2dc20cfcf6c81fe22f87c887c6f2b6215e Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Tue, 27 Dec 2016 17:05:28 -0500 Subject: [PATCH 2/8] Accidentally reverted port change --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 8cae9f6..2591a4b 100644 --- a/server.py +++ b/server.py @@ -102,7 +102,7 @@ class MyHandler(BaseHTTPRequestHandler): def main(): try: logger.info("Starting server...") - server = HTTPServer(('', 8080), MyHandler) + server = HTTPServer(('', 8088), MyHandler) server.serve_forever() except KeyboardInterrupt: logger.info("Stopping server...") From edda8306f3f9b1a35214c7c71adaf9bf67227c10 Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Tue, 27 Dec 2016 17:12:50 -0500 Subject: [PATCH 3/8] Back to port 8080 --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 2591a4b..8cae9f6 100644 --- a/server.py +++ b/server.py @@ -102,7 +102,7 @@ class MyHandler(BaseHTTPRequestHandler): def main(): try: logger.info("Starting server...") - server = HTTPServer(('', 8088), MyHandler) + server = HTTPServer(('', 8080), MyHandler) server.serve_forever() except KeyboardInterrupt: logger.info("Stopping server...") From 4a3387c5eafcda87002e43299662f3d03a4a3535 Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Tue, 27 Dec 2016 17:31:18 -0500 Subject: [PATCH 4/8] Fixed notice --- templates/pcgenerator.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/pcgenerator.html b/templates/pcgenerator.html index 19f8bdf..35f3d2c 100644 --- a/templates/pcgenerator.html +++ b/templates/pcgenerator.html @@ -27,8 +27,6 @@ [Back To Tools Home] [Back To My Blog]
-

NOTICE: I'm in the process of testing a new version... things might be unstable for a bit... -

This app will allow you to upload a small text file that contains your pattern represented as a sequence of rows with X's for punched holes and any other character except space for unpunched holes. The program will generate an SVG file that can be imported into the software you use to cut material with your die cutter.

IMPORTANT!!! This app is currently considered Beta. As such, there may be bugs. If you see something that doesn't look right, please report the issue using the instructions at the bottom of this page. @@ -62,7 +60,8 @@ - +

NOTICE: Passap Deco support is currently experimental and has not been fully tested. + # Vertical repeats (up to 24): From a12b68c043cc3f9b44409c30aba7a899f2615b9b Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Wed, 28 Dec 2016 10:29:50 -0500 Subject: [PATCH 5/8] added xml prolog --- __init__.py | 1 + modules/pcgenerator.py | 2 +- test/__init__.py | 1 + test/test.py | 5 ++++- 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 __init__.py create mode 100644 test/__init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..636bc1a --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +# intentionally empty diff --git a/modules/pcgenerator.py b/modules/pcgenerator.py index 8ab7d47..157ca2d 100644 --- a/modules/pcgenerator.py +++ b/modules/pcgenerator.py @@ -139,7 +139,7 @@ class PCGenerator: for i in sorted_objects: diagram.add(i) - return diagram.tostring() + return '{}'.format(diagram.tostring()) def create_card(self): diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..636bc1a --- /dev/null +++ b/test/__init__.py @@ -0,0 +1 @@ +# intentionally empty diff --git a/test/test.py b/test/test.py index ebc41d6..9f4f0ce 100644 --- a/test/test.py +++ b/test/test.py @@ -1,4 +1,7 @@ -from pcgenerator import PCGenerator +# invocation: python -m test.test + +from modules.pcgenerator import PCGenerator + pattern='-x--\n-xxx\nxxx-\n--x-\n' machine = '12-stitch-br-sr' #machine = '24-stitch-br-sr' From 50e8f44f74f86fe508ea4a5ded3626161c9fdcc5 Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Wed, 28 Dec 2016 10:31:27 -0500 Subject: [PATCH 6/8] removed test file --- test/houndstooth.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/houndstooth.svg diff --git a/test/houndstooth.svg b/test/houndstooth.svg deleted file mode 100644 index 3777dca..0000000 --- a/test/houndstooth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 90a65b4e4902e9d946796075bbb5b1ac923aac4a Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Thu, 29 Dec 2016 12:35:18 -0500 Subject: [PATCH 7/8] Added png conversion to test --- test/test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test.py b/test/test.py index 9f4f0ce..73caa26 100644 --- a/test/test.py +++ b/test/test.py @@ -1,13 +1,17 @@ # invocation: python -m test.test from modules.pcgenerator import PCGenerator +import cairosvg pattern='-x--\n-xxx\nxxx-\n--x-\n' -machine = '12-stitch-br-sr' +#machine = '12-stitch-br-sr' #machine = '24-stitch-br-sr' -#machine = '40-stitch-deco' +machine = '40-stitch-deco' generator = PCGenerator(None, pattern, machine, 10) result = generator.generate() text_file = open("{}.svg".format(machine), "w") text_file.write(result) text_file.close() +png_file = open("{}.png".format(machine), "w") +cairosvg.svg2png(bytestring=result,write_to=png_file) +png_file.close() From 623745be7d662bb07c23c0e40a357b161096c7a5 Mon Sep 17 00:00:00 2001 From: Brenda Bell Date: Thu, 29 Dec 2016 14:50:42 -0500 Subject: [PATCH 8/8] Added support for PNG files --- handlers/actions.py | 17 ++++++++++++++--- templates/pcgenerator.html | 7 +++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/handlers/actions.py b/handlers/actions.py index 449ede8..e9762c6 100644 --- a/handlers/actions.py +++ b/handlers/actions.py @@ -1,6 +1,8 @@ +import cairosvg import cgi import os import sys +import time import traceback from modules.pcgenerator import PCGenerator @@ -39,7 +41,7 @@ def pcgenerator_get(handler): def pcgenerator_post(handler): try: - ctype, pdict = cgi.parse_header(handler.headers.getheader('content-type')) + ctype, pdict = cgi.parse_header(handler.headers.getheader('Content-Type')) if ctype == 'multipart/form-data': query=cgi.parse_multipart(handler.rfile, pdict) @@ -52,6 +54,7 @@ def pcgenerator_post(handler): else: machine_type = query.get('machine') vert_repeat = query.get('vert') + convert_to_png = query.get('png', [''])[0] == 'png' generator = PCGenerator( handler, @@ -61,8 +64,16 @@ def pcgenerator_post(handler): result = generator.generate() handler.send_response(200) - handler.send_header('Content-type', 'image/svg+xml') - handler.send_header("Content-Disposition", "attachment; filename=punchcard.svg") + filename_template = 'attachment; filename="punchcard-{}.{}"' + + if convert_to_png: + result = cairosvg.svg2png(bytestring=result) + handler.send_header('Content-type', 'image/png') + handler.send_header('Content-Disposition', filename_template.format(int(time.time()), "png")) + else: + handler.send_header('Content-type', 'image/svg+xml') + handler.send_header('Content-Disposition', filename_template.format(int(time.time()), "svg")) + handler.end_headers() handler.wfile.write(result) diff --git a/templates/pcgenerator.html b/templates/pcgenerator.html index 35f3d2c..01cd5ff 100644 --- a/templates/pcgenerator.html +++ b/templates/pcgenerator.html @@ -35,6 +35,7 @@

  • Select the file to upload.
  • Select your machine type.
  • Enter the # vertical repeats if you need them. The # horizontal repeats will be automatically calculated based on the machine type.
  • +
  • Check "Convert to PNG" if you'd like to download a PNG image instead of SVG.
  • Click the Upload button.
  • In a few seconds, you should see a dialog prompting you for where you want to save your file. If you need more detailed instructions, keep reading below. @@ -69,6 +70,10 @@ + + Convert to PNG image: + + @@ -108,6 +113,8 @@

    The resulting SVG file can be imported into any application that understands SVG. You can also display the SVG in some browsers. Neither the text file or resulting SVG will be saved on this site. Be sure to keep them in a safe location so they don't get lost. +

    Silhouette users have reported issues importing SVG files or having to resize the SVG in Inkscape before importing. If you have trouble with the SVG file, try checking the Convert To PNG button to see if that helps. +

    Bugs & Disclaimers

    This program has only been tested using Sure Cuts A Lot on a Pazzles Creative Mighty. YMMV!