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
13 changes: 9 additions & 4 deletions src/midrc_react/core/excel_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(self, data_source, custom_age_ranges=None):
self._numeric_cols = data_source.numeric_cols # Extract numeric columns from config
self._columns = data_source.columns
self.raw_data = None
self._date_column = data_source.date_column

# Load preprocessing plugin if specified
self.preprocessor = None
Expand Down Expand Up @@ -221,26 +222,30 @@ def create_sheets_from_df(self, df: pd.DataFrame):
Returns:
None
"""
if self._date_column not in df.columns:
self._date_column = df.columns[0]
df.rename(columns={self._date_column: 'date'}, inplace=True) # Ensure the date column is named 'date'
Comment on lines +225 to +227

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If self._date_column is None (which can happen since it's Optional[str] in DataSourceConfig), this will raise a TypeError when checking None not in df.columns. You should add a check to handle the None case, such as defaulting to 'date' or df.columns[0] before the comparison.

Suggested change
if self._date_column not in df.columns:
self._date_column = df.columns[0]
df.rename(columns={self._date_column: 'date'}, inplace=True) # Ensure the date column is named 'date'
# Determine which column to use as the date column, handling None and missing cases safely.
if self._date_column is not None and self._date_column in df.columns:
date_column = self._date_column
elif 'date' in df.columns:
# Prefer an existing 'date' column if present.
date_column = 'date'
else:
# Fallback: use the first column in the DataFrame.
date_column = df.columns[0]
# Update the stored date column name to the resolved value.
self._date_column = date_column
df.rename(columns={date_column: 'date'}, inplace=True) # Ensure the date column is named 'date'

Copilot uses AI. Check for mistakes.
# make sure the date column is datetime
df['date'] = pd.to_datetime(df['date'])
for col in self._columns:
if col in df.columns:
df_cumsum = self.calculate_cumulative_sums(df, col)
if col in self._numeric_cols:
labels = self._numeric_cols[col].labels if hasattr(self._numeric_cols[col], 'labels') else None
if labels:
# The first column (e.g., date) remains at index 0.
date_column = df_cumsum.columns[0]
# Keep only labels that are present in the DataFrame.
labels_in_df = [col for col in labels if col in df_cumsum.columns]
# The remaining columns are those not in labels_in_df and not the date column.
remaining_cols = [col for col in df_cumsum.columns if
col not in labels_in_df and col != date_column]
col not in labels_in_df and col != 'date']
# Build the new column order.
new_order = [date_column] + labels_in_df + remaining_cols
new_order = ['date'] + labels_in_df + remaining_cols
df_cumsum = df_cumsum[new_order]
self.sheets[col] = DataSheet(col, self.data_source, self.custom_age_ranges, is_excel=False,
df=df_cumsum)

def calculate_cumulative_sums(self, df: pd.DataFrame, col: str):
def calculate_cumulative_sums(self, df: pd.DataFrame, col: str) -> pd.DataFrame:
"""
Calculates cumulative sums for a given column.

Expand Down
1 change: 1 addition & 0 deletions src/midrc_react/core/jsdconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class DataSourceConfig(BaseModel):
numeric_cols: Optional[Dict[str, NumericColumnConfig]] = None
plugin: Optional[str] = None
date: Optional[str] = None
date_column: Optional[str] = Field('date', alias='date column')
remove_column_name_text: Optional[List[str]] = Field(None, alias='remove column name text')

content: Optional[Any] = None # Placeholder for loaded content
Expand Down
4 changes: 3 additions & 1 deletion src/midrc_react/gui/common/jsdview_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QMainWindow

from midrc_react.core.jsdconfig import DataSourceConfig


class FileInfo(BaseModel):
description: Optional[str] = None
Expand Down Expand Up @@ -146,7 +148,7 @@ class JsdViewBase(QObject):
_data_selection_group_box = GroupBoxData()
_controller = None
update_view_on_controller_initialization = True
add_data_source = Signal(dict)
add_data_source = Signal(DataSourceConfig)

def __init__(self):
"""
Expand Down
49 changes: 23 additions & 26 deletions src/midrc_react/gui/pyside6/file_open_dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import yaml

from midrc_react.gui.pyside6.columnselectordialog import ColumnSelectorDialog, NumericColumnSelectorDialog

from midrc_react.core.jsdconfig import DataSourceConfig, NumericColumnConfig

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'NumericColumnConfig' is not used.

Suggested change
from midrc_react.core.jsdconfig import DataSourceConfig, NumericColumnConfig
from midrc_react.core.jsdconfig import DataSourceConfig

Copilot uses AI. Check for mistakes.

class BaseFileOptionsDialog(QDialog):
"""
Expand Down Expand Up @@ -162,8 +162,10 @@ def __init__(self, parent: Optional[QWidget], file_name: str,
form_layout = QFormLayout()
self.name_line_edit = QLineEdit()
self.description_line_edit = QLineEdit()
self.date_column_line_edit = QLineEdit()
form_layout.addRow("Name (Plot Titles):", self.name_line_edit)
form_layout.addRow("Description (Drop-Down Menu):", self.description_line_edit)
form_layout.addRow("Date Column:", self.date_column_line_edit)
top_layout.addLayout(form_layout)

# Step 2: Plugin dropdown and Process Plugin button
Expand Down Expand Up @@ -266,6 +268,7 @@ def _load_values_from_group(self, settings, group: str) -> Dict[str, Any]:
"plugin": settings.value("plugin", ""),
"name": settings.value("name", ""),
"description": settings.value("description", ""),
"date_column": settings.value("date_column", "date"),
"columns": settings.value("columns", ""),
"numeric_cols": settings.value("numeric_cols", ""),
}
Expand Down Expand Up @@ -293,6 +296,7 @@ def load_defaults(self) -> None:
saved_description = (
file_values.get("description") or last_values.get("description", self.default_base)
)
saved_date_column = file_values.get("date_column") or last_values.get("date_column", "date")
saved_columns = file_values.get("columns") or last_values.get("columns", "")
saved_numeric = file_values.get("numeric_cols") or last_values.get("numeric_cols", "")

