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

I1225 import fails #1230

Merged
merged 2 commits into from
Feb 13, 2019
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
8 changes: 7 additions & 1 deletion quantipy/sandbox/pptx/PptxChainClass.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,13 @@ def sig_test(self):
# Get the sig testing
sig_df = self.prepare_dataframe()
sig_df = sig_df.get_propstest()
self._sig_test = sig_df.df.values.tolist()
_sig_test = sig_df.df.values.tolist()

# Assume that all items in the list of sig tests has same length
check_list = map(lambda x: len(x), _sig_test)
assert check_list.count(check_list[0]) == len(check_list), 'List of sig test results is not uniform'

self._sig_test = [zip(*_sig_test)[i] for i in range(len(_sig_test[0]))]
return self._sig_test

@property
Expand Down
258 changes: 173 additions & 85 deletions quantipy/sandbox/pptx/PptxPainterClass.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import re
from lxml import etree
import warnings

# Imports from Python-PPTX
from pptx import Presentation
Expand Down Expand Up @@ -30,11 +31,11 @@
)

from PptxDefaultsClass import PptxDefaults
from PptxChainClass import float2String
import pandas as pd
import copy


# chartdata_from_dataframe taken from topy.core.pandas_pptx.py
def chartdata_from_dataframe(df, number_format="0%", xl_number_format='0.00%'):
"""
Return a CategoryChartData instance from the given Pandas DataFrame.
Expand Down Expand Up @@ -97,7 +98,6 @@ def get_parent(sub_categories, line, pos):
return cd


# return_slide_layout_by_name is taken from quantipy.core.builds.powerpoint.visual_editor.py
def return_slide_layout_by_name(pptx, slide_layout_name):
"""
Loop over the slide layout object and find slide layout by name, return slide layout
Expand All @@ -116,6 +116,18 @@ def return_slide_layout_by_name(pptx, slide_layout_name):
sld_layout=slide_layout_name))


def convertable(obj, func):
"""
Returns True if obj can be converted by func without an error.
"""

try:
func(obj)
return True
except ValueError:
return False


class PptxPainter(object):
"""
A convenience wrapper around the python-pptx library
Expand Down Expand Up @@ -169,6 +181,121 @@ def __init__(self, path_to_presentation, slide_layout=None, shape_properties=Non
'side_tables': {},
}

@staticmethod
def get_plot_values(plot):
"""
Return a list of dicts with serie name as dict-key and serie values as dict-value

Parameters
----------
plot: pptx.chart.plot._BasePlot

Returns
-------
list

"""
series = [
{series.name: [str(s) for s in series.values]}
for series in plot.series
]

return series

def show_data_labels(self, plot, decimals=0):
"""
Explicitly sets datalabels to allow for datalabel editing.

Parameters
----------
plot: pptx.chart.plot._BasePlot
The plot object for which datalabels need should be shown.
decimals: the number of decimals to show

Returns
-------
None
"""

# Get number format and font from data labels
data_labels = plot.data_labels
number_format = data_labels.number_format # '0%'
font = data_labels.font

plot_values = self.get_plot_values(plot)
for s, series in enumerate(plot_values):
values = [
value
for value in series.values()[0]
if convertable(value, float)
]
for v, value in enumerate(values):
if value is not None:
if number_format == '0%':
value = round(float(value) * 100, decimals)

str_value = float2String(value) + '%'
else:
str_value = str(value)
else:
str_value = ""
point = plot.series[s].points[v]
data_label = point.data_label
frame = data_label.text_frame
frame.text = str_value
pgraph = frame.paragraphs[0]
for run in pgraph.runs:
run.font.bold = font.bold
# run.font.color.rgb = font.color.rgb
# run.font.fill.fore_color.rgb = font.fill.fore_color.rgb

run.font.italic = font.italic
run.font.name = font.name
run.font.size = font.size
run.font.underline = font.underline

def edit_datalabel(self, plot, series, point, text, prepend=False, append=False, rgb=None):
"""
Add/append data label text.

Parameters
----------
plot: pptx.chart.plot._BasePlot
An instance of a Chart object.
serie: int
The serie where the data label should be edited
chart.series[serie]
point: int
The point where the data label should be edited
chart.series[serie].points[point]
text: basestring
The text to add/append to data label
prepend: bool
Set to True to prepend text to existing data label
append: bool
Set to True to append text to existing data label
rgb: tuple
Tuple with three ints defining each RGB color

Returns
-------
None

