Skip to content

Commit

Permalink
Merge pull request #14 from glycojones/Make-it-possible-to-pass-raw-v…
Browse files Browse the repository at this point in the history
…alues-#10

Make it possible to pass raw values #10
  • Loading branch information
AliDariusKhan authored Dec 12, 2023
2 parents 8a7ca2b + 3605cd6 commit e3b4aed
Show file tree
Hide file tree
Showing 13 changed files with 427 additions and 114 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/
.vscode
__pycache__/
PKG-INFO
70 changes: 45 additions & 25 deletions iris_validation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,61 @@
import os
import subprocess
import json

from iris_validation.graphics import Panel
from iris_validation.metrics import metrics_model_series_from_files


def generate_report(latest_model_path,
latest_reflections_path=None,
latest_sequence_path=None,
latest_distpred_path=None,
previous_model_path=None,
previous_reflections_path=None,
previous_sequence_path=None,
previous_distpred_path=None,
run_covariance=False,
run_molprobity=False,
calculate_rama_z=True,
multiprocessing=True,
wrap_in_html=True,
output_dir=None):
def generate_report(
latest_model_path,
latest_reflections_path=None,
latest_sequence_path=None,
latest_distpred_path=None,
previous_model_path=None,
previous_reflections_path=None,
previous_sequence_path=None,
previous_distpred_path=None,
run_covariance=False,
run_molprobity=False,
calculate_rama_z=True,
multiprocessing=True,
latest_model_metrics_json=None,
previous_model_metrics_json=None,
data_with_percentiles=None, # only works with model_metrics_json files
discrete_metrics_to_display=None,
continuous_metrics_to_display=None,
residue_bars_to_display=None,
percentile_bar_label=None,
percentile_bar_range=None,
wrap_in_html=True,
output_dir=None,
):

model_paths = (previous_model_path, latest_model_path)
reflections_paths = (previous_reflections_path, latest_reflections_path)
sequence_paths = (previous_sequence_path, latest_sequence_path)
distpred_paths = (previous_distpred_path, latest_distpred_path)
model_json_paths = (previous_model_metrics_json, latest_model_metrics_json)

model_series = metrics_model_series_from_files(model_paths,
reflections_paths,
sequence_paths,
distpred_paths,
run_covariance,
run_molprobity,
calculate_rama_z,
multiprocessing)
model_series = metrics_model_series_from_files(
model_paths,
reflections_paths,
sequence_paths,
distpred_paths,
run_covariance,
run_molprobity,
calculate_rama_z,
model_json_paths,
data_with_percentiles,
multiprocessing,
)
model_series_data = model_series.get_raw_data()
panel = Panel(model_series_data)
panel = Panel(
model_series_data,
continuous_metrics_to_display=continuous_metrics_to_display,
discrete_metrics_to_display=discrete_metrics_to_display,
residue_bars_to_display=residue_bars_to_display,
percentile_bar_label=percentile_bar_label,
percentile_bar_range=percentile_bar_range,
)
panel_string = panel.dwg.tostring()

if wrap_in_html:
Expand Down
18 changes: 14 additions & 4 deletions iris_validation/graphics/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@
from iris_validation._defs import COLORS, CHAIN_VIEW_RINGS, CHAIN_VIEW_GAP_ANGLE


class ChainView():
def __init__(self, data, chain_index, canvas_size=(1000, 1000), hidden=False):
class ChainView:
def __init__(
self,
data,
chain_index,
canvas_size=(1000, 1000),
hidden=False,
ChainViewRings_inp=None,
):
self.data = data
self.chain_view_rings = CHAIN_VIEW_RINGS
if ChainViewRings_inp:
self.chain_view_rings = ChainViewRings_inp
self.chain_index = chain_index
self.canvas_size = canvas_size
self.hidden = hidden

