from reportlab.platypus import SimpleDocTemplate, Flowable, Preformatted, Spacer, PageBreak
from reportlab.graphics.shapes import Drawing
from reportlab.lib import pagesizes
from reportlab.lib.units import cm
from reportlab.platypus.paragraph import Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.graphics import renderPDF
from reportlab.platypus.doctemplate import PageTemplate
from reportlab.platypus.frames import Frame
from functools import partial
from datetime import date
import matplotlib.pyplot as plt
from autodeer.DEER_analysis import DEERanalysis_plot, plot_overlap, DEERanalysis_plot_pub
from autodeer.Relaxation import plot_1Drelax
from svglib.svglib import svg2rlg
from io import BytesIO
import os
import numpy as np
import deerlab as dl
from collections import OrderedDict
[docs]
styles = getSampleStyleSheet()
[docs]
package_dir = os.path.dirname(os.path.split(__file__)[0])
[docs]
class SvgFlowable(Flowable):
"""Convert byte streasm containing SVG into a Reportlab Flowable."""
def __init__(self, svg: BytesIO) -> None:
"""Convert SVG to RML drawing on initializtion."""
svg.seek(0)
[docs]
self.drawing: Drawing = svg2rlg(svg)
[docs]
self.width: int = self.drawing.minWidth()
[docs]
self.height: int = self.drawing.height
self.drawing.setProperties({"vAlign": "CENTER", "hAlign": "CENTER"})
[docs]
def wrap(self, *_args):
"""Return diagram size."""
return (self.width, self.height)
[docs]
def draw(self) -> None:
"""Render the chart."""
renderPDF.draw(self.drawing, self.canv, 0, 0)
[docs]
class Reporter():
def __init__(self, filepath, pagesize='A4') -> None:
# Build the pdf
if pagesize == 'A4':
PAGESIZE = pagesizes.portrait(pagesizes.A4)
elif pagesize =='Letter':
PAGESIZE = pagesizes.portrait(pagesizes.LETTER)
else:
raise ValueError("Only pagesizes of 'A4' or 'Letter' are supported")
[docs]
self.pdf = SimpleDocTemplate(filepath, pagesize=PAGESIZE,
leftMargin = 2.2 * cm,
rightMargin = 2.2 * cm,
topMargin = 2.5 * cm,
bottomMargin = 2.5 * cm)
[docs]
self.story = OrderedDict() # possibly change to a normal dict in the future
frame = Frame(self.pdf.leftMargin, self.pdf.bottomMargin, self.pdf.width, self.pdf.height, id='normal')
template = PageTemplate(id=None, frames=frame, onPage=self.header, onPageEnd=self.footer)
self.pdf.addPageTemplates([template])
pass
[docs]
def _build(self):
# Convert ordered dict to list of values and then flatten
flat_list = []
for key in self.story:
flat_list += self.story[key]
self.pdf.build(flat_list)
[docs]
def add_title(self, key, title):
self.story[key] = [Paragraph(title, styles['Title'])]
[docs]
def add_new_section(self, key, title):
substory = []
substory.append(Paragraph(title, styles['Heading2']))
self.story[key] = substory
[docs]
def add_text(self, key, text, title = None):
if title is not None:
self.story[key].append(Paragraph(title, styles['Heading4']))
self.story[key].append(Paragraph(text, styles['Normal']))
[docs]
def add_code_block(self, key, code, title = None):
if title is not None:
self.story[key].append(Paragraph(title, styles['Heading4']))
self.story[key].append(Preformatted(code, styles['Code']))
[docs]
def add_space(self, key, height=5):
self.story[key].append(Spacer(width=100, height=height))
[docs]
def add_page_break(self, key):
self.story[key].append(PageBreak())
[docs]
def add_table(self, key, lists):
"""Generates a table as a reportlab flowable from a list of lists
"""
t = Table(lists)
t.setStyle(TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),]))
self.story[key].append(t)
[docs]
def create_report(save_path, Results:dict, SpectrometerInfo:dict=None, UserInputs:dict=None, Pulses=None):
report = Reporter(filepath=save_path,pagesize='A4')
report.add_title('title','autoDEER Report')
if SpectrometerInfo is not None:
report.add_new_section('spec',' Spectrometer')
report.add_text('spec', 'Local Name: ' + SpectrometerInfo['Local Name'])
report.add_text('spec', 'Manufacturer: ' + SpectrometerInfo['Manufacturer'])
report.add_text('spec', 'Model: ' + SpectrometerInfo['Model'])
report.add_text('spec', 'Resonator: ' + SpectrometerInfo['Resonator'])
report.add_space('spec', height=10)
report.add_new_section('inputs',' User Inputs')
if UserInputs is not None:
report.add_text('inputs', 'Sample Name: ' + UserInputs['Sample Name'])
report.add_text('inputs', f"Temperature: {UserInputs['Temp']:.3f} K")
report.add_text('inputs', f"Max Time: {UserInputs['MaxTime']} hrs")
report.add_page_break('inputs')
if 'fieldsweep' in Results:
report.add_new_section('fieldsweep',' Field Sweep')
fig,axs = plt.subplots(1,1,figsize=(5, 3))
Results['fieldsweep'].plot(axs=axs, fig=fig)
report.add_figure('fieldsweep', fig)
report.add_text('fieldsweep', f"Gyromagnetic Ratio: {Results['fieldsweep'].gyro:.3g} GHz/G")
if hasattr(Results['fieldsweep'], 'results'):
report.add_space('fieldsweep')
report.add_code_block('fieldsweep', Results['fieldsweep'].results.__str__(), title='Fit Results')
report.add_page_break('fieldsweep')
if 'respro' in Results:
report.add_new_section('respro',' Resonator Profile')
fig,axs = plt.subplots(1,1,figsize=(5, 5))
fitresult = Results['respro']
if 'fieldsweep'in Results:
fitresult.plot(fieldsweep=Results['fieldsweep'],axs=axs, fig=fig);
else:
fitresult.plot(axs=axs,fig=fig)
report.add_figure('respro', fig)
if hasattr(Results['respro'], 'results'):
report.add_space('respro')
report.add_code_block('respro', Results['respro'].results.__str__(), title='Fit Results')
report.add_page_break('respro')
if Pulses is not None:
pump_pulse = Pulses['pump_pulse']
exc_pulse = Pulses['exc_pulse']
ref_pulse = Pulses['ref_pulse']
report.add_new_section('pulses',' Optimised Pulses')
fig,axs = plt.subplots(1,1,figsize=(5, 5))
plot_overlap(Results['fieldsweep'], pump_pulse, exc_pulse,ref_pulse, axs=axs,fig=fig)
report.add_figure('pulses', fig)
report.add_space('pulses')
report.add_code_block('pulses', exc_pulse.__str__(), title='Excitation Pulse')
report.add_code_block('pulses', ref_pulse.__str__(), title='Refocusing Pulse')
report.add_code_block('pulses', pump_pulse.__str__(), title='Pump Pulse')
if 'relax' in Results:
report.add_new_section('relax',' Relaxation')
fig,axs = plt.subplots(1,1,figsize=(5, 5))
Results['relax'].plot(axs=axs, fig=fig)
report.add_figure('relax', fig)
if hasattr(Results['relax'], 'results'):
report.add_space('relax')
report.add_code_block('relax', Results['relax'].results.__str__(), title='Fit Results')
report.add_page_break('relax')
if 'quickdeer' in Results:
report.add_new_section('quickdeer',' QuickDEER')
fig,axs = plt.subplot_mosaic([['Primary_time'],
['Primary_dist']], figsize=(6,6))
DEERanalysis_plot(Results['quickdeer'], background=True, ROI=Results['quickdeer'].ROI, axs= axs, fig=fig,text=False)
report.add_figure('quickdeer', fig)
if 'quickdeer' in Results:
report.add_space('quickdeer')
report.add_code_block('quickdeer', Results['quickdeer'].__str__(), title='Fit Results')
report.add_page_break('quickdeer')
report._build()
pass