Skip to content

Commit

Permalink
enable additional information in reports
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan-diodan committed Sep 17, 2019
1 parent aef9b35 commit c8ec6e8
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# proMAD <img src='proMAD/data/templates/logo.png?raw=true' align="right"/>
# proMAD <img src='https://github.com/theia-dev/proMAD/raw/master/proMAD/data/templates/logo.png' align="right"/>
Semiquantitative densitometric measurement of protein microarrays


Expand Down
2 changes: 1 addition & 1 deletion proMAD/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path

app_name = 'proMAD'
version_number = (0, 2, 4)
version_number = (0, 2, 5)
version = f'{version_number[0]}.{version_number[1]}.{version_number[2]}'
app_author = 'Anna Jaeschke; Hagen Eckert'
url = 'https://proMAD.dev'
Expand Down
4 changes: 2 additions & 2 deletions proMAD/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ def figure_contact_sheet_spot(self, position, kind='raw', file=None, max_size=No
im = Image.fromarray(image)
im.save(file, format='JPEG')

def report(self, file=None, norm='hist_raw', report_type=None):
def report(self, file=None, norm='hist_raw', report_type=None, additional_info=None):
"""
Summarize the results for a specific evaluation strategy in a report.
Expand Down Expand Up @@ -1291,5 +1291,5 @@ def report(self, file=None, norm='hist_raw', report_type=None):
warnings.warn(f'No file was given.', RuntimeWarning)
return

report_types[report_type]['func'](aa=self, file=file, norm=norm)
report_types[report_type]['func'](aa=self, file=file, norm=norm, additional_info=additional_info)

14 changes: 8 additions & 6 deletions proMAD/data/templates/short_report.tex
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,14 @@
\BLOCK{ endif }
\item [\VAR{dn['array_id']}:] \VAR{dv['array_id']}
\item [\VAR{dn['array_name']}:] \VAR{dv['array_name']} (\VAR{dv['array_type']})
\item [\VAR{dn['norm']}:] \VAR{dv['norm']}
\item [\VAR{dn['norm_name']}:] \VAR{dv['norm_name']} (libkey: \VAR{ dv.norm })
\item [\VAR{dn['norm_description']}:] \VAR{dv['norm_description']}
\item[\VAR{dn['unit']}:] \VAR{dv['unit']}
\item [\VAR{dn['unit']}:] \VAR{dv['unit']}
\BLOCK{ if additional_info }
\BLOCK{ for entry in additional_info }
\item [\VAR{entry.name}:] \VAR{entry.value}
\BLOCK{ endfor }
\BLOCK{ endif }
\end{description}
\end{center}

Expand All @@ -65,7 +70,7 @@
\begin{axis}[
width = \textwidth,
bar width=0.04*\textwidth,
height = 11cm,
height = 9cm,
ybar,
compat=newest,
ymajorgrids = true,
Expand Down Expand Up @@ -101,9 +106,6 @@
\caption{Overview and alignment check}\label{fig:alignment}
\end{figure}




\begin{longtable}{l|rcS[table-number-alignment = center]}
\caption{List of average values of analyte spots.}\label{tab:all}\\
\toprule
Expand Down
65 changes: 41 additions & 24 deletions proMAD/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from pylatexenc.latexencode import unicode_to_latex
import shutil

from typing import List, Dict

from openpyxl import Workbook
from openpyxl.chart import BarChart, Reference
from openpyxl.drawing.image import Image as opImage
Expand All @@ -30,6 +32,15 @@ class Report:
reac="estimate of the catalytic enzyme concentration"
)

norm_names = dict(
raw='Spot average',
raw_bg='Background corrected spot average',
local_bg='Ratio to local background',
hist_fg='Foreground to global background ratio',
hist_raw='Ratio to global background',
reac='Catalytic enzyme concentration'
)

norm_return = dict(
raw=('values',),
raw_bg=('values',),
Expand Down Expand Up @@ -76,6 +87,7 @@ def get_details(cls, aa, norm, tex=False):
array_name=aa.array_data['name'],
array_type=aa.array_data['array_type'],
array_id=aa.array_data['id'],
norm_name=cls.norm_names[norm],
norm=norm,
norm_description=cls.norm_descriptions[norm],
unit=cls.norm_unit[norm]
Expand All @@ -93,15 +105,16 @@ def get_details(cls, aa, norm, tex=False):
array_name='Array Name',
array_type='Array Type',
array_id='Array ID',
norm='Norm key',
norm_description='Norm description',
norm_name='Method',
norm='Method key',
norm_description='Method description',
unit='Unit'
)

return detail_values, detail_names

@classmethod
def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
def exp_excel(cls, aa, file, norm='hist_raw', additional_info=None):
"""
Export results in an Excel file.
Expand All @@ -113,10 +126,8 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
evaluation strategy selection (see ArrayAnalyse.evaluate)
file:
file can be a path to a file (a string), a path-like object, or a file-like object
name: str
membrane name
additional_info: list((str, str))
a list with pairs of additional information ('name', 'value')
additional_info: List[Dict]
list with dictionaries containing 'name' and 'value' key
"""

if not aa.is_finalized:
Expand Down Expand Up @@ -193,10 +204,7 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
else:
new_rows.append([entry['info'][0], pos, entry['value']])

if name is None:
ws['A1'] = 'Membrane Results'
else:
ws['A1'] = f'{name} Results'
ws['A1'] = 'Membrane Results'
ws['A1'].style = highlight
ws.append([])
ws.append(('Name', 'Position', 'Value'))
Expand Down Expand Up @@ -235,10 +243,7 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
data = aa.evaluate(norm=norm)
for entry in data:
new_rows.append([entry['info'][0], aa.get_position_string(entry['position'])] + [v for v in entry['value']])
if name is None:
ws['A1'] = 'Membrane Result Details'
else:
ws['A1'] = f'{name} Result Details'
ws['A1'] = 'Membrane Result Details'
ws['A1'].style = highlight
ws.append([])
ws.append(['Name', 'Position'] + [head.title() for head in cls.norm_return[norm]])
Expand All @@ -262,7 +267,7 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
detail_values, detail_names = cls.get_details(aa, norm)
info_list = ['date', 'time', 'skip',
'program', 'version', 'url', 'skip',
'array_name', 'array_type', 'array_id', 'norm', 'norm_description', 'unit'
'array_name', 'array_type', 'array_id', 'norm_name', 'norm', 'norm_description', 'unit'
]
tech_data_list = []
for key in info_list:
Expand All @@ -272,7 +277,7 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
tech_data_list.append((detail_names[key], detail_values[key]))

if additional_info:
tech_data_list += [('', '')] + additional_info
tech_data_list += [('', '')] + [(entry['name'], entry['value']) for entry in additional_info]

ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 15
Expand All @@ -289,7 +294,7 @@ def exp_excel(cls, aa, file, norm='hist_raw', name=None, additional_info=None):
file.write(tmp.read())

@classmethod
def exp_csv(cls, aa, file, norm='hist_raw'):
def exp_csv(cls, aa, file, norm='hist_raw', additional_info=None):
"""
Export results in a comma separated file.
Expand All @@ -298,9 +303,11 @@ def exp_csv(cls, aa, file, norm='hist_raw'):
aa:
ArrayAnalyse instant
norm: str
evaluation strategy selection (see ArrayAnalyse.evaluate)
evaluation strategy selection (see ArrayAnalyse.evaluate)
file:
file can be a path to a file (a string), a path-like object, or a file-like object (string based)
additional_info:
is ignored for CSV
"""

data = aa.evaluate(norm=norm)
Expand Down Expand Up @@ -330,8 +337,8 @@ def exp_latex(cls, aa, file, norm='hist_raw', additional_info=None):
evaluation strategy selection (see ArrayAnalyse.evaluate)
file:
can be a path to a file (a string), a path-like object, or a file-like object (string based)
additional_info: dict
dictionary with pairs of additional information
additional_info: List[Dict]
list with dictionaries containing name and value key
"""

overview = []
Expand Down Expand Up @@ -367,6 +374,11 @@ def exp_latex(cls, aa, file, norm='hist_raw', additional_info=None):
col_num = sum(aa.array_data['net_layout_x'])
row_num = sum(aa.array_data['net_layout_y'])

if additional_info:
for entry in additional_info:
entry['name'] = unicode_to_latex(entry['name'])
entry['value'] = unicode_to_latex(entry['value'])
info_dict['additional_info'] = additional_info
info_dict['ai'] = dict(
row=[(get_column_letter(row_num-n), f'{n/row_num + 0.5/row_num:.3f}') for n in range(row_num)],
col=[(str(n+1), f'{n/col_num + 0.5/col_num:.3f}') for n in range(col_num)]
Expand Down Expand Up @@ -399,8 +411,8 @@ def exp_json(cls, aa, file, norm='hist_raw', additional_info=None):
evaluation strategy selection (see ArrayAnalyse.evaluate)
file:
can be a path to a file (a string), a path-like object, or a file-like object (string based)
additional_info: dict
dictionary with pairs of additional information
additional_info: List[Dict]
list with dictionaries containing 'key' and 'value' key
"""

data = aa.evaluate(norm=norm)
Expand All @@ -423,7 +435,12 @@ def exp_json(cls, aa, file, norm='hist_raw', additional_info=None):
)

if additional_info:
export_dict['info'].update(additional_info)
info_update = {entry['key']: entry['value'] for entry in additional_info}
else:
info_update = {}

if additional_info:
export_dict['info'].update(info_update)

if isinstance(file, os.PathLike) or isinstance(file, str):
Path(file).write_text(json.dumps(export_dict, indent=4))
Expand Down
10 changes: 6 additions & 4 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,9 @@ def test_figure_alignment(self):
'87027ff24e5dc57ee7ba21c10969a4b5873fb980c92a09fb657b690a790b5532'])

self.aa.figure_alignment(file=self.out_folder / 'alignment.jpg', max_size=500)
self.assertEqual(hash_file(self.out_folder / 'alignment.jpg'),
'abc22b8192d0e2d5d10e5631ff50e827da29577c6567a3ee309701e90480cdf2')
self.assertIn(hash_file(self.out_folder / 'alignment.jpg'), [
'abc22b8192d0e2d5d10e5631ff50e827da29577c6567a3ee309701e90480cdf2',
'315efb4e3b7ce92dcfa0999b36014c2d63d87700b3d40caa54bb0c3a02799aeb'])

def test_figure_contact_sheet_spot(self):
save_mem = io.BytesIO()
Expand All @@ -314,8 +315,9 @@ def test_figure_contact_sheet_spot(self):
'953544ba7e4e0c70eaa70de6dc513b52849da5fe0e5b5adfedfcacf835232781'])

self.aa.figure_contact_sheet_spot(file=self.out_folder / 'contact_sheet_spot.jpg', max_size=150, position='A1')
self.assertEqual(hash_file(self.out_folder / 'contact_sheet_spot.jpg', skip=0),
'10ddfc8494dda2a3a0e29ed0f1fd996347a10d2b806d212358e390ecc26ef450')
self.assertIn(hash_file(self.out_folder / 'contact_sheet_spot.jpg', skip=0), [
'10ddfc8494dda2a3a0e29ed0f1fd996347a10d2b806d212358e390ecc26ef450',
'60f694b096a3ff2e0820bc896b1351dfa270cef59db8343e218a6acf997285a3'])

def test_figure_reaction_fit(self):
self.aa.figure_reaction_fit(file=self.out_folder / 'reaction_fit.png')
Expand Down
26 changes: 19 additions & 7 deletions tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ def setUpClass(cls):
cls.aa = ArrayAnalyse.load(cls.cases / 'save' / 'dump.tar')
cls.out_folder = cls.cases / 'testing_reports'
cls.out_folder.mkdir(exist_ok=True, parents=True)
cls.additional_info = [
dict(key='system_name', name='System name', value='UnitTest'),
dict(key='membrane_name', name='Membrane name', value='Membrane A')
]

@classmethod
def tearDownClass(cls):
Expand Down Expand Up @@ -68,7 +72,7 @@ def test_json(self):
compare_123 = {'position': [5, 11], 'info': ['IL-31', '386653', None], 'value': 0.9974935542164649,
'intercept': -0.0008484813336183776, 'r_squared': 0.9992305292185427}

self.aa.report(test_file_path, norm='raw')
self.aa.report(test_file_path, norm='raw', additional_info=self.additional_info)
data = json.loads(test_file_path.read_text())

self.assertAlmostEqual(data['result'][11]['values'][0], 0.023836107062511658, places=7)
Expand All @@ -79,6 +83,9 @@ def test_json(self):
self.assertAlmostEqual(data['result'][123]['values'][2], 0.027834284233911134, places=7)
self.assertAlmostEqual(data['result'][123]['values'][4], 0.03295996329826092, places=7)

for entry in self.additional_info:
self.assertEqual(data['info'][entry['key']], entry['value'])

save_mem = io.StringIO()
self.aa.report(save_mem, report_type='json')
save_mem.seek(0)
Expand All @@ -93,7 +100,7 @@ def test_json(self):

def test_latex(self):
test_file_path = self.out_folder / 'rep.tex'
self.aa.report(test_file_path)
self.aa.report(test_file_path, additional_info=self.additional_info)

self.assertTrue((self.out_folder / 'logo.png').is_file())
self.assertTrue((self.out_folder / 'figure_alignment.jpg').is_file())
Expand All @@ -104,20 +111,22 @@ def test_latex(self):
r'\node [anchor=east] (E) at (0, 0.550 ) {\small E};',
r'\node [anchor=south] (15) at (0.604, 1) {\small 15};',
r'BDNF &627~\href{https://www.ncbi.nlm.nih.gov/gene/?term=627}{\faExternalLink}& A15, A16 &0.9561\\',
r'Serpin E1 &5054~\href{https://www.ncbi.nlm.nih.gov/gene/?term=5054}{\faExternalLink}& I1, I2 &3.831\\'
r'Serpin E1 &5054~\href{https://www.ncbi.nlm.nih.gov/gene/?term=5054}{\faExternalLink}& I1, I2 &3.831\\',
r'\item [System name:] UnitTest'
]

generated_tex = test_file_path.read_text()
for part in compare:
self.assertIn(part, generated_tex)

def test_excel(self):
test_file_path = self.out_folder / 'rep.xls'
self.aa.report(test_file_path)
self.aa.report(test_file_path, additional_info=self.additional_info)
test_file_path = test_file_path.with_suffix('.xlsx')
wb_file = load_workbook(filename=test_file_path, read_only=True)

save_mem = io.BytesIO()
self.aa.report(save_mem, report_type='excel')
self.aa.report(save_mem, report_type='excel', additional_info=self.additional_info)
wb_mem = load_workbook(filename=save_mem, read_only=True)

for wb in [wb_mem, wb_file]:
Expand All @@ -143,14 +152,17 @@ def test_excel(self):
self.assertAlmostEqual(ws['C7'].value, 0.965608450754212, places=7)

ws = wb['Info']
self.assertEqual('A1:B15', ws.calculate_dimension())
self.assertEqual('A1:B19', ws.calculate_dimension())
info_data = []
for row in ws.rows:
for cell in row:
info_data.append(cell.value)
self.assertIn('ARY022B', info_data)
self.assertIn('hist_raw', info_data)
self.assertIn('Norm description', info_data)
self.assertIn('Method description', info_data)
for entry in self.additional_info:
self.assertIn(entry['name'], info_data)
self.assertIn(entry['value'], info_data)


if __name__ == '__main__':
Expand Down

0 comments on commit c8ec6e8

Please sign in to comment.