diff --git a/diploma.py b/diploma.py index 864652e..d7a516e 100644 --- a/diploma.py +++ b/diploma.py @@ -78,7 +78,7 @@ def create_diploma_image(template): for name, field in template.fields.items(): draw_centered_full_size(context, base_template.size, field) - if template.signature is not None: + if template.signature and template.signature.value: draw_scaled_signature(text, base_template.size, template.signature) return Image.alpha_composite(base_template, text) @@ -109,7 +109,7 @@ def create_signature_preview(signature, size): def generate_diploma(template_name, **fields): - t = import_templates('templates/templates.json')[template_name] + t = import_templates()[template_name] path = fields.pop('signature', None) if path and t.signature is not None: @@ -125,7 +125,7 @@ def generate_diploma(template_name, **fields): def preview_template(template_name, size): - template = import_templates('templates/templates.json')[template_name] + template = import_templates()[template_name] return create_template_preview(template, size) diff --git a/server.py b/server.py index 3715ebd..05c65ec 100644 --- a/server.py +++ b/server.py @@ -1,12 +1,15 @@ from io import BytesIO from flask import Flask, send_file, request from json import dumps -from diploma import generate_diploma, UnknownFields, MissingFields, preview_template, preview_signature -from template import valid_template_names, BadSignature +from diploma import generate_diploma, UnknownFields, \ + MissingFields, preview_template, preview_signature +from template import import_templates, BadSignature, \ + frontend_templates_as_json, frontend_signatures_as_json import os app = Flask(__name__) + def get_size(width, height): if not (width or height): size = None @@ -32,7 +35,12 @@ def serve_image(diploma_image): @app.route('/.json') def serve_json_file(data): - return send_file(f"{data}/{data}.json") + if data == "templates": + return frontend_templates_as_json() + elif data == "signatures": + return frontend_signatures_as_json() + + return my404('not found') @app.route('/preview/') @@ -43,9 +51,9 @@ def serve_preview_image(filename): signature = f"signatures/{filename}.png" - if filename in valid_template_names(): + if filename in import_templates(): return serve_image(preview_template(filename, size)) - elif os.path.exists(signature): + if os.path.exists(signature): return serve_image(preview_signature(signature, size)) else: return my404('whoops') @@ -54,8 +62,6 @@ def serve_preview_image(filename): @app.route('/') def serve_diploma(template_name): template_name = template_name.lower() - if template_name not in valid_template_names(): - return my404('whoops') kwargs = {k: ' '.join(v) for k, v in dict(request.args).items()} finished_diploma = generate_diploma(template_name, **kwargs) diff --git a/signatures/signatures.json b/signatures/signatures.json index 5bef4f5..b0d9dd0 100644 --- a/signatures/signatures.json +++ b/signatures/signatures.json @@ -1,20 +1,16 @@ [{ - "name": "Gavin Belson", - "path": "gavinbelson.png", - "description": "Visionary behind 'The Box 3'" + "id": "gavinbelson", + "name": "Gavin Belson" }, { - "name": "Walt Disney", - "path": "waltdisney.png", - "description": "Old-timey nazi sympathizer" + "id": "waltdisney", + "name": "Walt Disney" }, { - "name": "Donald Trump", - "path": "donaldtrump.png", - "description": "Nazi sympathizer" + "id": "donaldtrump", + "name": "Donald Trump" }, { - "name": "Harry Potter", - "path": "harrypotter.png", - "description": "The boy who lived" + "id": "harrypotter", + "name": "Harry Potter" }] diff --git a/template.py b/template.py index 55d4234..471788f 100644 --- a/template.py +++ b/template.py @@ -1,13 +1,18 @@ -from json import loads +from json import loads, dumps import os +TEMPLATE_FOLDER = 'templates' +TEMPLATE_FILE = 'templates.json' +SIGNATURE_FOLDER = 'signatures' +SIGNATURE_FILE = 'signatures.json' + class BadSignature(ValueError): pass class Field(object): - def __init__(self, name, color, x, y, w, h): + def __init__(self, color, x, y, w, h): self.color = color self.value = "" self.x = x @@ -15,23 +20,17 @@ def __init__(self, name, color, x, y, w, h): self.w = w self.h = h - @classmethod - def fromdict(cls, field_dict): - return cls(**field_dict) - def copy(self): return self.__class__(None, self.color, self.x, self.y, self.w, self.h) class Template(object): - def __init__(self, name, path, fields, signature=None, dirpath=''): - self.name = name - self.path = os.path.join(dirpath, path) - self.fields = { - field_dict['name'].lower(): Field.fromdict(field_dict) - for field_dict in fields - } - self._signature = Field.fromdict(signature) if signature else None + def __init__(self, path, fields, signature=None): + self.path = path + self.fields = create_fields(fields) + # This private instance variable will only have its + # 'value' attribute modified by the setter for signature + self._signature = Field(**signature) if signature else None @property def signature(self): @@ -42,22 +41,19 @@ def signature(self): @signature.setter def signature(self, value): - if not value.endswith('.png'): - value += ".png" - if value and not os.path.exists(value): - new_value = 'signatures/' + value - if not os.path.exists(new_value): - raise BadSignature(f"{value} is not a valid signature") - else: - value = new_value - self._signature.value = value + path = os.path.join(SIGNATURE_FOLDER, f"{value}.png") + if not os.path.isfile(path): + raise BadSignature(f"{value} is not a valid signature") + self._signature.value = path @property def valid(self): file_exists = os.path.exists(self.path) if self.signature and self.signature.value: + # Check if signature is set to a valid signature file signature_exists = os.path.exists(self.signature.value) else: + # A blank signature is also fine signature_exists = True fields_valid = all(f.value for f in self.fields.values()) return file_exists and signature_exists and fields_valid @@ -71,23 +67,83 @@ def copy(self): signature = self.signature.copy if self.signature else None return self.__class__(self.name, self.path, [f.copy() for f in self.fields], signature) - @classmethod - def from_dict(cls, template_dict, dirpath=''): - template_dict['dirpath'] = dirpath - return cls(**template_dict) - -def import_templates(filename): +def load_json_from(filename): + """Returns the dumped JSON metadata from filename""" with open(filename) as infile: dicts = loads(infile.read()) - directory_name = os.path.dirname(filename) - - return {template_dict['name'].lower(): Template.from_dict(template_dict, dirpath=directory_name) - for template_dict in dicts} - - -def valid_template_names(): - return [k for k, v - in import_templates('templates/templates.json').items() - if os.path.exists(v.path)] + return dicts + + +def discard_nonexistant_templates(template_dicts): + """Returns a new dictionary of template attributes, discarding + all templates that are not actually available on disk""" + return { + k: v for k, v + in template_dicts.items() + if os.path.isfile(os.path.join(TEMPLATE_FOLDER, v['path'])) + } + + +def discard_nonexistant_signatures(signature_dicts): + """Returns a new dictionary of signature attributes, discarding + all signatures that are not actually available on disk""" + return [ + d for d in signature_dicts + if os.path.isfile(os.path.join(SIGNATURE_FOLDER, d['id'] + '.png')) + ] + + +def create_fields(field_dicts): + """Takes a list of dicts that have Field attributes and + returns a dictionary where they keys are names of fields + and the values are the fields themselves.""" + return {k.lower(): Field(**v) for k, v in field_dicts.items()} + + +def create_clean_template(template_dict): + """Takes a dictionary that has Template attributes + and removes all attributes that the frontend does not need. + Transforms the list of Field dicts into a list of their names.""" + cleaned = template_dict.copy() + cleaned.pop('path', None) + signature = cleaned.pop('signature', False) + field_dicts = cleaned['fields'] + cleaned['fields'] = list(field_dicts.keys()) + cleaned['signature'] = bool(signature) + return cleaned + + +def frontend_templates_as_json(): + """Returns the frontend-friendly JSON version of all valid templates. + They look like this: + { + "template name" : { + "signature" : true, + "fields" : ["first field", "second field"] + } + } + """ + dicts = load_json_from(os.path.join(TEMPLATE_FOLDER, TEMPLATE_FILE)) + valid = discard_nonexistant_templates(dicts) + return dumps({k: create_clean_template(v) for k, v in valid.items()}) + + +def frontend_signatures_as_json(): + """Returns the frontend-friendly JSON for all valid signatures""" + dicts = load_json_from(os.path.join(SIGNATURE_FOLDER, SIGNATURE_FILE)) + valid = discard_nonexistant_signatures(dicts) + return dumps(valid) + + +def import_templates(): + """Returns a dictionary where the keys are template names + and the values are the actual Template instances. + """ + dicts = load_json_from(os.path.join(TEMPLATE_FOLDER, TEMPLATE_FILE)) + valid = discard_nonexistant_templates(dicts) + + for d in valid.values(): + d['path'] = os.path.join(TEMPLATE_FOLDER, d['path']) + return {k.lower(): Template(**v) for k, v in valid.items()} diff --git a/templates/templates.json b/templates/templates.json index 9b73a78..aa79466 100644 --- a/templates/templates.json +++ b/templates/templates.json @@ -1,73 +1,73 @@ -[{ +{ + "Employee": { "path": "employee.png", - "name": "Employee", - "fields": [{ - "name": "Name", - "x": 0.25, - "y": 0.25, - "h": 0.15, - "w": 0.5, - "color": "black" - }, { - "name": "Date", - "x": 0.12, - "y": 0.715, - "h": 0.06, - "w": 0.34, - "color": "black" - }, { - "name": "Boss", - "x": 0.53, - "y": 0.78, - "h": 0.065, - "w": 0.18, - "color": "black" - }], + "fields": { + "Name": { + "color": "black", + "x": 0.25, + "y": 0.25, + "w": 0.5, + "h": 0.15 + }, + "Date": { + "color": "black", + "x": 0.12, + "y": 0.715, + "w": 0.34, + "h": 0.06 + }, + "Boss": { + "color": "black", + "x": 0.53, + "y": 0.78, + "w": 0.18, + "h": 0.065 + } + }, "signature": { - "name": "signature", + "color": "black", "x": 0.6, "y": 0.675, - "h": 0.1, "w": 0.20, - "color": "black" + "h": 0.1 } - - }, { + }, + "Student": { "path": "student.png", - "name": "Student", - "fields": [{ - "name": "Name", - "x": 0.25, - "y": 0.33, - "h": 0.15, - "w": 0.5, - "color": "black" - }, { - "name": "Helped with", - "x": 0.71, - "y": 0.585, - "h": 0.045, - "w": 0.125, - "color": "white" - }, { - "name": "Teacher name", - "x": 0.33, - "y": 0.8, - "h": 0.075, - "w": 0.33, - "color": "white" - }] + "fields": { + "Name": { + "color": "black", + "x": 0.25, + "y": 0.33, + "w": 0.5, + "h": 0.15 + }, + "Subject": { + "color": "white", + "x": 0.71, + "y": 0.585, + "w": 0.125, + "h": 0.045 + }, + "Teacher": { + "color": "white", + "x": 0.33, + "y": 0.8, + "w": 0.33, + "h": 0.075 + } + } }, - { + "Good effort": { "path": "good_effort.png", - "name": "Good effort", - "fields": [{ - "name": "Name", - "x": 0.45, - "y": 0.4, - "h": 0.2, - "w": 0.4, - "color": "white" - }] + "fields": { + "Name": { + "color": "white", + "x": 0.45, + "y": 0.4, + "w": 0.4, + "h": 0.2 + } + } } -] +}