Commit 5ea4529b authored by Sebastian Heimann's avatar Sebastian Heimann

addd report command

parent 3dda57ff
......@@ -30,6 +30,7 @@ setup(
'grond.optimizers.highscore',
'grond.analysers',
'grond.listeners',
'grond.report',
],
python_requires='>=3.5',
entry_points={
......@@ -39,6 +40,9 @@ setup(
},
package_dir={'grond': 'src'},
package_data={'grond': ['baraddur/templates/*.html',
'baraddur/res/*']},
'baraddur/res/*',
'report/app/css/*.css',
'report/app/js/*.js',
'report/app/*.html']},
data_files=[] + grond_completion(),
)
......@@ -34,6 +34,8 @@ subcommand_descriptions = {
'movie': 'visualize optimizer evolution',
'baraddur': 'start Barad-dur plotting, watching this directory',
'export': 'export results',
'report': 'create result report',
'report-index': 'create report index',
'qc-polarization': 'check sensor orientations with polarization analysis',
}
......@@ -50,6 +52,8 @@ subcommand_usages = {
'movie': 'movie <rundir> <xpar> <ypar> <filetemplate> [options]',
'baraddur': 'plot-server',
'export': 'export (best|mean|ensemble|stats) <rundirs> ... [options]',
'report': 'report <rundir> [options]',
'report-index': 'report <reportdir> [options]',
'qc-polarization': 'qc-polarization <configfile> <eventname> '
'<targetconfigname> [options]',
}
......@@ -74,6 +78,8 @@ Subcommands:
plot %(plot)s
movie %(movie)s
export %(export)s
report %(report)s
report-index %(report_index)s
qc-polarization %(qc_polarization)s
baraddur %(baraddur)s
......@@ -400,10 +406,8 @@ def command_go(args):
config_path = args[0]
config = grond.read_config(config_path)
print('Available Events:')
for event_name in grond.get_event_names(config):
print('* %s' % event_name)
print('\n')
print(event_name)
help_and_die(parser, 'missing arguments')
......@@ -655,6 +659,47 @@ def command_export(args):
die(str(e))
def command_report(args):
import grond.report
def setup(parser):
pass
parser, options, args = cl_parse('report', args, setup)
if len(args) < 1:
help_and_die(parser, 'arguments required')
rundirs = args
try:
for rundir in rundirs:
grond.report.report(rundir)
except grond.GrondError as e:
die(str(e))
def command_report_index(args):
import grond.report
def setup(parser):
pass
parser, options, args = cl_parse('report-index', args, setup)
if len(args) < 1:
help_and_die(parser, 'arguments required')
report_base_path = args[0]
try:
grond.report.report_index(report_base_path)
except grond.GrondError as e:
die(str(e))
def command_qc_polarization(args):
def setup(parser):
......
from __future__ import print_function
import os
import glob
import sys
import logging
import time
......@@ -15,7 +16,7 @@ from pyrocko import parimap, model, marker as pmarker
from .dataset import DatasetConfig, NotFound
from .problems.base import ProblemConfig, Problem, \
load_problem_info_and_data, load_problem_data
load_problem_info_and_data, load_problem_data, load_problem_info
from .optimizers.base import OptimizerConfig, BadProblem
from .targets.base import TargetGroup
......@@ -213,7 +214,7 @@ def forward(rundir_or_config_path, event_names):
if not event_names:
return
if os.path.isdir(rundir_or_config_path):
if op.isdir(rundir_or_config_path):
rundir = rundir_or_config_path
config = guts.load(
filename=op.join(rundir, 'config.yaml'))
......@@ -856,7 +857,7 @@ def export(what, rundirs, type=None, pnames=None, filename=None):
if out is not sys.stdout:
out.close()
__all__ = '''
EngineConfig
......
......@@ -2089,7 +2089,6 @@ def plot_result(dirname, plotnames_want,
for fig in figs:
plt.close(fig)
if 7 != len({
'fits',
'fits_statics',
......
from .base import * # noqa
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
table.have-op-icons tr span.op-icon,
table.have-op-icons tr span.op-icon-sec {
visibility: hidden;
}
table.have-op-icons tr:hover span.op-icon,
table.have-op-icons tr:hover span.op-icon-sec {
visibility: visible;
}
table.have-op-icons span.op-icon-sec {
color: #999;
}
table.have-op-icons td:hover span.op-icon-sec {
color: black;
}
textarea.code {
font-family: monospace;
}
.clickable {
cursor: pointer;
}
div.section-menu {
float: right;
}
tr.no-hover:hover th,
tr.no-hover:hover td {
background-color: white;
}
span.author-and-date {
font-size: small;
color: #999;
}
.spacedrow {
margin: 5px;
}
.lightgray {
background-color: #999;
}
.lightgray:hover {
background-color: #555;
}
.pagination {
margin: 0px;
}
.droptoggle {
margin-right: 5px;
}
del {
background-color: #efcbcb;
}
ins {
background-color: #cae7ca;
}
tr.has-manual-comments {
font-weight: bold;
}
table.stats tt {
white-space: pre;
}
<!DOCTYPE html>
<html lang="en" ng-app="reportApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Earthquake reports</title>
<!-- CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/local.css" rel="stylesheet">
<!-- JS -->
<script type="text/javascript" src="js/js-yaml.min.js"></script>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/angular.js"></script>
<script type="text/javascript" src="js/angular-route.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
</head>
<body>
<div ng-controller="NavigationController">
<nav class="navbar navbar-inverse navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li ng-class="active('/list')"><a href="#/list">Runs</a></li>
</ul>
</div>
</div>
</nav>
<div ng-view></div>
<div class="container footer">
<hr />
<div class="row">
<p class="text-center">Busted with Grond</p>
</div>
</div>
</div>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
'use strict';
function copy_properties(source, target) {
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
if (source[prop] != null) {
target[prop] = source[prop];
}
}
}
}
function Dummy(obj) { copy_properties(obj, this); }
function ReportEntry(obj) { copy_properties(obj, this); }
var yaml_type_map = [
['!grond.ReportEntry', Dummy],
['!grond.ReportPlot', Dummy],
['!grond.ParameterStats', Dummy],
['!grond.TargetAnalysisResult', Dummy],
['!grond.ResultStats', Dummy],
['!grond.WaveformMisfitTarget', Dummy],
['!grond.WaveformMisfitConfig', Dummy],
['!grond.CMTProblem', Dummy],
['!pf.MTSource', Dummy],
['!pf.HalfSinusoidSTF', Dummy],
];
function make_constructor(type) {
var type = type;
var construct = function(data) {
return new type(data);
};
return construct;
}
var yaml_types = [];
for (var i=0; i<yaml_type_map.length; i++) {
var type = yaml_type_map[i][1]
var t = new jsyaml.Type(yaml_type_map[i][0], {
kind: 'mapping',
instanceOf: type,
construct: make_constructor(type),
});
yaml_types.push(t);
}
var report_schema = jsyaml.Schema.create(yaml_types);
function parse_fields_float(fields, input, output, error, factor) {
parse_fields(fields, input, output, error, factor, parseFloat);
}
function parse_fields_int(fields, input, output, error) {
parse_fields(fields, input, output, error, 1.0, parseInt);
}
function parse_fields(fields, input, output, error, factor, parse) {
for (var i=0; i<fields.length; i++) {
var field = fields[i];
if (input[field].length == 0) {
val = null;
} else {
var val = parse(input[field]) * factor;
if (val.isNaN) {
error[field] = true;
return false;
}
}
output[field] = val;
}
}
angular.module('reportApp', ['ngRoute'])
.controller('NavigationController', function($scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
$scope.active = function(path) {
return (path === $location.path().substr(0,path.length)) ? 'active' : '';
};
})
.config(function($routeProvider, $locationProvider) {
$locationProvider.hashPrefix('');
$routeProvider
.when('/reports/', {
controller: 'ReportListController',
templateUrl: 'report_list.html',
})
.when('/reports/:report_path*/', {
controller: 'ReportController',
templateUrl:'report.html',
})
.otherwise({
redirectTo: '/reports/',
});
})
.factory('YamlDoc', function($http) {
var funcs = {};
funcs.query = function(path, loaded, options) {
$http.get(path).then(
function(response) {
var doc = jsyaml.safeLoad(response.data, options);
loaded(doc);
}
);
};
return funcs;
})
.factory('YamlMultiDoc', function($http) {
var funcs = {};
funcs.query = function(path, loaded, options) {
$http.get(path).then(
function(response) {
jsyaml.safeLoadAll(response.data, loaded, options);
}
);
};
return funcs;
})
.controller('ReportListController', function($scope, YamlMultiDoc) {
$scope.report_entries = [];
YamlMultiDoc.query(
'report_list.yaml',
function(doc) { $scope.report_entries.push(doc); console.log(doc); },
{schema: report_schema});
})
.controller('ReportController', function(
$scope, YamlDoc, YamlMultiDoc, $routeParams) {
$scope.stats = null;
$scope.plots = [];
$scope.path = $routeParams.report_path;
YamlDoc.query(
$scope.path + '/stats.yaml',
function(doc) { $scope.stats = doc; console.log(doc); },
{schema: report_schema});
YamlMultiDoc.query(
$scope.path + '/plots.yaml',
function(doc) { $scope.plots.push(doc); },
{schema: report_schema});
})
.filter('eround', function() {
return function(input, std) {
if (std > 0) {
var ndig = - Math.floor(Math.log10(std)) + 2;
var factor = Math.pow(10, ndig);
return Math.round(input * factor) / factor;
} else {
return input;
}
};
})
.filter('dotalign', function() {
return function(input) {
input = input.toString();
var dotpos = input.indexOf('.');
if (dotpos == -1) {
dotpos = input.length;
}
var fill = ' ';
return fill.repeat(Math.max(0, 5 - dotpos)) + input;
};
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<div class="container">
<h1>Report for run {{ path }}</h1>
<h2>Results</h2>
<table class="stats">
<tr>
<th>parameter</th>
<th>best</th>
<th>mean</th>
<th>std</th>
<th>min</th>
<th>5%</th>
<th>16%</th>
<th>84%</th>
<th>95%</th>
<th>max</th>
</tr>
<tr ng-repeat="pstats in stats.parameter_stats_list">
<td>{{ pstats.name }}</td>
<td><tt>{{ pstats.best | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.mean | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.std | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.minimum | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.percentile5 | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.percentile16 | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.percentile84 | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.percentile95 | eround:pstats.std | dotalign }}</tt></td>
<td><tt>{{ pstats.maximum | eround:pstats.std | dotalign }}</tt></td>
</tr>
</table>
<h2>Plots</h2>
<div ng-repeat="plot in plots">
<h3>Plot: {{ plot.name }}</h3>
<div ng-repeat="file_name in plot.file_names">
<img style="max-width: 100%;" src="{{ path }}/plots/{{ file_name }}" />
</div>
</div>
</div>
<div class="container">
<h1>Reports</h1>
<div ng-repeat="report_entry in report_entries">
<p>
<a href="#/reports/{{ report_entry.path }}/">{{ report_entry.path }}</a>
</p>
</div>
</div>
import os.path as op
import glob
import shutil
import os
from pyrocko import guts, util
from pyrocko.guts import Object, List, String
from grond.meta import Path, expand_template
from grond import core
from grond.problems.base import load_problem_info
guts_prefix = 'grond'
class ReportConfig(Object):
reportdir_template = Path.T(
default='reports/${event_name}/${problem_name}')
plot_names = List.T(
String.T(),
default=['solution', 'fits', 'jointpar', 'hudson', 'sequence',
'fits_ensemble', 'contributions', 'bootstrap'])
class ReportPlot(Object):
name = String.T()
file_names = List.T(String.T())
class ReportPlots(Object):
plots = List.T(ReportPlot.T())
def report(rundir, report_config=None):
if report_config is None:
report_config = ReportConfig()
config = guts.load(
filename=op.join(rundir, 'config.yaml'))
config.set_basepath(rundir)
problem = load_problem_info(rundir)
reportdir = expand_template(
report_config.reportdir_template,
dict(
event_name=problem.base_source.name,
problem_name=problem.name))
util.ensuredir(reportdir)
core.export('stats', [rundir], filename=op.join(reportdir, 'stats.yaml'))
plots_dir_out = op.join(reportdir, 'plots')
util.ensuredir(plots_dir_out)
rps = []
for plot_name in report_config.plot_names:
plot_path_pat = op.join(rundir, 'plots', plot_name + '-*.png')
plot_paths = sorted(glob.glob(plot_path_pat))
plot_basenames = []
for plot_path in plot_paths:
plot_basename = op.basename(plot_path)
plot_path_out = op.join(plots_dir_out, plot_basename)
shutil.copy(plot_path, plot_path_out)
plot_basenames.append(plot_basename)
rp = ReportPlot(
name=plot_name,
file_names=plot_basenames)
rps.append(rp)
guts.dump_all(rps, filename=op.join(reportdir, 'plots.yaml'))
def iter_report_dirs(report_base_path):
for path, dirnames, filenames in os.walk(report_base_path):
for dirname in dirnames:
dirpath = op.join(path, dirname)
stats_path = op.join(dirpath, 'stats.yaml')
if op.exists(stats_path):
yield dirpath
class ReportEntry(Object):
path = String.T()
def copytree(src, dst):
names = os.listdir(src)
if not op.exists(dst):
os.makedirs(dst)
for name in names:
srcname = op.join(src, name)
dstname = op.join(dst, name)
if op.isdir(srcname):
copytree(srcname, dstname)
else:
shutil.copy(srcname, dstname)
def report_index(report_base_path):
reports = []
for report_path in iter_report_dirs(report_base_path):
report_relpath = op.relpath(report_path, report_base_path)
reports.append(ReportEntry(
path=report_relpath))
guts.dump_all(
reports,