public release 4.2.0 - see README.md and CHANGES.md for details
This commit is contained in:
229
pmsco/reports/base.py
Normal file
229
pmsco/reports/base.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
@package pmsco.reports.base
|
||||
base class of project reports
|
||||
|
||||
@author Matthias Muntwiler, matthias.muntwiler@psi.ch
|
||||
|
||||
@copyright (c) 2021 by Paul Scherrer Institut @n
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); @n
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
"""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
import pmsco.config as config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProjectReport(config.ConfigurableObject):
|
||||
"""
|
||||
base class of project reports
|
||||
|
||||
what do we need to know from project?
|
||||
- directories to resolve path names
|
||||
- database session factory
|
||||
- database job id
|
||||
- calculation id
|
||||
|
||||
usage:
|
||||
1. assign public attributes as necessary, leave defaults at None.
|
||||
2. call validate() if necessary.
|
||||
3. call set_database() if necessary.
|
||||
4. load data into result_data by calling select_data() or by modifying result_data directly.
|
||||
5. call create_report()
|
||||
|
||||
implementations of reports should not need to access any project members directly!
|
||||
|
||||
this class is under development
|
||||
"""
|
||||
|
||||
## @var _project
|
||||
# project object reference
|
||||
#
|
||||
|
||||
## @var _dba
|
||||
# database access (session factory)
|
||||
#
|
||||
|
||||
## @var _modes
|
||||
# compatible project modes
|
||||
#
|
||||
# set of modes in which the report can be used.
|
||||
# if an incompatible project is assigned, the enabled property is set to False.
|
||||
|
||||
## @var canvas
|
||||
# matplotlib canvas for graphical reports
|
||||
#
|
||||
# a FigureCanvas class reference of a matplotlib backend.
|
||||
# the default is matplotlib.backends.backend_agg.FigureCanvasAgg
|
||||
# which produces a bitmap file in PNG format.
|
||||
# some other options are
|
||||
# matplotlib.backends.backend_pdf.FigureCanvasPdf or
|
||||
# matplotlib.backends.backend_svg.FigureCanvasSVG.
|
||||
|
||||
## @var enabled
|
||||
# enable/disable the report
|
||||
#
|
||||
# the flag allows to temporarily enable or disable a report.
|
||||
#
|
||||
# the flag does not change the behaviour of the class.
|
||||
# it is up to the caller to respect it.
|
||||
#
|
||||
# the validate method can set the flag to False if the project is not compatible.
|
||||
|
||||
## @var report_dir
|
||||
# destination directory for report files
|
||||
#
|
||||
# this should be a Path or PathLike object.
|
||||
# by default, the project's directories['report'] entry is used.
|
||||
|
||||
## @var base_filename
|
||||
# base name of output files
|
||||
#
|
||||
# this value gets copied into the {base} placeholder of filename_format.
|
||||
#
|
||||
# by default, this is the stem of the output filename from the project settings.
|
||||
|
||||
## @var filename_format (str)
|
||||
# format of the output file name
|
||||
#
|
||||
# the format method of the string will be used to produce individual names.
|
||||
# possible fields are:
|
||||
# base, job_id, job_name, mode, model, gen, particle, param0, param1
|
||||
# where base corresponds to the stem of the output files produced by the calculators.
|
||||
#
|
||||
# a file extension according to the file format is appended.
|
||||
|
||||
## @var title_format (str)
|
||||
# title of the plot
|
||||
#
|
||||
# the format method of the string will be used to produce individual titles.
|
||||
# possible fields are:
|
||||
# base, job_id, job_name, mode, model, gen, particle, param0, param1
|
||||
|
||||
## @var trigger_levels (set of str)
|
||||
# events that may trigger creation of a report
|
||||
#
|
||||
# this attribute selects when a report is created.
|
||||
# the following values are currently recognized.
|
||||
# any other value disables the report.
|
||||
# further modes may be implemented in the future.
|
||||
#
|
||||
# - `model` - every time a new model has been calculated.
|
||||
# - `end` (default) - only once at the end of an optimization job.
|
||||
#
|
||||
# it is up to the calling code to respect this attribute.
|
||||
# it does not affect the behaviour of the class.
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._project = None
|
||||
self._dba = None
|
||||
self._modes = set()
|
||||
|
||||
self.enabled = True
|
||||
self.trigger_levels = {'end'}
|
||||
self.canvas = None
|
||||
self.report_dir = None
|
||||
self.base_filename = None
|
||||
self.filename_format = "${base}"
|
||||
self.title_format = ""
|
||||
|
||||
def get_session(self):
|
||||
"""
|
||||
get a new database session context handler
|
||||
|
||||
@return: context handler which provides an sqlalchemy database session,
|
||||
e.g. a pmsco.database.access.LockedSession() object.
|
||||
"""
|
||||
return self._dba.session()
|
||||
|
||||
def validate(self, project):
|
||||
"""
|
||||
validate the configuration (object properties).
|
||||
|
||||
@param project: pmsco.project.Project object,
|
||||
or any object that contains equivalent directories and output_file attributes.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
self._project = project
|
||||
if self._project:
|
||||
if self.report_dir is None:
|
||||
self.report_dir = self._project.directories['report']
|
||||
if self.base_filename is None:
|
||||
self.base_filename = self._project.output_file.name
|
||||
|
||||
if self.enabled and self._project.mode not in self._modes:
|
||||
self.enabled = False
|
||||
logger.warning(f"project mode {self._project.mode} incompatible with {self.__class__.__name__}")
|
||||
|
||||
if self.report_dir:
|
||||
Path(self.report_dir).mkdir(exist_ok=True)
|
||||
|
||||
def set_database(self, database_access):
|
||||
"""
|
||||
preparation steps for the report.
|
||||
|
||||
@param database_access: fully initialized pmsco.database.project.ProjectDatabase object
|
||||
which provides database sessions.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
self._dba = database_access
|
||||
|
||||
def resolve_template(self, template, mapping):
|
||||
"""
|
||||
resolve placeholders in template string
|
||||
|
||||
the function first tries to resolve using the project's resolve_path function
|
||||
and reverts to plain python Template strings if no project is set.
|
||||
|
||||
@param template: template string using ${name}-style place holders.
|
||||
name must be declared in project.directories or mapping.
|
||||
non-string objects (e.g. Path) are converted to a string using the str function.
|
||||
@param mapping: dictionary of name-value pairs for placeholders.
|
||||
@return: resolved string
|
||||
"""
|
||||
try:
|
||||
r = self._project.directories.resolve_path(template, mapping)
|
||||
except AttributeError:
|
||||
if template:
|
||||
r = Template(str(template)).substitute(mapping)
|
||||
else:
|
||||
r = ""
|
||||
return r
|
||||
|
||||
def select_data(self, jobs=-1, calcs=None):
|
||||
"""
|
||||
query data from the database
|
||||
|
||||
this method must be implemented by the sub-class.
|
||||
|
||||
@param jobs: filter by job.
|
||||
the argument can be a singleton or sequence of orm.Job objects or numeric id.
|
||||
if None, results from all jobs are loaded.
|
||||
if -1 (default), results from the most recent job (by datetime field) are loaded.
|
||||
|
||||
@param calcs: filter by result.
|
||||
the argument can be a singleton or sequence of CalcID objects.
|
||||
if None (default), all results are loaded.
|
||||
if -1, the most recent result is loaded.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_report(self):
|
||||
"""
|
||||
generate or update the report from stored data.
|
||||
|
||||
this method must be implemented by the sub-class.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
pass
|
||||
Reference in New Issue
Block a user