Expand All @@ -302,6 +306,7 @@ def load_defaults(self) -> None:
self.plugin_combo.setCurrentIndex(idx)
self.name_line_edit.setText(saved_name)
self.description_line_edit.setText(saved_description)
self.date_column_line_edit.setText(saved_date_column)
if saved_columns:
self.columns_line_edit.setText(saved_columns)
if saved_numeric:
Expand All @@ -319,6 +324,7 @@ def _set_settings_values_to_group(self, settings, group):
settings.setValue("plugin", self.plugin_combo.currentText().strip())
settings.setValue("name", self.name_line_edit.text())
settings.setValue("description", self.description_line_edit.text())
settings.setValue("date_column", self.date_column_line_edit.text())
settings.setValue("columns", self.columns_line_edit.text())
settings.setValue("numeric_cols", self.numeric_cols_text_edit.toPlainText())
settings.endGroup()
Expand Down Expand Up @@ -473,6 +479,7 @@ def get_data(self) -> Dict[str, Any]:
return {
"name": self.name_line_edit.text(),
"description": self.description_line_edit.text(),
"date_column": self.date_column_line_edit.text(),
"columns": selected_cols,
"numeric_cols": numeric_cols,
"plugin": self.plugin_combo.currentText().strip(),
Expand Down Expand Up @@ -512,17 +519,13 @@ def open_excel_file_dialog(self: Any) -> None:
if dialog.exec() != QDialog.Accepted:
return # User cancelled the dialog

data_source_dict: Dict[str, Union[str, Any]] = {
'name': dialog.name_line_edit.text(),
'description': dialog.description_line_edit.text(),
'data type': 'file',
'filename': file_name,
'remove column name text': dialog.remove_column_text_line_edit.text(),
}
self.add_data_source.emit(data_source_dict)
data_source_dict = dialog.get_data()

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FileOptionsDialog class does not have a get_data() method, but it's being called here. This will result in an AttributeError at runtime when opening Excel files. You need to either add a get_data() method to FileOptionsDialog that returns the necessary fields (name, description, remove_column_name_text), or directly access the dialog's line edit fields as was done in the previous implementation.

Suggested change
data_source_dict = dialog.get_data()
if hasattr(dialog, "get_data") and callable(getattr(dialog, "get_data", None)):
data_source_dict = dialog.get_data()
else:
# Fallback: construct the data source dict directly from dialog attributes
data_source_dict = {
"name": getattr(dialog, "name", ""),
"description": getattr(dialog, "description", ""),
"remove_column_name_text": getattr(dialog, "remove_column_name_text", ""),
}

Copilot uses AI. Check for mistakes.
data_source_config = DataSourceConfig(**data_source_dict, filename=file_name, data_type='file')
self.add_data_source.emit(data_source_config)

self.dataselectiongroupbox.add_file_to_comboboxes(
data_source_dict['description'],
data_source_dict['name'],
data_source_config.description,
data_source_config.name,
)


Expand Down Expand Up @@ -565,10 +568,11 @@ def open_yaml_input_dialog(self: Any) -> None:

# Save the current YAML content as default
settings.setValue("yaml_input/default_yaml", yaml_content)
self.add_data_source.emit(data_source_dict)
data_source_config = DataSourceConfig(**data_source_dict)
self.add_data_source.emit(data_source_config)
self.dataselectiongroupbox.add_file_to_comboboxes(
data_source_dict.get("description", ""),
data_source_dict.get("name", ""),
data_source_config.description,
data_source_config.name,
)


Expand All @@ -591,17 +595,10 @@ def open_csv_tsv_file_dialog(self: Any) -> None:
return # User cancelled the dialog

data = dialog.get_data()
data_source_dict: Dict[str, Any] = {
'name': data['name'],
'description': data['description'],
'data type': 'file',
'filename': file_name,
'columns': data['columns'],
'numeric_cols': data['numeric_cols'], # Caller can later parse this as YAML if desired
'plugin': data['plugin'],
}
self.add_data_source.emit(data_source_dict)
data_source_config = DataSourceConfig(**data, filename=file_name, data_type='file')

self.add_data_source.emit(data_source_config)
self.dataselectiongroupbox.add_file_to_comboboxes(
data_source_dict.get('description', ''),
data_source_dict.get('name', ''),
data_source_config.description,
data_source_config.name,
)
4 changes: 2 additions & 2 deletions src/midrc_react/gui/pyside6/jsdview.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)

from midrc_react.core.datetimetools import convert_date_to_milliseconds, numpy_datetime64_to_qdate
from midrc_react.core.jsdconfig import DataSourceConfigList
from midrc_react.core.jsdconfig import DataSourceConfigList, DataSourceConfig
from midrc_react.gui.common.jsdview_base import JsdViewBase
from midrc_react.gui.pyside6.copyabletableview import CopyableTableView
from midrc_react.gui.pyside6.dataselectiongroupbox import JsdDataSelectionGroupBox
Expand All @@ -56,7 +56,7 @@ class JsdWindow(QMainWindow, JsdViewBase):
WINDOW_TITLE (str): The window title.
DOCK_TITLES (dict): Titles for various dock widgets.
"""
add_data_source = Signal(dict)
add_data_source = Signal(DataSourceConfig)

WINDOW_TITLE: str = 'MIDRC-REACT Representativeness Exploration and Comparison Tool'
DOCK_TITLES: Dict[str, str] = {
Expand Down
Loading