Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up #16

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arches_templating/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import importlib.metadata

__version__ = importlib.metadata.version(__package__)
__version__ = importlib.metadata.version(__package__)
6 changes: 1 addition & 5 deletions arches_templating/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
from django.contrib import admin
from arches_templating import models

admin.site.register(
[
models.ArchesTemplate
]
)
admin.site.register([models.ArchesTemplate])
6 changes: 3 additions & 3 deletions arches_templating/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
3 changes: 2 additions & 1 deletion arches_templating/models.py
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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()
self.templateid = uuid.uuid4()
2 changes: 1 addition & 1 deletion arches_templating/template_engine/__init__.py
Original file line number Diff line number Diff line change
@@ -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
from arches_templating.template_engine.implementations.docx_template_engine import DocxTemplateEngine
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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:
Expand Down Expand Up @@ -150,39 +150,46 @@ 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:
DocxTemplateEngine.delete_paragraph(original_block)
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:
Expand All @@ -203,7 +210,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
Expand All @@ -212,11 +218,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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <arches: node_alias>
# should match <arches: node_alias>

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
Expand All @@ -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
Expand All @@ -82,78 +82,78 @@ 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:
column = -1
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:
Expand All @@ -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)
return (bytestream, mime, incomplete)
Loading