Skip to content

Commit

Permalink
first summary element
Browse files Browse the repository at this point in the history
  • Loading branch information
andped10 committed Nov 15, 2024
1 parent e95df0f commit ff1980c
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/easyreflectometry/summary/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .summary import Summary

__all__ = [Summary]
157 changes: 157 additions & 0 deletions src/easyreflectometry/summary/html_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
HTML_TEMPLATE = """<!DOCTYPE html>
<html>
<style>
th, td {
padding-right: 18px;
}
th {
text-align: left;
}
</style>
<body>
<table>
<tr></tr>
<!-- Summary title -->
<tr>
<td><h1>Summary</h1></td>
</tr>
<tr></tr>
<!-- Project -->
project_information_section
<!-- Phases -->
<tr>
<td><h3>Crystal data</h3></td>
</tr>
<tr></tr>
crystal_data_section
<!-- Experiments -->
<tr>
<td><h3>Data collection</h3></td>
</tr>
<tr></tr>
data_collection_section
<!-- Analysis -->
<tr>
<td><h3>Refinement</h3></td>
</tr>
<tr></tr>
<tr>
<td>Calculation engine</td>
<td>calculation_engine &mdash; https://www.cryspy.fr</td>
</tr>
<tr>
<td>Minimization engine</td>
<td>minimization_engine &mdash; https://lmfit.github.io/lmfit-py</td>
</tr>
<tr>
<td>Goodness-of-fit: reduced <i>&chi;</i><sup>2</sup></td>
<td>goodness_of_fit</td>
</tr>
<tr>
<td>No. of parameters: total, free, fixed</td>
<td>num_total_params,&nbsp;&nbsp;num_free_params,&nbsp;&nbsp;num_fixed_params</td>
</tr>
<tr>
<td>No. of constraints</td>
<td>0</td>
</tr>
<tr></tr>
</table>
</body>
</html>"""


HTML_PROJECT_INFORMATION_TEMPLATE = """
<tr>
<td><h3>Project information</h3></td>
</tr>
<tr></tr>
<tr>
<th>Title</th>
<th>project_title</th>
</tr>
<tr>
<td>Description</td>
<td>project_description</td>
</tr>
<tr>
<td>No. of experiments</td>
<td>num_experiments</td>
</tr>
<tr></tr>
"""

HTML_CRYSTAL_DATA_TEMPLATE = """
<tr>
<th>Phase datablock</th>
<th>phase_name</th>
</tr>
<tr>
<td>Crystal system, space group</td>
<td>crystal_system,&nbsp;&nbsp;<i>name_H_M_alt</i></td>
</tr>
<tr>
<td>Cell lengths: <i>a</i>, <i>b</i>, <i>c</i> (&#8491;)</td>
<td>length_a,&nbsp;&nbsp;length_b,&nbsp;&nbsp;length_c</td>
</tr>
<tr>
<td>Cell angles: <i>&#593;</i>, <i>&beta;</i>, <i>&#611;</i> (&deg;)</td>
<td>angle_alpha,&nbsp;&nbsp;angle_beta,&nbsp;&nbsp;angle_gamma</td>
</tr>
<tr></tr>
"""

HTML_DATA_COLLECTION_TEMPLATE = """
<tr>
<th>Experiment datablock</th>
<th>experiment_name</th>
</tr>
<tr>
<td>Radiation probe</td>
<td>radiation_probe</td>
</tr>
<tr>
<td>Radiation type</td>
<td>radiation_type</td>
</tr>
<tr>
<td>Measured range: min, max, inc (range_units)</td>
<td>range_min,&nbsp;&nbsp;range_max,&nbsp;&nbsp;range_inc</td>
</tr>
<tr>
<td>No. of data points</td>
<td>num_data_points</td>
</tr>
<tr></tr>
"""
115 changes: 115 additions & 0 deletions src/easyreflectometry/summary/summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from easyreflectometry import Project

from .html_templates import HTML_CRYSTAL_DATA_TEMPLATE
from .html_templates import HTML_DATA_COLLECTION_TEMPLATE
from .html_templates import HTML_PROJECT_INFORMATION_TEMPLATE
from .html_templates import HTML_TEMPLATE


class Summary:
def __init__(self, project: Project):
self._project = project

def compile_html_summary(self):
html = HTML_TEMPLATE
self._set_project_information_section(html)
self._set_crystal_data_section(html)
self._set_data_collection_section(html)
self._set_refinement_section(html)
return html

def _set_project_information_section(self, html: str) -> None:
html_project = ''
if self._project.created:
name = self._project._info['name']
short_description = self._project._info['short_description']
html_project = HTML_PROJECT_INFORMATION_TEMPLATE
html_project = html_project.replace('project_title', f'{name}')
html_project = html_project.replace('project_description', f'{short_description}')
# html_project = html_project.replace('num_phases', f'{self._project.status.phaseCount}')
html_project = html_project.replace('num_experiments', f'{len(self._project.experiments)}')

return html.replace('project_information_section', html_project)

def _set_crystal_data_section(self, html: str) -> None:
html_phases = []

for phase in self._project.dataBlocks:
phase_name = phase['name']['value']
crystal_system = phase['params']['_space_group']['crystal_system']['value']
name_H_M_alt = phase['params']['_space_group']['name_H-M_alt']['value']
length_a = phase['params']['_cell']['length_a']['value']
length_b = phase['params']['_cell']['length_b']['value']
length_c = phase['params']['_cell']['length_c']['value']
angle_alpha = phase['params']['_cell']['angle_alpha']['value']
angle_beta = phase['params']['_cell']['angle_beta']['value']
angle_gamma = phase['params']['_cell']['angle_gamma']['value']

