Skip to content
Open
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ramutils/test/test_data/*.png
out.html
.cache/
ramutils/test/plotting/sample_plots/
Expand All @@ -9,7 +10,6 @@ scratch/
*.pdf
*.synctex.gz
*.swp
*.png
*.zip
*.ipynb_checkpoints/

Expand Down
19 changes: 7 additions & 12 deletions demos/report_generation.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
from __future__ import print_function

import os.path
from ramutils.parameters import FilePaths, FRParameters, PS5Parameters
from ramutils.parameters import FilePaths, FRParameters
from ramutils.pipelines.report import make_report

from ramutils.tasks import memory

memory.cachedir = "~"


memory.cachedir = "scratch/ramutils_test"

paths = FilePaths(
root='/',
dest='/scratch/zduey/',
data_db='/scratch/zduey/'
root="~/mnt/rhino",
dest="scratch/ramutils_test/",
data_db="/scratch/report_database",
)

params = FRParameters()
make_report("R1001P", "FR1", paths, exp_params=params, stim_params=None,
joint_report=False, rerun=False, sessions=None)
make_report("R1384J", "FR5", paths, exp_params=params, stim_params=None,
joint_report=False, rerun=False, sessions=[0], clinical=True)
11 changes: 8 additions & 3 deletions ramutils/pipelines/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def make_report(subject, experiment, paths, joint_report=False,
retrain=False, stim_params=None, exp_params=None,
sessions=None, vispath=None, rerun=False,
trigger_electrode=None, use_classifier_excluded_leads=False,
pipeline_name="report"):
pipeline_name="report", clinical=False):
""" Constructs a report and saves out all the necessary data to re-construct the report

This pipeline should be used for generating single session reports for both record-only and
Expand Down Expand Up @@ -59,6 +59,9 @@ def make_report(subject, experiment, paths, joint_report=False,
classifier training
pipeline_name : str
Name to use for status updates.
clinical: bool
If True, builds a demo clinical report instead of the internal RAM
report

Returns
-------
Expand Down Expand Up @@ -117,7 +120,8 @@ def make_report(subject, experiment, paths, joint_report=False,
pre_built_results['target_selection_table'],
pre_built_results['classifier_evaluation_results'],
paths.dest,
hmm_results=pre_built_results['hmm_results'])
hmm_results=pre_built_results['hmm_results'],
clinical=clinical)
return report.compute()

final_pairs = generate_pairs_for_classifier(ec_pairs, excluded_pairs)
Expand Down Expand Up @@ -182,7 +186,8 @@ def make_report(subject, experiment, paths, joint_report=False,
report = build_static_report(subject, experiment, data.session_summaries,
data.math_summaries, data.target_selection_table,
data.classifier_evaluation_results,
hmm_results=output, dest=paths.dest)
hmm_results=output, dest=paths.dest,
clinical=clinical)

if vispath is not None:
report.visualize(filename=vispath)
Expand Down
41 changes: 31 additions & 10 deletions ramutils/reports/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
import json
import os.path as osp
import random
import itertools

from itertools import compress
from jinja2 import Environment, PackageLoader
import numpy as np
from pkg_resources import resource_listdir, resource_string
from pkg_resources import resource_listdir, resource_filename, resource_string

from ramutils import __version__
from ramutils.reports.summary import FRSessionSummary, MathSummary, FRStimSessionSummary
from ramutils.events import extract_experiment_from_events, extract_subject
from ramutils.events import extract_experiment_from_events
from ramutils.utils import extract_experiment_series


Expand Down Expand Up @@ -68,9 +67,12 @@ class ReportGenerator(object):
"""

def __init__(self, subject, experiment, session_summaries, math_summaries,
target_selection_table, classifier_summaries, hmm_results=None, dest='.'):
target_selection_table, classifier_summaries, hmm_results=None,
dest='.', clinical=False):
self.subject = subject
self.experiment = experiment
self.clinical = clinical

self.session_summaries = session_summaries
self.math_summaries = math_summaries
self.target_selection_table = target_selection_table
Expand Down Expand Up @@ -301,7 +303,7 @@ def generate(self):
raise NotImplementedError("Unsupported report type")

def _render(self, experiment, **kwargs):
"""Convenience method to wrap common keyword arguments passed to the
""" Convenience method to wrap common keyword arguments passed to the
template renderer.

Parameters
Expand All @@ -311,7 +313,20 @@ def _render(self, experiment, **kwargs):
Additional keyword arguments that are passed to the render method.

"""
template = self._env.get_template(experiment.lower() + '.html')
if self.clinical:
from base64 import b64encode

template = self._env.get_template('clinical_target_selection.html')

for hemi in ["left", "right"]:
with open(resource_filename("ramutils.reports.static",
"r1384j_{}.png".format(hemi)), "rb") as infile:
encoded = b64encode(infile.read())
img_string = "data:image/png;base64,{}".format(encoded.decode())
kwargs["{}_hemisphere_image".format(hemi)] = img_string
else:
template = self._env.get_template(experiment.lower() + '.html')

return template.render(
version=self.version,
subject=self.subject,
Expand Down Expand Up @@ -364,20 +379,26 @@ def generate_open_loop_fr_report(self):
)

def generate_closed_loop_fr_report(self, experiment):
""" Generate an FR5 report
""" Generate a closed loop stimulation report

Returns
-------
Rendered FR5 report as a string.
Rendered stimulation report as a string

"""
stim_params = FRStimSessionSummary.stim_parameters(self.session_summaries)
multistim = False
if len(stim_params) > 1:
multistim = True

return self._render(
experiment,
stim=True,
multistim=multistim,
date=self.session_summaries[0].session_datetime,
combined_summary=self._make_combined_summary(),
classifiers=self._make_classifier_data(),
stim_params=FRStimSessionSummary.stim_parameters(
self.session_summaries),
stim_params=stim_params,
recall_tests=FRStimSessionSummary.recall_test_results(
self.session_summaries, experiment),
feature_data=self._make_feature_plots(),
Expand Down
45 changes: 30 additions & 15 deletions ramutils/reports/static/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ var ramutils = (function (mod, Plotly) {
* @param {Array} serialPos - Serial positions (x-axis)
* @param {Object} overallProbs - Overall probabilities per serial position
* @param {Object} firstProbs - Probability of first recall per serial position
* @param {Object} plot_first -- If true, plot probability of first recall
*/
plotSerialpos: function (serialPos, overallProbs, firstProbs) {
plotSerialpos: function (serialPos, overallProbs, firstProbs, plot_first = true) {
const mode = "lines+markers";
let data = [];
max_prob = 0

for (let name in overallProbs) {
current_max = Math.max(...overallProbs[name])
if (current_max > max_prob) {
max_prob = current_max
};
data.push({
x: serialPos,
y: overallProbs[name],
Expand All @@ -23,14 +29,20 @@ var ramutils = (function (mod, Plotly) {
});
}

for (let name in firstProbs) {
data.push({
x: serialPos,
y: firstProbs[name],
mode: mode,
name: name
});
}
if (plot_first) {
for (let name in firstProbs) {
current_max = Math.max(...firstProbs[name])
if (current_max > max_prob) {
max_prob = current_max
};
data.push({
x: serialPos,
y: firstProbs[name],
mode: mode,
name: name
});
}
};

const layout = {
title: "Probability of Recall as a Function of Serial Position",
Expand All @@ -40,7 +52,7 @@ var ramutils = (function (mod, Plotly) {
},
yaxis: {
title: 'Probability',
range: [0, 1]
range: [0, max_prob + 0.01]
}
};

Expand Down Expand Up @@ -103,9 +115,10 @@ var ramutils = (function (mod, Plotly) {
* @param {Object} nonStimRecalls
* @param {Object} stimRecalls
* @param {Object} stimEvents
* @param {bool} plot_stim_events
*/
plotRecallSummary: function (nonStimRecalls, stimRecalls, stimEvents) {
const data = [
plotRecallSummary: function (nonStimRecalls, stimRecalls, stimEvents, plot_stim_events = true) {
let data = [
{
x: nonStimRecalls.listno,
y: nonStimRecalls.recalled,
Expand All @@ -122,13 +135,15 @@ var ramutils = (function (mod, Plotly) {
marker: {size: 12},
name: 'Stim recalls'
},
{
]
if (plot_stim_events == true) {
data.push({
x: stimEvents.listno,
y: stimEvents.count,
type: 'bar',
name: 'Stim events'
}
];
})
};

const layout = {
title: 'Number of Items Stimulated and Number of Items Recalled',
Expand Down
Binary file added ramutils/reports/static/r1384j_left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ramutils/reports/static/r1384j_right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 27 additions & 12 deletions ramutils/reports/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,9 +1029,16 @@ def recall_test_results(summaries, experiment):
df = df[df.list > -1]

results = []
n_correct_recalls = df.recalled.sum()
n_items = len(df)
for name, group in df.groupby(['stimAnodeTag', 'stimCathodeTag',
'amplitude', 'stim_duration',
'pulse_freq']):
if name[0].find(",") != -1:
target_name = "Multi-Site"
else:
target_name = "-".join([name[0], name[1]])
single_target_results = {"target": target_name}
parameters = "/".join([str(n) for n in name])

# Stim lists vs. non-stim lists
Expand All @@ -1047,13 +1054,16 @@ def recall_test_results(summaries, experiment):
n_correct_stim_list_recalls, n_correct_nonstim_list_recalls],
[n_stim_list_words, n_nonstim_list_words])

results.append({"parameters": parameters,
"comparison": "Stim Lists vs. Non-stim Lists",
"stim": (n_correct_stim_list_recalls,
n_stim_list_words),
"non-stim": (n_correct_nonstim_list_recalls, n_nonstim_list_words),
"t-stat": tstat_list,
"p-value": pval_list})
single_target_results['list'] = {
"parameters": parameters,
"comparison": "Stim Lists vs. Non-stim Lists",
"stim": (n_correct_stim_list_recalls,
n_stim_list_words),
"non-stim": (n_correct_nonstim_list_recalls, n_nonstim_list_words),
"delta_recall": 100 * ((n_correct_stim_list_recalls/n_stim_list_words) -
(n_correct_nonstim_list_recalls/n_nonstim_list_words)) / (n_correct_recalls / n_items),
"t-stat": tstat_list,
"p-value": pval_list}

# stim items vs. non-stim low biomarker items
n_correct_stim_item_recalls = group[group.is_stim_item == True].recalled.sum(
Expand All @@ -1071,13 +1081,15 @@ def recall_test_results(summaries, experiment):
[n_correct_stim_item_recalls, n_correct_nonstim_item_recalls],
[n_stim_items, n_nonstim_items])

results.append({
single_target_results['item'] = {
"parameters": parameters,
"comparison": "Stim Items vs. Low Biomarker Non-stim Items",
"stim": (n_correct_stim_item_recalls, n_stim_items),
"non-stim": (n_correct_nonstim_item_recalls, n_nonstim_items),
"delta_recall": 100 * ((n_correct_stim_item_recalls/n_stim_items) -
(n_correct_nonstim_item_recalls/n_nonstim_items)) / (n_correct_recalls / n_items),
"t-stat": tstat_list,
"p-value": pval_list})
"p-value": pval_list}

# post stim items vs. non-stim low biomarker items
n_correct_post_stim_item_recalls = group[group.is_post_stim_item == True].recalled.sum(
Expand All @@ -1089,14 +1101,17 @@ def recall_test_results(summaries, experiment):
[n_correct_post_stim_item_recalls, n_correct_nonstim_item_recalls],
[n_post_stim_items, n_nonstim_items])

results.append({
single_target_results['post_stim_item'] = {
"parameters": parameters,
"comparison": "Post-stim Items vs. Low Biomarker Non-stim Items",
"stim": (n_correct_post_stim_item_recalls, n_post_stim_items),
"non-stim": (n_correct_nonstim_item_recalls, n_nonstim_items),
"delta_recall": 100 * ((n_correct_post_stim_item_recalls/n_post_stim_items) -
(n_correct_nonstim_item_recalls/n_nonstim_items)) / (n_correct_recalls / n_items),
"t-stat": tstat_list,
"p-value": pval_list})

"p-value": pval_list
}
results.append(single_target_results)
return results

@staticmethod
Expand Down
33 changes: 33 additions & 0 deletions ramutils/reports/templates/clinical_base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% set sections = [] %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.css">
{% for stylesheet in css.values() %}
<style>{{ stylesheet }}</style>
{% endfor %}
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{% for script in js.values() %}
<script>{{ script }}</script>
{% endfor %}
</head>
<body>
<div class="container">
<div class="jumbotron" style="text-align: center;">
<h1 class="display-4">Stimulation Report</h1>
<p class="lead">Courtesy of Nia Theraputics: {{ datetime.now().strftime('%Y-%m-%d %H:%M') }}</p>
</div>
<div>
{% block report %}{% endblock %}
</div>
</div>
<div id="footer-spacer"></div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.js"></script>
</body>
</html>
Loading