From 79486bd014ad4f6507982cc31d3fdf4c0b284bc5 Mon Sep 17 00:00:00 2001 From: Aaron Gundel Date: Wed, 22 Nov 2023 14:13:40 -0700 Subject: [PATCH 1/3] Auto-formatting, deprecation purging, pyproject toml switch --- arches_templating/__init__.py | 2 +- arches_templating/admin.py | 6 +- arches_templating/apps.py | 6 +- arches_templating/models.py | 3 +- arches_templating/template_engine/__init__.py | 2 +- .../implementations/docx_template_engine.py | 40 +++++----- .../implementations/pptx_template_engine.py | 61 ++++++++-------- .../implementations/xlsx_template_engine.py | 73 ++++++++++--------- .../template_engine/template_engine.py | 47 +++++++----- .../template_engine_factory.py | 11 +-- .../template_engine/template_tag.py | 5 +- .../template_engine/template_tag_type.py | 4 +- arches_templating/urls.py | 13 +++- arches_templating/views/index.py | 3 +- arches_templating/views/template.py | 44 ++++++----- pyproject.toml | 53 ++++++++++++++ setup.py | 49 ------------- 17 files changed, 225 insertions(+), 197 deletions(-) create mode 100644 pyproject.toml delete mode 100755 setup.py diff --git a/arches_templating/__init__.py b/arches_templating/__init__.py index 2f9e5b1..c1d20e7 100644 --- a/arches_templating/__init__.py +++ b/arches_templating/__init__.py @@ -1,3 +1,3 @@ import importlib.metadata -__version__ = importlib.metadata.version(__package__) \ No newline at end of file +__version__ = importlib.metadata.version(__package__) diff --git a/arches_templating/admin.py b/arches_templating/admin.py index 0683bb6..9ef1b2b 100644 --- a/arches_templating/admin.py +++ b/arches_templating/admin.py @@ -1,8 +1,4 @@ from django.contrib import admin from arches_templating import models -admin.site.register( - [ - models.ArchesTemplate - ] -) \ No newline at end of file +admin.site.register([models.ArchesTemplate]) diff --git a/arches_templating/apps.py b/arches_templating/apps.py index 2b5e133..701b658 100644 --- a/arches_templating/apps.py +++ b/arches_templating/apps.py @@ -2,6 +2,6 @@ class ArchesTemplatingConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'arches_templating' - verbose_name = 'Arches Templating' + default_auto_field = "django.db.models.BigAutoField" + name = "arches_templating" + verbose_name = "Arches Templating" diff --git a/arches_templating/models.py b/arches_templating/models.py index 56c772c..9672857 100644 --- a/arches_templating/models.py +++ b/arches_templating/models.py @@ -1,6 +1,7 @@ import uuid from django.db import models + class ArchesTemplate(models.Model): templateid = models.UUIDField(primary_key=True, unique=True) name = models.TextField(blank=False, null=False) @@ -18,4 +19,4 @@ class Meta: def __init__(self, *args, **kwargs): super(ArchesTemplate, self).__init__(*args, **kwargs) if not self.templateid: - self.templateid = uuid.uuid4() \ No newline at end of file + self.templateid = uuid.uuid4() diff --git a/arches_templating/template_engine/__init__.py b/arches_templating/template_engine/__init__.py index f0a617c..cb799ba 100644 --- a/arches_templating/template_engine/__init__.py +++ b/arches_templating/template_engine/__init__.py @@ -1,3 +1,3 @@ from arches_templating.template_engine.implementations.xlsx_template_engine import XlsxTemplateEngine from arches_templating.template_engine.implementations.pptx_template_engine import PptxTemplateEngine -from arches_templating.template_engine.implementations.docx_template_engine import DocxTemplateEngine \ No newline at end of file +from arches_templating.template_engine.implementations.docx_template_engine import DocxTemplateEngine diff --git a/arches_templating/template_engine/implementations/docx_template_engine.py b/arches_templating/template_engine/implementations/docx_template_engine.py index 578f5b0..c925026 100644 --- a/arches_templating/template_engine/implementations/docx_template_engine.py +++ b/arches_templating/template_engine/implementations/docx_template_engine.py @@ -19,7 +19,8 @@ from docx.text.paragraph import Paragraph from docx.section import _Header -@TemplateEngineFactory.register('docx') + +@TemplateEngineFactory.register("docx") class DocxTemplateEngine(TemplateEngine): def extract_regex_matches(self, template) -> List[Tuple]: self.doc = docx.Document(template) @@ -84,10 +85,10 @@ def iter_block_items(self, parent): def delete_paragraph(paragraph): p = paragraph._element paragraph_parent = p.getparent() - if paragraph_parent is not None: + if paragraph_parent is not None: paragraph_parent.remove(p) p._p = p._element = None - + def delete_table(table): table._element.getparent().remove(table._element) @@ -105,10 +106,9 @@ def replace_tags(self, tags: List[TemplateTag]): # render a table parent = tag.context_children_template[-1].optional_keys["parent"] if isinstance(parent, Table): - column = 0 # this is ugly, but way more efficient than the alternative - + current_row = parent.add_row() for child in tag.children: @@ -150,31 +150,39 @@ def replace_tags(self, tags: List[TemplateTag]): # get all blocks between context start and end for item in self.iterate_inner_block(block, tag): all_blocks_in_context.append(item) - + # for each child (row) copy the context section context_index = 0 while context_index < tag.context_length: block_index = 0 while block_index <= len(all_blocks_in_context) - 1: if block_index == 0: - new_index_attribute = "index=\"{}\" ".format(context_index) + new_index_attribute = 'index="{}" '.format(context_index) match = re.findall(self.regex, all_blocks_in_context[block_index].text) try: tag_length = len(all_blocks_in_context[block_index].text) attribute_length = len(match[0][2]) - + insert_index = tag_length - attribute_length - new_tag_text = all_blocks_in_context[block_index].text[:insert_index] + new_index_attribute + all_blocks_in_context[block_index].text[insert_index:] + new_tag_text = ( + all_blocks_in_context[block_index].text[:insert_index] + + new_index_attribute + + all_blocks_in_context[block_index].text[insert_index:] + ) except IndexError: # OK to fail, means there was an issue getting a tag match. Bail/abort rather than scream. - + pass - tag.end_tag.optional_keys["docxBlock"].insert_paragraph_before(new_tag_text, all_blocks_in_context[block_index].style) + tag.end_tag.optional_keys["docxBlock"].insert_paragraph_before( + new_tag_text, all_blocks_in_context[block_index].style + ) else: - tag.end_tag.optional_keys["docxBlock"]._element.addprevious(copy.deepcopy(all_blocks_in_context[block_index]._element)) - + tag.end_tag.optional_keys["docxBlock"]._element.addprevious( + copy.deepcopy(all_blocks_in_context[block_index]._element) + ) + block_index += 1 context_index += 1 for original_block in all_blocks_in_context: @@ -182,7 +190,7 @@ def replace_tags(self, tags: List[TemplateTag]): else: replace_tags_result = self.replace_tags(tag.children) incomplete = incomplete or replace_tags_result - + DocxTemplateEngine.delete_paragraph(block) DocxTemplateEngine.delete_paragraph(tag.end_tag.optional_keys["docxBlock"]) elif tag.type == TemplateTagType.VALUE: @@ -203,7 +211,6 @@ def replace_tags(self, tags: List[TemplateTag]): DocxTemplateEngine.delete_paragraph(item) return incomplete - def iterate_inner_block(self, block, tag): found_if_start = False found_if_end = False @@ -212,11 +219,10 @@ def iterate_inner_block(self, block, tag): found_if_start = True if item._element == tag.end_tag.optional_keys["docxBlock"]._element: found_if_end = True - yield item + yield item if found_if_start and not found_if_end: yield item - def create_file(self, tags: List[TemplateTag], template): bytestream = BytesIO() mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" diff --git a/arches_templating/template_engine/implementations/pptx_template_engine.py b/arches_templating/template_engine/implementations/pptx_template_engine.py index 373c900..93cf2a1 100644 --- a/arches_templating/template_engine/implementations/pptx_template_engine.py +++ b/arches_templating/template_engine/implementations/pptx_template_engine.py @@ -12,34 +12,34 @@ from pptx.shapes.graphfrm import GraphicFrame from pptx.table import _Cell, _Row -@TemplateEngineFactory.register('pptx') -class PptxTemplateEngine(TemplateEngine): +@TemplateEngineFactory.register("pptx") +class PptxTemplateEngine(TemplateEngine): def extract_regex_matches(self, template) -> List[Tuple]: self.presentation = Presentation(template) parsed_tags: List[Tuple] = [] parsed_tags += self.iterate_over_container(self.presentation.slides) return parsed_tags - # should match + # should match def match_text(self, container, parent, cell=None): parsed_tags: List[Tuple] = [] for match in re.findall(self.regex, container.text): parsed_tags.append((match, {"container": container, "parent": parent, "cell": None})) return parsed_tags - + def iterate_over_shapes_in_slide(self, slide): for shape in slide.shapes: yield shape - + def iterate_over_container(self, container, parent=None): parsed_tags: List[Tuple] = [] - if hasattr(container, '__iter__'): + if hasattr(container, "__iter__"): for s in container: for shape in s.shapes: if type(shape) is not GraphicFrame: parsed_tags += self.match_text(shape, container) - + else: try: table = shape.table @@ -55,7 +55,7 @@ def iterate_over_container(self, container, parent=None): current_row += 1 pass except AttributeError: - pass # ok to pass; happens if the shape doesn't have a table - in this case, don't render subtags - we don't know how. + pass # ok to pass; happens if the shape doesn't have a table - in this case, don't render subtags - we don't know how. elif type(container) is _Cell: parsed_tags += self.match_text(container, parent) return parsed_tags @@ -82,32 +82,31 @@ def add_row(table, index=None): for tc in new_row.tc_lst: cell = _Cell(tc, new_row.tc_lst) - cell.text = '' + cell.text = "" if not index: table._tbl.append(new_row) elif index < 0: - index = index % len(table._tbl) + index = index % len(table._tbl) table._tbl.insert(index, new_row) return _Row(new_row, table) - def replace_tags(self, tags:List[TemplateTag]): + def replace_tags(self, tags: List[TemplateTag]): for tag in tags: - block = tag.optional_keys['container'] + block = tag.optional_keys["container"] if tag.type == TemplateTagType.CONTEXT: - if block._parent is not _Cell: + if block._parent is not _Cell: block.text = block.text.replace(tag.raw, "") - end_block = tag.end_tag.optional_keys['container'] + end_block = tag.end_tag.optional_keys["container"] end_block.text = end_block.text.replace(tag.end_tag.raw, "") if tag.has_rows: column = 0 # this is ugly, but way more efficient than the alternative parent_table = tag.context_children_template[-1].optional_keys["parent"] - - current_row = PptxTemplateEngine.add_row(parent_table) + current_row = PptxTemplateEngine.add_row(parent_table) for child in tag.children: if child.type == TemplateTagType.ROWEND: @@ -115,45 +114,46 @@ def replace_tags(self, tags:List[TemplateTag]): current_row = PptxTemplateEngine.add_row(parent_table) elif child.type == TemplateTagType.VALUE: # grab any borders from the original cell copy them to the new cell. - #template_block = tag.context_children_template[column].optional_keys["container"] + # template_block = tag.context_children_template[column].optional_keys["container"] paragraph = current_row.cells[column].text_frame.add_paragraph() paragraph.text = "" if child.value == None else child.value column += 1 - # these two lines look for the templating rows, (i.e., the values that we're trying to replace in each row) # and remove them. If they exist on line 1, the table likely has no header row. # if they exist on line 2, the table likely has a header row followed by the values. - if len(self.regex.findall(parent_table.cell(2,0).text)): + if len(self.regex.findall(parent_table.cell(2, 0).text)): PptxTemplateEngine.remove_row(parent_table, 2) - if len(self.regex.findall(parent_table.cell(1,0).text)): + if len(self.regex.findall(parent_table.cell(1, 0).text)): PptxTemplateEngine.remove_row(parent_table, 1) # this looks for tags in the lead row of a table. - lead_matches_len = len(self.regex.findall(parent_table.cell(0,0).text)) + lead_matches_len = len(self.regex.findall(parent_table.cell(0, 0).text)) # if there is a more than one tag in the lead row (there should, at minimum, be a context tag) # then maintain the lead row. Otherwise, delete it. if lead_matches_len > 1: - primary_cell = parent_table.cell(0,0) + primary_cell = parent_table.cell(0, 0) primary_cell.text = primary_cell.text.replace(tag.raw, primary_cell.text) else: PptxTemplateEngine.remove_row(parent_table, 0) - + else: self.replace_tags(tag.children) elif tag.type == TemplateTagType.VALUE: block.text = block.text.replace(tag.raw, tag.value) elif tag.type == TemplateTagType.IMAGE: - block._parent.add_picture(BytesIO(b64decode(re.sub("data:image/jpeg;base64,", '', tag.value))), block.left, block.top, block.width, block.height) + block._parent.add_picture( + BytesIO(b64decode(re.sub("data:image/jpeg;base64,", "", tag.value))), block.left, block.top, block.width, block.height + ) PptxTemplateEngine.delete_element(block) elif tag.type == TemplateTagType.IF: if tag.render: block.text = block.text.replace(tag.raw, "") - end_block = tag.end_tag.optional_keys['container'] + end_block = tag.end_tag.optional_keys["container"] end_block.text = end_block.text.replace(tag.end_tag.raw, "") self.replace_tags(tag.children) else: @@ -170,24 +170,25 @@ def replace_tags(self, tags:List[TemplateTag]): item.text = item.text.replace(child_tag.raw, "") item.text = item.text.replace(tag.raw, "") item.text = item.text.replace(tag.end_tag.raw, "") - except AttributeError: # ok to skip if text is not found + except AttributeError: # ok to skip if text is not found pass if found_if_start and not found_if_end: try: item.text = "" - except AttributeError: # ok to skip if text is not found + except AttributeError: # ok to skip if text is not found pass + def get_all_child_tags(container_tag): raw_tags = [] for child in container_tag.children: raw_tags.append(child) raw_tags += PptxTemplateEngine.get_all_child_tags(child) - return raw_tags + return raw_tags - def create_file(self, tags:List[TemplateTag], template): + def create_file(self, tags: List[TemplateTag], template): incomplete = False bytestream = BytesIO() mime = "application/vnd.openxmlformats-officedocument.presentationml.presentation" self.replace_tags(tags) self.presentation.save(bytestream) - return (bytestream, mime, incomplete) \ No newline at end of file + return (bytestream, mime, incomplete) diff --git a/arches_templating/template_engine/implementations/xlsx_template_engine.py b/arches_templating/template_engine/implementations/xlsx_template_engine.py index 5069137..6af80fc 100644 --- a/arches_templating/template_engine/implementations/xlsx_template_engine.py +++ b/arches_templating/template_engine/implementations/xlsx_template_engine.py @@ -11,11 +11,11 @@ from openpyxl import load_workbook from openpyxl.drawing.image import Image -@TemplateEngineFactory.register('xlsx') -class XlsxTemplateEngine(TemplateEngine): +@TemplateEngineFactory.register("xlsx") +class XlsxTemplateEngine(TemplateEngine): def extract_regex_matches(self, template) -> List[Tuple]: - self.workbook = load_workbook(filename = template) + self.workbook = load_workbook(filename=template) parsed_tags: List[Tuple] = [] for sheet in self.workbook.worksheets: parsed_tags += self.iterate_over_sheet(sheet) @@ -40,7 +40,7 @@ def column_from_offset(offset): return column - def offset_from_column(column:str): + def offset_from_column(column: str): """Convert a spreadsheet column to the corresponding numeric offset. Args: @@ -53,20 +53,20 @@ def offset_from_column(column:str): for i, letter in enumerate(column): offset = offset * 26 + (ord(letter) - 64) return offset - - def increment_column(column:str): + + def increment_column(column: str): return XlsxTemplateEngine.column_from_offset(XlsxTemplateEngine.offset_from_column(column) + 1) def iterate_over_sheet(self, sheet): parsed_tags: List[Tuple] = [] - range_regex = re.compile(r'^([A-Z]+)(\d+):([A-Z]+)(\d+)$') + range_regex = re.compile(r"^([A-Z]+)(\d+):([A-Z]+)(\d+)$") dimensions = sheet.dimensions match = range_regex.match(dimensions) if match: - start_col = match.group(1) - start_row = int(match.group(2)) - end_col = match.group(3) - end_row = int(match.group(4)) + start_col = match.group(1) + start_row = int(match.group(2)) + end_col = match.group(3) + end_row = int(match.group(4)) current_row = start_row start_col_offset = XlsxTemplateEngine.offset_from_column(start_col) @@ -79,20 +79,29 @@ def iterate_over_sheet(self, sheet): current_cell = XlsxTemplateEngine.column_from_offset(current_col) + str(current_row) if sheet[current_cell].value: for match in re.findall(self.regex, sheet[current_cell].value): - parsed_tags.append((match, {"cell": sheet[current_cell], "sheet": sheet, "row": current_row, "column": XlsxTemplateEngine.column_from_offset(current_col)})) + parsed_tags.append( + ( + match, + { + "cell": sheet[current_cell], + "sheet": sheet, + "row": current_row, + "column": XlsxTemplateEngine.column_from_offset(current_col), + }, + ) + ) current_col += 1 - current_row +=1 + current_row += 1 else: print("Invalid range format") return parsed_tags - def replace_tags(self, tags:List[TemplateTag], rowshift=0): - + def replace_tags(self, tags: List[TemplateTag], rowshift=0): for tag in tags: - cell = tag.optional_keys['cell'] - sheet = tag.optional_keys['sheet'] - row = tag.optional_keys['row'] + rowshift - column = tag.optional_keys['column'] + cell = tag.optional_keys["cell"] + sheet = tag.optional_keys["sheet"] + row = tag.optional_keys["row"] + rowshift + column = tag.optional_keys["column"] if tag.type == TemplateTagType.CONTEXT: if tag.has_rows: column = 0 @@ -106,8 +115,8 @@ def replace_tags(self, tags:List[TemplateTag], rowshift=0): rowshift += 1 elif child.type == TemplateTagType.VALUE: # grab any borders from the original cell copy them to the new cell. - #template_block = tag.context_children_template[column].optional_keys["container"] - sheet[child.optional_keys['column'] + str(current_row)].value = child.value + # template_block = tag.context_children_template[column].optional_keys["container"] + sheet[child.optional_keys["column"] + str(current_row)].value = child.value else: rowshift = self.replace_tags(tag.children, rowshift) @@ -128,31 +137,29 @@ def replace_tags(self, tags:List[TemplateTag], rowshift=0): elif tag.type == TemplateTagType.IF: if tag.render: cell.value = cell.value.replace(tag.raw, "") - tag.end_tag.optional_keys['cell'].value = tag.end_tag.optional_keys['cell'].value.replace(tag.end_tag.raw, "") + tag.end_tag.optional_keys["cell"].value = tag.end_tag.optional_keys["cell"].value.replace(tag.end_tag.raw, "") rowshift = self.replace_tags(tag.children, rowshift) else: - #delete rows between tags - if row != tag.end_tag.optional_keys['row'] + rowshift: - sheet.delete_rows(row, tag.end_tag.optional_keys['row'] + rowshift) - rowshift += row - (tag.end_tag.optional_keys['row'] + rowshift) + # delete rows between tags + if row != tag.end_tag.optional_keys["row"] + rowshift: + sheet.delete_rows(row, tag.end_tag.optional_keys["row"] + rowshift) + rowshift += row - (tag.end_tag.optional_keys["row"] + rowshift) - elif row == tag.end_tag.optional_keys['row'] + rowshift and column != tag.end_tag.optional_keys['column']: + elif row == tag.end_tag.optional_keys["row"] + rowshift and column != tag.end_tag.optional_keys["column"]: current_col = column - end_col = tag.end_tag.optional_keys['column'] + end_col = tag.end_tag.optional_keys["column"] while current_col != end_col: sheet[current_col + str(row)].value = "" current_col = XlsxTemplateEngine.increment_column(current_col) else: cell.value = "" - - return (rowshift) - + return rowshift - def create_file(self, tags:List[TemplateTag], template): + def create_file(self, tags: List[TemplateTag], template): incomplete = False bytestream = BytesIO() mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" self.replace_tags(tags) self.workbook.save(bytestream) - return (bytestream, mime, incomplete) \ No newline at end of file + return (bytestream, mime, incomplete) diff --git a/arches_templating/template_engine/template_engine.py b/arches_templating/template_engine/template_engine.py index f77a0ae..9f86bcc 100644 --- a/arches_templating/template_engine/template_engine.py +++ b/arches_templating/template_engine/template_engine.py @@ -9,6 +9,7 @@ import requests from urllib.parse import urlparse, urlunparse + class TemplateEngine(object): def __init__( self, @@ -84,7 +85,7 @@ def normalize_dictionary_keys(self, dictionary): def traverse_dictionary(self, path, dictionary): path_parts = path.split("/") - path_parts = [i for i in path_parts if i] # remove empty strings + path_parts = [i for i in path_parts if i] # remove empty strings current_value = dictionary for part in path_parts: @@ -109,7 +110,7 @@ def get_tag_values(self, tags, context, root_context=None) -> List[TemplateTag]: if path_value.startswith("/"): # any path that starts with "/" will be tied to the root context. We can be more explicit in tag attributes later if need be tag.value = self.traverse_dictionary(path_value, current_root_context) - else: + else: tag.value = self.traverse_dictionary(path_value, context) else: tag.value = "" @@ -119,24 +120,29 @@ def get_tag_values(self, tags, context, root_context=None) -> List[TemplateTag]: if path_value.startswith("/"): # any path that starts with "/" will be tied to the root context. We can be more explicit in tag attributes later if need be image_value = self.traverse_dictionary(path_value, current_root_context) - else: + else: image_value = self.traverse_dictionary(path_value, context) try: - if re.match("^http", image_value): - if(re.search("\/temp_file\/", image_value)): + if re.match(r"^http", image_value): + if re.search(r"/temp_file/", image_value): parsed_url = urlparse(image_value) - if 'internal_base_url' in root_context: # sometimes the internal base url differs from the externally exposed one. Esp the case in docker networks - internal_base_url = urlparse(root_context['internal_base_url']) + if ( + "internal_base_url" in root_context + ): # sometimes the internal base url differs from the externally exposed one. Esp the case in docker networks + internal_base_url = urlparse(root_context["internal_base_url"]) parsed_url = parsed_url._replace(netloc=internal_base_url.netloc) image_value = urlunparse(parsed_url) - tag.value = "data:image/jpeg;base64," + base64.b64encode(requests.get(image_value).content).decode('utf-8') + tag.value = "data:image/jpeg;base64," + base64.b64encode(requests.get(image_value).content).decode("utf-8") else: tag.value = image_value - except TypeError as e: #when image_value is null, the render failed - raise Exception('Could not get a URL for a requested image, image_value was not a string', e) from e + except TypeError as e: # when image_value is null, the render failed + raise Exception("Could not get a URL for a requested image, image_value was not a string", e) from e except (HTTPError, ConnectionError) as e: - raise Exception('An error occurred retrieving an image for inclusion in the report. Check your PUBLIC_SERVER_ADDRESS setting.', e) from e + raise Exception( + "An error occurred retrieving an image for inclusion in the report. Check your PUBLIC_SERVER_ADDRESS setting.", + e, + ) from e extended_tags.append(tag) elif tag.type == TemplateTagType.CONTEXT: path_value = tag.attributes.get("path", None) @@ -145,7 +151,7 @@ def get_tag_values(self, tags, context, root_context=None) -> List[TemplateTag]: if path_value.startswith("/"): # any path that starts with "/" will be tied to the root context. We can be more explicit in tag attributes later if need be new_context = self.traverse_dictionary(path_value, current_root_context) - else: + else: new_context = self.traverse_dictionary(path_value, context) if isinstance(new_context, List) and index_value: self.get_tag_values(tag.children, new_context[int(index_value)], current_root_context) @@ -155,24 +161,28 @@ def get_tag_values(self, tags, context, root_context=None) -> List[TemplateTag]: tag.children = [] tag.context_length = len(new_context) for item in new_context: - tag.children.extend(self.get_tag_values(copy.deepcopy(tag.context_children_template), item, current_root_context)) + tag.children.extend( + self.get_tag_values(copy.deepcopy(tag.context_children_template), item, current_root_context) + ) tag.children.append(TemplateTag("", TemplateTagType.ROWEND)) else: self.get_tag_values(tag.children, new_context, current_root_context) elif tag.type == TemplateTagType.IF: path_value = tag.attributes.get("path", None) - inverse_value:str = tag.attributes.get("inverse", "") + inverse_value: str = tag.attributes.get("inverse", "") inverse = True if inverse_value.lower() == "true" else False if path_value: tag_value = self.traverse_dictionary(path_value, context) else: tag_value = False - + tag.render = tag_value if inverse is False else not tag_value - + if tag.render: - self.get_tag_values(tag.children, context, current_root_context) # use the existing context; if tags do not generate a new context. - + self.get_tag_values( + tag.children, context, current_root_context + ) # use the existing context; if tags do not generate a new context. + elif tag.type == TemplateTagType.END: context.pop() @@ -195,4 +205,3 @@ def document_replace(self, template, context): result_file_stream, mime_type, incomplete = self.document_replace(result_file_stream, context) return result_file_stream, mime_type, False - diff --git a/arches_templating/template_engine/template_engine_factory.py b/arches_templating/template_engine/template_engine_factory.py index 6a6f5fc..e4bb7a0 100644 --- a/arches_templating/template_engine/template_engine_factory.py +++ b/arches_templating/template_engine/template_engine_factory.py @@ -5,23 +5,24 @@ logger = logging.getLogger(__name__) + class TemplateEngineFactory(object): registry = {} @classmethod - def register(cls, name:str): + def register(cls, name: str): def inner_wrapper(wrapped_class: TemplateEngine) -> Callable: if name in cls.registry: - logger.warning('Template Engine %s already exists. Will replace it', name) + logger.warning("Template Engine %s already exists. Will replace it", name) cls.registry[name] = wrapped_class return wrapped_class return inner_wrapper @classmethod - def create_engine(cls, name: str, **kwargs) -> 'TemplateEngine': - """ Factory command to create the template engine """ + def create_engine(cls, name: str, **kwargs) -> "TemplateEngine": + """Factory command to create the template engine""" template_engine_class = cls.registry[name] template_engine = template_engine_class(**kwargs) - return template_engine \ No newline at end of file + return template_engine diff --git a/arches_templating/template_engine/template_tag.py b/arches_templating/template_engine/template_tag.py index b98b9a0..cd9106e 100644 --- a/arches_templating/template_engine/template_tag.py +++ b/arches_templating/template_engine/template_tag.py @@ -1,9 +1,8 @@ - - from ast import List from arches_templating.template_engine.template_tag_type import TemplateTagType + class TemplateTag(object): def __init__(self, raw: str, tag_type: TemplateTagType, attributes: dict = {}, optional_keys: dict = {}): self.type = tag_type @@ -16,4 +15,4 @@ def __init__(self, raw: str, tag_type: TemplateTagType, attributes: dict = {}, o self.has_rows = False self.context_children_template = None self.render = True - self.context_length = None \ No newline at end of file + self.context_length = None diff --git a/arches_templating/template_engine/template_tag_type.py b/arches_templating/template_engine/template_tag_type.py index f73fba3..645c853 100644 --- a/arches_templating/template_engine/template_tag_type.py +++ b/arches_templating/template_engine/template_tag_type.py @@ -1,10 +1,10 @@ - from enum import Enum + class TemplateTagType(Enum): VALUE = 1 CONTEXT = 2 END = 3 ROWEND = 4 IMAGE = 5 - IF = 6 \ No newline at end of file + IF = 6 diff --git a/arches_templating/urls.py b/arches_templating/urls.py index 9df87a9..520ff16 100644 --- a/arches_templating/urls.py +++ b/arches_templating/urls.py @@ -1,9 +1,14 @@ from arches_templating.views.index import IndexView from arches_templating.views.template import TemplateView from django.urls import path, re_path +from django.views.decorators.csrf import csrf_exempt urlpatterns = [ - path('', IndexView.as_view(), name='index'), - path('template/', TemplateView.as_view(), name="archestemplating_template_view"), - re_path(r'template\/?', TemplateView.get, name="archestemplating_template_view_get"), -] \ No newline at end of file + path("", IndexView.as_view(), name="index"), + path( + "template/", + csrf_exempt(TemplateView.as_view()), + name="archestemplating_template_view", + ), + re_path(r"template\/?", TemplateView.get, name="archestemplating_template_view_get"), +] diff --git a/arches_templating/views/index.py b/arches_templating/views/index.py index b7627ce..244f673 100644 --- a/arches_templating/views/index.py +++ b/arches_templating/views/index.py @@ -1,5 +1,6 @@ from django.shortcuts import render from django.views import generic + class IndexView(generic.TemplateView): - template_name = 'index.html' \ No newline at end of file + template_name = "index.html" diff --git a/arches_templating/views/template.py b/arches_templating/views/template.py index 73920df..ef4d955 100644 --- a/arches_templating/views/template.py +++ b/arches_templating/views/template.py @@ -1,4 +1,3 @@ - import os import logging import json @@ -14,8 +13,8 @@ from arches_templating.template_engine.template_engine_factory import TemplateEngineFactory from django.views.decorators.csrf import csrf_exempt -class TemplateView(generic.View): +class TemplateView(generic.View): logger = logging.getLogger(__name__) def get(request): @@ -23,31 +22,30 @@ def get(request): response = [] for template in all_templates: template_object = {} - template_object['templateid'] = template.templateid - template_object['name'] = template.name - template_object['description'] = template.description - template_object['template'] = {} - template_object['template']['url'] = template.template.url - template_object['template']['name'] = template.template.name + template_object["templateid"] = template.templateid + template_object["name"] = template.name + template_object["description"] = template.description + template_object["template"] = {} + template_object["template"]["url"] = template.template.url + template_object["template"]["name"] = template.template.name if template.preview: - template_object['preview'] = {} - template_object['preview']['url'] = template.preview.url - template_object['preview']['name'] = template.preview.url + template_object["preview"] = {} + template_object["preview"]["url"] = template.preview.url + template_object["preview"]["name"] = template.preview.url if template.thumbnail: - template_object['thumbnail'] = {} - template_object['thumbnail']['url'] = template.thumbnail.url - template_object['thumbnail']['name'] = template.thumbnail.name + template_object["thumbnail"] = {} + template_object["thumbnail"]["url"] = template.thumbnail.url + template_object["thumbnail"]["name"] = template.thumbnail.name response.append(template_object) return JSONResponse(response) - + @csrf_exempt def post(self, request, templateid): json_data = json.loads(request.body) - #template_id = json_data["templateId"] if "templateId" in json_data else None + # template_id = json_data["templateId"] if "templateId" in json_data else None template_record = ArchesTemplate.objects.get(pk=templateid) template = template_record.template.name - #template = settings.AFS_CUSTOM_REPORTS[template_id] if template_id in settings.AFS_CUSTOM_REPORTS else None - + # template = settings.AFS_CUSTOM_REPORTS[template_id] if template_id in settings.AFS_CUSTOM_REPORTS else None bytestream = BytesIO() extension = os.path.splitext(template)[1].replace(".", "") @@ -55,15 +53,15 @@ def post(self, request, templateid): # I don't love doing this, because it's a "hardcoded" dependency on Arches - but it's optional. try: - json_data['internal_base_url'] = settings.PUBLIC_SERVER_ADDRESS + json_data["internal_base_url"] = settings.PUBLIC_SERVER_ADDRESS except: - pass # no need for the setting to exist, but if it does, use it. + pass # no need for the setting to exist, but if it does, use it. engine = factory.create_engine(extension) - with template_record.template.open('rb') as f: + with template_record.template.open("rb") as f: source_stream = BytesIO(f.read()) (bytestream, mime, incomplete) = engine.document_replace(source_stream, json_data) - file_name = "{}.{}" + file_name = "{}.{}" file_name = file_name.format(json_data["filename"] if "filename" in json_data else "untitled", extension) bytestream.seek(0) @@ -73,4 +71,4 @@ def post(self, request, templateid): response = HttpResponse(content=bytestream.read()) response["Content-Type"] = mime response["Content-Disposition"] = "attachment; filename={}".format(file_name) - return response \ No newline at end of file + return response diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a67e91e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "arches-templating" +readme = "README.md" +authors = [ + {name = "Farallon Geographics, Inc", email = "dev@fargeo.com"} +] +license = {text = "GNU AGPL3"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Intended Audience :: Information Technology", + "Topic :: Software Development :: Build Tools", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Framework :: Django :: 1.11", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", +] +requires-python = ">=3.9" +dependencies = [ + "Django >= 3.2.18", + "python-pptx >= 0.6.21", + "python-docx >= 0.8.11", + "openpyxl >= 3.0.7", + "PyYAML >= 6.0" +] +version = "0.1.1" + +[tool.setuptools] +packages = ["arches_templating"] + +[tool.black] +line-length = 140 +target-version = ['py310'] +include = '\.pyi?$' +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' diff --git a/setup.py b/setup.py deleted file mode 100755 index 72da3b7..0000000 --- a/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -from setuptools import setup, find_packages - -version = '0.1.2' - -setup( - name="arches-templating", - # Versions should comply with PEP440. For a discussion on single-sourcing - # the version across setup.py and the project code, see - # http://packaging.python.org/en/latest/tutorial.html#version - version=version, - description="Arches templating provides an extensible templating engine for various file types", - url="http://archesproject.org/", - long_description=open("README.md").read(), - long_description_content_type="text/markdown", - author="Farallon Geographics, Inc", - author_email="dev@fargeo.com", - license="GNU AGPL3", - install_requires=[ - 'Django >= 3.2.18', - 'python-pptx >= 0.6.21', - 'python-docx >= 0.8.11, != 1.0.0', - 'openpyxl >= 3.0.7', - 'PyYAML >= 6.0' - ], - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Intended Audience :: Information Technology", - "Topic :: Software Development :: Build Tools", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Framework :: Django :: 1.11", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - ], - # What does your project relate to? - keywords="django arches cultural heritage", - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - packages=find_packages(), - include_package_data=True, - zip_safe=False, - test_suite="tests.run_tests.run_all", -) From 5555267f544aea879b97fbcb9692755732f62bec Mon Sep 17 00:00:00 2001 From: Aaron Gundel Date: Wed, 22 Nov 2023 14:17:39 -0700 Subject: [PATCH 2/3] auto-formatting fix --- .../template_engine/implementations/docx_template_engine.py | 1 - 1 file changed, 1 deletion(-) diff --git a/arches_templating/template_engine/implementations/docx_template_engine.py b/arches_templating/template_engine/implementations/docx_template_engine.py index c925026..4193d4a 100644 --- a/arches_templating/template_engine/implementations/docx_template_engine.py +++ b/arches_templating/template_engine/implementations/docx_template_engine.py @@ -172,7 +172,6 @@ def replace_tags(self, tags: List[TemplateTag]): except IndexError: # OK to fail, means there was an issue getting a tag match. Bail/abort rather than scream. - pass tag.end_tag.optional_keys["docxBlock"].insert_paragraph_before( From 1a749c406702ee0e18bebd15a68d852bc0f6035a Mon Sep 17 00:00:00 2001 From: Aaron Gundel Date: Wed, 22 Nov 2023 14:39:01 -0700 Subject: [PATCH 3/3] fix version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a67e91e..442789c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "openpyxl >= 3.0.7", "PyYAML >= 6.0" ] -version = "0.1.1" +version = "0.1.2" [tool.setuptools] packages = ["arches_templating"]