html_phase = HTML_CRYSTAL_DATA_TEMPLATE
html_phase = html_phase.replace('phase_name', f'{phase_name}')
html_phase = html_phase.replace('crystal_system', f'{crystal_system}')
html_phase = html_phase.replace('name_H_M_alt', f'{name_H_M_alt}')
html_phase = html_phase.replace('length_a', f'{length_a}')
html_phase = html_phase.replace('length_b', f'{length_b}')
html_phase = html_phase.replace('length_c', f'{length_c}')
html_phase = html_phase.replace('angle_alpha', f'{angle_alpha}')
html_phase = html_phase.replace('angle_beta', f'{angle_beta}')
html_phase = html_phase.replace('angle_gamma', f'{angle_gamma}')
html_phases.append(html_phase)

html = html.replace('crystal_data_section', '\n'.join(html_phases))

def _set_data_collection_section(self, html: str) -> None:
html_experiments = []

for idx, experiment in enumerate(self._project.experiment.dataBlocksNoMeas):
experiment_name = experiment['name']['value']
radiation_probe = experiment['params']['_diffrn_radiation']['probe']['value']
radiation_type = experiment['params']['_diffrn_radiation']['type']['value']
radiation_type = radiation_type.replace('cwl', 'constant wavelength')
radiation_type = radiation_type.replace('tof', 'time-of-flight')
num_data_points = len(self._project.experiment._yMeasArrays[idx])
if '_pd_meas' in experiment['params']:
if 'tof_range_min' in experiment['params']['_pd_meas']: # pd-tof
range_min = experiment['params']['_pd_meas']['tof_range_min']['value']
range_max = experiment['params']['_pd_meas']['tof_range_max']['value']
range_inc = experiment['params']['_pd_meas']['tof_range_inc']['value']
range_units = '&micro;s'
elif '2theta_range_min' in experiment['params']['_pd_meas']: # pd-cwl
range_min = experiment['params']['_pd_meas']['2theta_range_min']['value']
range_max = experiment['params']['_pd_meas']['2theta_range_max']['value']
range_inc = experiment['params']['_pd_meas']['2theta_range_inc']['value']
range_units = '&deg;'
elif '_exptl_crystal' in experiment['loops']: # sg-cwl
cryspy_block_name = f'diffrn_{experiment_name}'
range_min = self._project.data._cryspyInOutDict[cryspy_block_name]['sthovl'].min()
range_max = self._project.data._cryspyInOutDict[cryspy_block_name]['sthovl'].max()
range_inc = '-'
range_units = 'Å⁻¹'

html_experiment = HTML_DATA_COLLECTION_TEMPLATE
html_experiment = html_experiment.replace('experiment_name', f'{experiment_name}')
html_experiment = html_experiment.replace('radiation_probe', f'{radiation_probe}')
html_experiment = html_experiment.replace('radiation_type', f'{radiation_type}')
html_experiment = html_experiment.replace('range_min', f'{range_min}')
html_experiment = html_experiment.replace('range_max', f'{range_max}')
html_experiment = html_experiment.replace('range_inc', f'{range_inc}')
html_experiment = html_experiment.replace('range_units', f'{range_units}')
html_experiment = html_experiment.replace('num_data_points', f'{num_data_points}')
html_experiments.append(html_experiment)

html = html.replace('data_collection_section', '\n'.join(html_experiments))

def _set_refinement_section(self, html: str) -> None:
num_free_params = self._project.fittables.freeParamsCount
num_fixed_params = self._project.fittables.fixedParamsCount
num_params = num_free_params + num_fixed_params
goodness_of_fit = self._project.status.goodnessOfFit
goodness_of_fit = goodness_of_fit.split(' → ')[-1]

html = html.replace('calculation_engine', f'{self._project.status.calculator}')
html = html.replace('minimization_engine', f'{self._project.status.minimizer}')
html = html.replace('goodness_of_fit', f'{goodness_of_fit}')
html = html.replace('num_total_params', f'{num_params}')
html = html.replace('num_free_params', f'{num_free_params}')
html = html.replace('num_fixed_params', f'{num_fixed_params}')
45 changes: 45 additions & 0 deletions tests/summary/test_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

from easyreflectometry import Project
from easyreflectometry.summary import Summary
from easyreflectometry.summary.html_templates import HTML_TEMPLATE


class TestSummary:
@pytest.fixture
def project(self) -> Project:
project = Project()
project.default_model()
return project

def test_constructor(self, project: Project) -> None:
# When Then
result = Summary(project)

# Expect
assert result._project == project

# def test_compile_html_summary(self, project: Project) -> None:
# # When
# summary = Summary(project)

# # Then
# result = summary.compile_html_summary()

# # Expect
# assert result is not None

def test_set_project_information_section(self, project: Project) -> None:
# When
project._created = True
summary = Summary(project)
html = 'project_information_section'

# Then
html = summary._set_project_information_section(html)

# Expect
assert (
html
== '\n<tr>\n <td><h3>Project information</h3></td>\n</tr>\n\n<tr></tr>\n\n<tr>\n <th>Title</th>\n <th>Example Project</th>\n</tr>\n<tr>\n <td>Description</td>\n <td>reflectometry, 1D</td>\n</tr>\n<tr>\n <td>No. of experiments</td>\n <td>0</td>\n</tr>\n\n<tr></tr>\n'
)

0 comments on commit ff1980c

Please sign in to comment.