Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/midrc_react/core/jsdcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ def build_date_list(df1, df2):
str_col = category[:-6]
num_col = data_source_1.numeric_cols[str_col]['raw column']

# remove the 'Not Reported' data before calculation
combined_df = combined_df[pd.to_numeric(combined_df[num_col], errors='coerce').notnull()]

input_data = [float(calc_ks2_samp_by_feature(combined_df[combined_df['date'] <= date],
num_col)['Dataset 0 vs Dataset 1']) for date in date_list]

Expand Down
21 changes: 18 additions & 3 deletions src/midrc_react/gui/pyside6/grabbablewidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def __init__(self, parent: QWidget = None, save_file_prefix: str = DEFAULT_SAVE_
"""
self.save_dialog_open = False # Indicates if the save dialog is currently open
super().__init__(parent)
self.copyable_data: str | None = None # new attribute for copyable text data
self.save_file_prefix = save_file_prefix
parent.setContextMenuPolicy(Qt.CustomContextMenu)
parent.customContextMenuRequested.connect(self.show_context_menu)
Expand Down Expand Up @@ -82,8 +83,13 @@ def show_context_menu(self, pos):
None
"""
context_menu = QMenu(self.parent)
copy_action = QAction("Copy", self.parent)
save_action = QAction("Save", self.parent)
# New action: Copy Data, enabled only if copyable_data is set
copy_data_action = QAction("Copy Data", self.parent)
copy_data_action.setEnabled(bool(self.copyable_data))
copy_data_action.triggered.connect(self.copy_data_to_clipboard)
context_menu.addAction(copy_data_action)
copy_action = QAction("Copy Image", self.parent)
save_action = QAction("Save Image", self.parent)

copy_action.triggered.connect(self.copy_to_clipboard)
save_action.triggered.connect(self.save_to_disk)
Expand All @@ -93,7 +99,7 @@ def show_context_menu(self, pos):

# Only display this action if we don't have the high-res save dialog open for this widget
if not self.save_dialog_open:
save_high_res_action = QAction("Save High Resolution", self.parent)
save_high_res_action = QAction("Save High Resolution Image", self.parent)
save_high_res_action.triggered.connect(self.save_high_res_to_disk)
context_menu.addAction(save_high_res_action)

Expand Down Expand Up @@ -190,6 +196,14 @@ def save_high_res_to_disk(self) -> None:

self.save_dialog_open = False

def copy_data_to_clipboard(self):
"""
Copy the stored copyable_data text to the clipboard.
"""
if self.copyable_data:
clipboard = QApplication.clipboard()
clipboard.setText(self.copyable_data)


class SaveWidgetAsImageDialog(QDialog):
"""
Expand Down Expand Up @@ -366,3 +380,4 @@ def save_chart_to_disk(self):
None
"""
self.grabbable_mixin.save_to_disk()

28 changes: 27 additions & 1 deletion src/midrc_react/gui/pyside6/jsdview.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def add_spider_chart_view(self) -> GrabbableChartView:
self.spider_chart, save_file_prefix="MIDRC-REACT_spider_chart",
)
self.spider_chart_vbox.addWidget(spider_chart_view)
self.spider_chart_view = spider_chart_view # store reference for copyable data
return spider_chart_view

def update_pie_chart_dock(self, sheet_dict: Dict[Any, Any]) -> None:
Expand Down Expand Up @@ -384,6 +385,26 @@ def _create_pie_chart_series(series: QPieSeries, category: str) -> GrabbableChar
chart.legend().setAlignment(Qt.AlignRight)
return chart_view

def _set_spider_chart_copyable_data(self, spider_plot_values_dict: Dict[Any, Dict[str, float]]) -> None:
"""
Set the copyable data for the spider chart.

Args:
spider_plot_values_dict (dict): Dictionary containing series values keyed by index pairs.

Returns:
None
"""
if hasattr(self, 'spider_chart_view') and spider_plot_values_dict:
headers = sorted(next(iter(spider_plot_values_dict.values())).keys())
formatted_text = "File 1\tFile 2\t" + "\t".join(headers) + "\n"
for series_key, series in spider_plot_values_dict.items():
file1 = self._dataselectiongroupbox.file_comboboxes[series_key[0]].currentText()
file2 = self._dataselectiongroupbox.file_comboboxes[series_key[1]].currentText()
values = "\t".join(str(series[label]) for label in headers)
formatted_text += f"{file1}\t{file2}\t{values}\n"
self.spider_chart_view.grabbable_mixin.copyable_data = formatted_text

def update_spider_chart(self, spider_plot_values_dict: Dict[Any, Dict[str, float]]) -> bool:
"""
Update the spider chart with the provided plot values.
Expand All @@ -397,6 +418,8 @@ def update_spider_chart(self, spider_plot_values_dict: Dict[Any, Dict[str, float
if not spider_plot_values_dict:
return False

self._set_spider_chart_copyable_data(spider_plot_values_dict)

self.spider_chart.removeAllSeries()
for axis in self.spider_chart.axes():
self.spider_chart.removeAxis(axis)
Expand Down Expand Up @@ -570,7 +593,7 @@ def update_jsd_timeline_plot(self, jsd_model) -> bool:
for c, column_info in enumerate(jsd_model.column_infos):
col: int = c * 2
series: QLineSeries = QLineSeries()
series.setName(f"{column_info['file1']} vs {column_info['file2']} {column_info['category']} JSD")
series.setName(f"{column_info['file1']} vs {column_info['file2']} {column_info['category']} Distance")
row_count: int = jsd_model.rowCount(jsd_model.createIndex(0, col))
for i in range(row_count):
time_point: Optional[float] = convert_date_to_milliseconds(jsd_model.input_data[col][i])
Expand Down Expand Up @@ -712,3 +735,6 @@ def clear_layout(layout: Optional[QLayout]) -> bool:
layout.removeItem(child)

return True



33 changes: 33 additions & 0 deletions src/midrc_react/plugins/missing_date_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2025 Medical Imaging and Data Resource Center (MIDRC).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# 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
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""
This module contains functions for processing TSV files downloaded from the data.MIDRC.org website.
"""

from datetime import datetime
import re

import pandas as pd

def process_dataframe(df):
"""Applies both transformations on a pandas DataFrame."""
df['date'] = pd.to_datetime(datetime.today().date())
return df

def preprocess_data(df):
"""Preprocesses a pandas DataFrame."""
return process_dataframe(df)