Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to pass raw values #10 #14

Merged
merged 22 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2c3fb3f
Replace rotamer classification with score from tortoize
AliDariusKhan Feb 1, 2023
5aaaf4d
add options to read data from external json file
aj26git Mar 22, 2023
0a6e404
add density scores
aj26git Apr 4, 2023
8322686
Merge branch 'master' into Make-it-possible-to-pass-raw-values-#10
aj26git Apr 4, 2023
10f16d2
Add options to provide percentile data or similar
aj26git Apr 12, 2023
54566eb
add gitignore
aj26git Jun 18, 2023
cdb2d3c
include floating point bar labels
aj26git Jun 18, 2023
410a611
remove console log
aj26git Jun 18, 2023
8aa0e94
discard linting+formatting changes
aj26git Jun 20, 2023
ae5790a
fix import statements
aj26git Jun 20, 2023
0da2dea
Merge remote-tracking branch 'origin/replace-rotamer-classification-w…
aj26git Jun 20, 2023
d1686bd
bug fixes
aj26git Jun 20, 2023
43c9add
allow empty list for discrete_metric display
aj26git Jun 20, 2023
3aa4800
fix bug with number of chain views
aj26git Jun 21, 2023
4f06529
Revert "Merge remote-tracking branch 'origin/replace-rotamer-classifi…
aj26git Jul 21, 2023
6715472
fix rama_z scores
aj26git Jul 21, 2023
adb4f05
Update iris_validation/graphics/panel.py
aj26git Jul 24, 2023
24be952
fix panels.py (duplicate functions)
aj26git Jul 24, 2023
bf6014f
add helper function to simplify chain data gathering
aj26git Jul 24, 2023
5ccf1d4
reset metric calculations if provided externally
aj26git Jul 24, 2023
af88815
set calc rama_z as True by default
aj26git Dec 6, 2023
3605cd6
pass rama_z and rota_z as separate dictionaries
aj26git Dec 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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