"""
data_label = plot.series[series].points[point].data_label
frame = data_label.text_frame

run = frame.paragraphs[0].runs[0]
original_text = frame.text
if prepend:
run.text = u'{}{}'.format(text, original_text)
elif append:
run.text = u'{}{}'.format(original_text, text)
else:
run.text = text
if rgb is not None:
run.font.color.rgb = RGBColor(*rgb)

def queue_slide_items(self, pptx_chain, slide_items):
"""
Helper function to queue a full automated slide.
Expand Down Expand Up @@ -241,10 +368,28 @@ def queue_slide_items(self, pptx_chain, slide_items):
side_table_draft['values_suffix_columns'] = pct_index
self.queue_side_table(settings=side_table_draft)
if slide_item.startswith('chart'):
sig_test = False
cell_items = slide_item.split(':')[1]

'''
Makes no sense to actually have 'test' as a cell_item.
Will remove it from cell_items and set flag sig_test as True
'''
cell_items = cell_items.split(',')
if 'test' in cell_items:
sig_test = True
pptx_chain.add_test_letter_to_column_labels()
pptx_chain.chart_df = pptx_chain.prepare_dataframe()
cell_items.remove('test')
cell_items = ','.join(cell_items)

pptx_frame = pptx_chain.chart_df.get(cell_items)
if not pptx_frame().empty:
chart_draft = self.draft_autochart(pptx_frame(), pptx_chain.chart_type)
if sig_test:
chart_draft['sig_test_visible'] = True
chart_draft['sig_test_results'] = pptx_chain.sig_test

self.queue_chart(settings=chart_draft)

self._check_shapes()
Expand Down Expand Up @@ -879,7 +1024,11 @@ def add_chart(self, slide,

# Number format
number_format='0.00%',
xl_number_format='0.00%'
xl_number_format='0.00%',

# Sig test
sig_test_visible = False,
sig_test_results = None,
):
"""
Adds a chart to the given slide and sets all properties for the chart
Expand Down Expand Up @@ -1044,6 +1193,27 @@ def add_chart(self, slide,
data_labels.number_format = data_labels_num_format
data_labels.number_format_is_linked = data_labels_num_format_is_linked

if not sig_test_results: sig_test_visible = False
if len(dataframe.columns) == 1: sig_test_visible = False
if sig_test_visible:
self.show_data_labels(plot, decimals=0)
for serie, column in enumerate(sig_test_results[::-1]):
for point, test_result in enumerate(column[::-1]):
if not isinstance(test_result, basestring): continue
for text in ['*.',
'*',
'**.',
'**',
'\'@L\'.',
'\'@L\'',
'\'@H\'.',
'\'@H\'',
]:
test_result = test_result.replace(text,'')
if test_result == '': continue
text = u' ({})'.format(test_result)
self.edit_datalabel(plot, serie, point, text, prepend=False, append=True)

# # ================================ series
# for i, ser in enumerate(dataframe.columns):
# ser = plot.series[i]
Expand Down Expand Up @@ -1416,85 +1586,3 @@ def add_net(slide,
#paragraph.line_spacing = Pt(6)
cell.text = str(subval)

@staticmethod
def edit_datalabel(chart, series, point, text, prepend=False, append=False,
position=None, rgb=None):
"""
Add/append data label text.
"""
data_label = chart.series[series].points[point].data_label
frame = data_label.text_frame
if prepend:
original_text = frame.text
frame.text = text
pgraph = frame.add_paragraph()
pgraph.text = original_text
run = frame.paragraphs[0].runs[0]
elif append:
pgraph = frame.add_paragraph()
pgraph.text = text
run = frame.paragraphs[-1].runs[0]
else:
frame.text = text
run = frame.paragraphs[0].runs[0]
if rgb is not None:
run.font.color.rgb = RGBColor(*rgb)
if position is not None:
data_label.position = data_label_pos_dct(position)

def show_data_labels(self, chart, position=None):
"""
Explicitly sets datalabels to allow for datalabel editing.

Parameters
----------
chart : pptx.chart.chart.Chart
The chart object for which datalabels need should be shown.
position : str, default=None
The position, relative to the data point, that the datalabel
should be appear in. Must be one of the following, or None:
'above', 'below', 'best', 'center', 'inside_base',
'inside_end', 'left', 'mixed', 'outside_end', 'right'
If None then the position already set for the chart will
be used.

Returns
-------
None
"""

chart_values = self.get_chart_values(chart)
for s, series in enumerate(chart_values):
values = [
value
for value in series.values()[0]
if self.convertable(value, float)
]
for v, value in enumerate(values):
point = chart.series[s].points[v]
frame = point.data_label.text_frame
frame.text = "" if value is None else str(value)
if position is not None:
point.data_label.position = data_label_pos_dct(position)

@staticmethod
def get_chart_values(chart):

series = [
{series.name: [str(s) for s in series.values]}
for series in chart.series
]

return series

@staticmethod
def convertable(obj, func):
"""
Returns True if obj can be convertedby func without an error.
"""

try:
func(obj)
return True
except ValueError:
return False