self.dwg = None
self.cfa_cache = { }
self.num_rings = len(CHAIN_VIEW_RINGS)
self.num_rings = len(self.chain_view_rings)
self.num_versions = self.data['num_versions']
self.num_segments = self.data['aligned_length']
self.center = (self.canvas_size[0] // 2, self.canvas_size[1] // 2)
Expand Down Expand Up @@ -58,7 +68,7 @@ def _draw(self):


# Draw data rings
for ring_id, ring_metric in enumerate(CHAIN_VIEW_RINGS):
for ring_id, ring_metric in enumerate(self.chain_view_rings):
self._add_ring(ring_id, ring_metric)

# Draw missing-data shade
Expand Down
1 change: 1 addition & 0 deletions iris_validation/graphics/js/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ const boxColors = {box_colors};
const boxLabels = {box_labels};
const gapDegrees = {gap_degrees};
const chainSelectorColors = {chain_selector_colors};
const bar_y_lim = {bar_y_lim};
14 changes: 8 additions & 6 deletions iris_validation/graphics/js/interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ function getResidueViewData() {
bccPoints.push([x, y]);
};
barOffsetY = bccPoints[2][1];
barMultiplierY = -(bccPoints[2][1]-bccPoints[0][1]) / 100;

barMultiplierY = -(bccPoints[2][1]-bccPoints[0][1]) / (bar_y_lim[1]-bar_y_lim[0]);
// Boxplot ranges
for (var versionID = 0; versionID < modelData[selectedChain]['num_versions']; ++versionID) {
barLineYs.push([ ]); // Model-version holder
Expand All @@ -120,14 +119,16 @@ function getResidueViewData() {
let metricMax = Math.max.apply(null, allPercentileValues);
let metricMean = mean(allPercentileValues);
let metricStd = standardDeviation(allPercentileValues);
let metricLow = Math.max(0, metricMean-metricStd);
let metricHigh = Math.min(100, metricMean+metricStd);
let metricLow = Math.max(bar_y_lim[0], metricMean-metricStd);
let metricHigh = Math.min(bar_y_lim[1], metricMean+metricStd);
let distributionValues = [ metricMin, metricMax, metricLow, metricMean, metricHigh ];
let versionLineYs = [ ];
for (var valueID = 0; valueID < 5; ++valueID) {
let lineY = parseFloat((barOffsetY + barMultiplierY * distributionValues[valueID]).toFixed(1));
let barValueLim = Math.max(0.0, distributionValues[valueID]-bar_y_lim[0]);
let lineY = parseFloat((barOffsetY + barMultiplierY * barValueLim).toFixed(1));
versionLineYs.push(lineY);
};
console.log(versionLineYs);
barLineYs[versionID].push(versionLineYs);
};
};
Expand Down Expand Up @@ -277,7 +278,8 @@ function updateSelectedResidue() {
boxplots[barID].setAttribute('opacity', 1);
let barValue = modelData[selectedChain]['percentile_values'][metricID][selectedVersion][selectedResidue];
// Set main line coordinates
barY = parseFloat((barOffsetY + barMultiplierY * barValue).toFixed(1));
let barValueLim = Math.max(0.0, barValue-bar_y_lim[0]);
barY = parseFloat((barOffsetY + barMultiplierY * barValueLim).toFixed(1));
barMainlines[barID].setAttribute('y1', barY);
barMainlines[barID].setAttribute('y2', barY);
// Set bar label text and position
Expand Down
118 changes: 101 additions & 17 deletions iris_validation/graphics/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,52 @@

from iris_validation.graphics.chain import ChainView
from iris_validation.graphics.residue import ResidueView
from iris_validation._defs import COLORS, CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS, CHAIN_VIEW_GAP_ANGLE
from iris_validation._defs import (
COLORS,
CHAIN_VIEW_RINGS,
RESIDUE_VIEW_BOXES,
RESIDUE_VIEW_BARS,
CHAIN_VIEW_GAP_ANGLE,
DISCRETE_METRICS,
CONTINUOUS_METRICS,
)


JS_PATH = os.path.join(os.path.dirname(__file__), 'js')
JS_CONSTANTS_PATH = os.path.join(JS_PATH, 'constants.js')
JS_INTERACTION_PATH = os.path.join(JS_PATH, 'interaction.js')


class Panel():
def __init__(self, data, canvas_size=(1500, 1000)):
class Panel:
def __init__(
self,
data,
canvas_size=(1500, 1000),
continuous_metrics_to_display=None,
discrete_metrics_to_display=None,
residue_bars_to_display=None,
percentile_bar_label=None,
percentile_bar_range=None,
):
self.data = data
self.canvas_size = canvas_size

self.chain_view_rings = CHAIN_VIEW_RINGS
if continuous_metrics_to_display:
self.chain_view_rings = self.get_chain_view_rings(
continuous_metrics_to_display,
discrete_metrics_to_display=discrete_metrics_to_display,
)
self.residue_view_bars = RESIDUE_VIEW_BARS
if residue_bars_to_display is not None:
self.residue_view_bars = self.get_residue_view_bars(residue_bars_to_display)
if percentile_bar_label:
self.percentile_bar_label = percentile_bar_label
else:
self.percentile_bar_label = None
if percentile_bar_range:
self.percentile_bar_range = percentile_bar_range
else:
self.percentile_bar_range = [0, 100]
self.dwg = None
self.javascript = None
self.chain_views = None
Expand All @@ -36,8 +69,12 @@ def __init__(self, data, canvas_size=(1500, 1000)):

# TODO: Make this nicer
def _verify_chosen_metrics(self):
global CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS
for metric_list in (CHAIN_VIEW_RINGS, RESIDUE_VIEW_BOXES, RESIDUE_VIEW_BARS):
global RESIDUE_VIEW_BOXES
for metric_list in (
self.chain_view_rings,
RESIDUE_VIEW_BOXES,
self.residue_view_bars,
):
if not isinstance(metric_list, list):
raise ValueError('Chosen metrics in the _defs.py file must be lists')
for metric_index in reversed(range(len(metric_list))):
Expand All @@ -55,7 +92,7 @@ def _verify_chosen_metrics(self):
def _generate_javascript(self):
json_data = json.dumps(self.data)
num_chains = len(self.chain_ids)
bar_metric_ids = [ metric['id'] for metric in RESIDUE_VIEW_BARS ]
bar_metric_ids = [metric["id"] for metric in self.residue_view_bars]
box_metric_ids = [ metric['id'] for metric in RESIDUE_VIEW_BOXES ]
box_colors = json.dumps([ metric['seq_colors'] for metric in RESIDUE_VIEW_BOXES ])
box_labels = json.dumps([ metric['seq_labels'] for metric in RESIDUE_VIEW_BOXES ])
Expand All @@ -67,23 +104,35 @@ def _generate_javascript(self):
with open(JS_INTERACTION_PATH, 'r', encoding='utf8') as infile:
js_interation = infile.read()

js_constants = js_constants.format(model_data=json_data,
num_chains=num_chains,
bar_metric_ids=bar_metric_ids,
box_metric_ids=box_metric_ids,
box_colors=box_colors,
box_labels=box_labels,
gap_degrees=gap_degrees,
chain_selector_colors=self.swtich_colors)
js_constants = js_constants.format(
model_data=json_data,
num_chains=num_chains,
bar_metric_ids=bar_metric_ids,
box_metric_ids=box_metric_ids,
box_colors=box_colors,
box_labels=box_labels,
gap_degrees=gap_degrees,
chain_selector_colors=self.swtich_colors,
bar_y_lim=self.percentile_bar_range,
)

self.javascript = js_constants + js_interation

def _generate_subviews(self):
self.chain_views = [ ]
for chain_index, chain_data in enumerate(self.data):
chain_view = ChainView(chain_data, chain_index, hidden=chain_index>0).dwg
chain_view = ChainView(
chain_data,
chain_index,
hidden=chain_index > 0,
ChainViewRings_inp=self.chain_view_rings,
).dwg
self.chain_views.append(chain_view)
self.residue_view = ResidueView().dwg
self.residue_view = ResidueView(
ResidueViewBars_inp=self.residue_view_bars,
percentile_bar_label=self.percentile_bar_label,
percentile_bar_range=self.percentile_bar_range,
).dwg

def _draw(self):
middle_gap = 30
Expand Down Expand Up @@ -284,3 +333,38 @@ def _draw(self):
self.residue_view.attribs['x'] = str(view_adj_x)
self.residue_view.attribs['viewBox'] = f'{width_buffer} {height_buffer} {viewbox_width} {viewbox_height}'
self.dwg.add(self.residue_view)

def _add_metrics(self, metrics_to_display, metrics_source):
view = []
for metric_name in metrics_to_display:
for metric_info in metrics_source:
if metric_info["short_name"] == metric_name:
view.append(metric_info)
break
return view

def get_chain_view_rings(
self,
continuous_metrics_to_display,
discrete_metrics_to_display=None
):
chain_view = []

# add discrete types first
if discrete_metrics_to_display:
chain_view.extend(
self._add_metrics(discrete_metrics_to_display, DISCRETE_METRICS)
)
else:
chain_view.extend(
self._add_metrics([m["short_name"] for m in CHAIN_VIEW_RINGS if m["type"] == "discrete"], DISCRETE_METRICS)
)

chain_view.extend(
self._add_metrics(continuous_metrics_to_display, CONTINUOUS_METRICS)
)

return chain_view

def get_residue_view_bars(self, residue_bars_to_display):
return self._add_metrics(residue_bars_to_display, CONTINUOUS_METRICS)
Loading

0 comments on commit e3b4aed

Please sign in to comment.