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

Add support for more attirbutes: comment, hyperlink, font, alignment, fill, protection #1

Merged
merged 4 commits into from
Sep 23, 2015
Merged
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 .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ python:
- "pypy"

install:
- pip install --use-mirrors -r requirements/package.txt -r requirements/qa.txt
- pip install -r requirements/package.txt -r requirements/qa.txt
- pip install coveralls

script:
Expand Down
42 changes: 42 additions & 0 deletions surveyor/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import openpyxl
import openpyxl.utils
import openpyxl.comments
import openpyxl.styles
import six

import surveyor.classes.simple
Expand Down Expand Up @@ -271,12 +273,30 @@ def stylize(self, cells):
return cells


StyleInfo = collections.namedtuple("StyleInfo", ["attribute", "constructor"])


class Cell(BaseElement):

TAG_NAME = "td"

ATTR_SEPARATOR = "-"

ATTR_CLASS = "class"
ATTR_NUMBER_FORMAT = "number_format"
ATTR_HYPERLINK = "hyperlink"
ATTR_COMMENT = "comment"
ATTR_COMMENT_AUTHOR = "comment{0}author".format(ATTR_SEPARATOR)

STYLE_ATTRIBUTES = {
"font": StyleInfo("font", openpyxl.styles.Font),
"pattern-fill": StyleInfo("fill", openpyxl.styles.PatternFill),
"gradient-fill": StyleInfo("fill", openpyxl.styles.GradientFill),
"alignment": StyleInfo("alignment", openpyxl.styles.Alignment),
"protection": StyleInfo("protection", openpyxl.styles.Protection),
# TODO border
}
STYLE_PREFIXES = tuple(STYLE_ATTRIBUTES.keys())

DEFAULT_STYLER = surveyor.classes.simple.Cell

Expand All @@ -287,17 +307,39 @@ def __init__(self, element):
self.value = surveyor.utils.guess_text(element.text)
# check openpyxl.styles.numbers
self.number_format = element.attrib.get(self.ATTR_NUMBER_FORMAT)
self.hyperlink = element.attrib.get(self.ATTR_HYPERLINK)

comment_text = element.attrib.get(self.ATTR_COMMENT)
if comment_text:
self.comment = openpyxl.comments.Comment(comment_text, element.attrib.get(self.ATTR_COMMENT_AUTHOR))
else:
self.comment = None

self.styles_by_prefix = collections.defaultdict(dict)
for attr, value in element.attrib.items():
if attr.startswith(self.STYLE_PREFIXES):
prefix, name = attr.rsplit(self.ATTR_SEPARATOR, 1)
self.styles_by_prefix[prefix][name] = value
# TODO check for multiple type of fills (maybe by the same StyleInfo.attribute)

def collect(self, element, row_idx=1, col_idx=1):
cell = element.cell(row=row_idx, column=col_idx)
cell.value = self.value
if self.hyperlink is not None:
cell.hyperlink = self.hyperlink
if self.comment is not None:
cell.comment = self.comment

return cell

def apply_inline_styles(self, cell):
if self.number_format is not None:
cell.number_format = self.number_format

for prefix, kwargs in self.styles_by_prefix.items():
style_info = self.STYLE_ATTRIBUTES[prefix]
style_object = style_info.constructor(**kwargs)
setattr(cell, style_info.attribute, style_object)
return cell

def stylize(self, cell):
Expand Down
167 changes: 167 additions & 0 deletions tests/test_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import unicode_literals

import pytest
import openpyxl.styles

import surveyor.parse as parse

Expand Down Expand Up @@ -128,3 +129,169 @@ def test_check_inline_number_format(number_format):
workbook = workbook.process()

assert workbook.worksheets[0].cell(row=1, column=1).number_format == number_format


# noinspection PyUnresolvedReferences
@pytest.mark.parametrize("hyperlink", (
"http:",
"aaa",
"http://google.com",
))
def test_hyperlink(hyperlink):
xml = """
<workbook>
<sheet>
<table>
<tr>
<td hyperlink="{0}">1</td>
</tr>
</table>
</sheet>
</workbook>
""".format(hyperlink).strip()

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

assert workbook.worksheets[0].cell(row=1, column=1).hyperlink == hyperlink


# noinspection PyUnresolvedReferences
@pytest.mark.parametrize("comment, author", (
("", None),
("comment", None),
("comment", "author"),
))
def test_comment(comment, author):
attributes = "comment='{0}'".format(comment)
if author is not None:
attributes += " comment-author='{0}'".format(author)

xml = """
<workbook>
<sheet>
<table>
<tr>
<td {0}>1</td>
</tr>
</table>
</sheet>
</workbook>
""".format(attributes).strip()

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
if not comment:
assert cell.comment is None
else:
assert cell.comment.text == comment
if author:
assert cell.comment.author == author


def test_font_styles():
xml = """
<workbook>
<sheet>
<table>
<tr>
<td font-name="name" font-sz="21" font-family="4">1</td>
</tr>
</table>
</sheet>
</workbook>
"""

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
assert cell.font.name == "name"
assert cell.font.sz == 21
assert cell.font.family == 4


def test_pattern_fill_styles():
xml = """
<workbook>
<sheet>
<table>
<tr>
<td pattern-fill-patternType="solid" pattern-fill-fgColor="0d0d0d">1</td>
</tr>
</table>
</sheet>
</workbook>
"""

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
assert cell.fill.patternType == "solid"
assert cell.fill.fgColor == openpyxl.styles.Color("0d0d0d")


def test_gradient_fill_styles():
xml = """
<workbook>
<sheet>
<table>
<tr>
<td gradient-fill-degree="10" gradient-fill-left="5">1</td>
</tr>
</table>
</sheet>
</workbook>
"""

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
assert cell.fill.degree == 10
assert cell.fill.left == 5


def test_alignment_styles():
xml = """
<workbook>
<sheet>
<table>
<tr>
<td alignment-horizontal="right" alignment-vertical="top">1</td>
</tr>
</table>
</sheet>
</workbook>
"""

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
assert cell.alignment.horizontal == "right"
assert cell.alignment.vertical == "top"


def test_protection_styles():
xml = """
<workbook>
<sheet>
<table>
<tr>
<td protection-locked="0" protection-hidden="1">1</td>
</tr>
</table>
</sheet>
</workbook>
"""

workbook = parse.parse_fileobj(xml)
workbook = workbook.process()

cell = workbook.worksheets[0].cell(row=1, column=1)
assert not cell.protection.locked
assert cell.protection.hidden