Ejemplo n.º 1
0
class EndpointFrame:
    """
    Object to be passed into notebook panel for the Endpoint tab
    """
    def __init__(self, parent, group_data, time_series, regression,
                 control_chart):
        """
        :param parent:  notebook panel in main view
        :type parent: Panel
        :param group_data: dvh, table_data, and stats_data
        :type group_data: dict
        :param time_series: Time Series object in notebook
        :type time_series: TimeSeriesFrame
        :param regression: Regression frame object in notebook
        :type regression: RegressionFrame
        :param control_chart: Control Chart frame object in notebook
        :type control_chart: ControlChartFrame
        """

        self.parent = parent
        self.group_data = group_data
        self.time_series = time_series
        self.regression = regression
        self.control_chart = control_chart
        self.group_data = group_data
        self.initial_columns = ['MRN', 'Tx Site', 'ROI Name', 'Volume (cc)']
        self.widths = [150, 150, 250, 100]

        self.button = {
            'add': wx.Button(self.parent, wx.ID_ANY, "Add Endpoint"),
            'del': wx.Button(self.parent, wx.ID_ANY, "Delete Endpoint"),
            'exp': wx.Button(self.parent, wx.ID_ANY, 'Export')
        }

        self.table = {
            key: wx.ListCtrl(self.parent,
                             wx.ID_ANY,
                             style=wx.BORDER_SUNKEN | wx.LC_HRULES
                             | wx.LC_REPORT | wx.LC_VRULES)
            for key in [1, 2]
        }
        for table in self.table.values():
            table.SetMinSize(get_window_size(0.623, 0.28))
        self.data_table = {key: DataTable(self.table[key]) for key in [1, 2]}

        self.endpoint_defs = DataTable(None,
                                       columns=[
                                           'label', 'output_type',
                                           'input_type', 'input_value',
                                           'units_in', 'units_out'
                                       ])

        for key in [1, 2]:
            if self.group_data[key]['dvh']:
                self.group_data[key]['dvh'].endpoints[
                    'data'] = self.data_table[key].data
                self.group_data[key]['dvh'].endpoints[
                    'defs'] = self.endpoint_defs.data

        self.__do_bind()
        self.__set_properties()
        self.__do_layout()

        self.disable_buttons()

    def __do_bind(self):
        self.parent.Bind(wx.EVT_BUTTON,
                         self.add_ep_button_click,
                         id=self.button['add'].GetId())
        self.parent.Bind(wx.EVT_BUTTON,
                         self.del_ep_button_click,
                         id=self.button['del'].GetId())
        self.parent.Bind(wx.EVT_BUTTON,
                         self.on_export_csv,
                         id=self.button['exp'].GetId())

    def __set_properties(self):
        for table in self.table.values():
            for i, column in enumerate(self.initial_columns):
                table.AppendColumn(column,
                                   format=wx.LIST_FORMAT_LEFT,
                                   width=self.widths[i])

    def __do_layout(self):
        sizer_wrapper = wx.BoxSizer(wx.VERTICAL)
        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        for key in list(self.button):
            hbox.Add(self.button[key], 0, wx.ALL, 5)
        vbox.Add(hbox, 0, wx.ALL | wx.EXPAND, 5)
        for grp in [1, 2]:
            vbox.Add(
                wx.StaticText(self.parent, wx.ID_ANY, "Query Group %s:" % grp),
                0, wx.EXPAND | wx.BOTTOM, 5)
            vbox.Add(self.table[grp], 1, wx.EXPAND, 0)
            vbox.Add((10, 10), 0, 0, 0)
        sizer_wrapper.Add(vbox, 1, wx.ALL | wx.EXPAND, 20)
        self.layout = sizer_wrapper

    def calculate_endpoints(self):

        columns = {key: [c for c in self.initial_columns] for key in [1, 2]}
        if self.data_table[1].data:
            current_labels = [
                key for key in list(self.data_table[1].data)
                if key not in columns
            ]
        else:
            current_labels = []

        eps = {
            grp: {
                'MRN': group_data['dvh'].mrn,
                'Tx Site': group_data['dvh'].get_plan_values('tx_site'),
                'ROI Name': group_data['dvh'].roi_name,
                'Volume (cc)': group_data['dvh'].volume
            }
            for grp, group_data in self.group_data.items() if group_data['dvh']
        }

        ep_defs = self.endpoint_defs.data
        for group, ep in eps.items():
            if ep_defs:
                for i, ep_name in enumerate(ep_defs['label']):

                    if ep_name not in columns[group]:
                        columns[group].append(ep_name)

                        if ep_name in current_labels:
                            ep[ep_name] = deepcopy(
                                self.data_table[group].data[ep_name])

                        else:
                            endpoint_input = ep_defs['input_type'][i]
                            endpoint_output = ep_defs['output_type'][i]

                            x = float(ep_defs['input_value'][i])
                            if endpoint_input == 'relative':
                                x /= 100.

                            dvh = self.group_data[group]['dvh']
                            if 'V' in ep_name:
                                ep[ep_name] = dvh.get_volume_of_dose(
                                    x,
                                    volume_scale=endpoint_output,
                                    dose_scale=endpoint_input)
                            else:
                                ep[ep_name] = dvh.get_dose_to_volume(
                                    x,
                                    dose_scale=endpoint_output,
                                    volume_scale=endpoint_input)

        for group, ep in eps.items():
            self.data_table[group].set_data(ep, columns[group])
            self.data_table[group].set_column_width(0, 150)
            self.data_table[group].set_column_width(1, 150)
            self.data_table[group].set_column_width(2, 200)

    def add_ep_button_click(self, evt):
        # TODO: Track down duplicate table refreshes/calculations
        dlg = AddEndpointDialog()
        res = dlg.ShowModal()
        if res == wx.ID_OK and dlg.is_endpoint_valid:
            self.endpoint_defs.append_row(dlg.endpoint_row)
            self.calculate_endpoints()
            self.enable_buttons()
            self.update_endpoints_in_dvh()
        dlg.Destroy()
        self.update_endpoints_and_radbio_in_group_data()
        self.regression.update_combo_box_choices()
        self.control_chart.update_combo_box_y_choices()
        self.time_series.update_y_axis_options()

    def del_ep_button_click(self, evt):
        dlg = DelEndpointDialog(self.data_table[1].columns)
        res = dlg.ShowModal()
        if res == wx.ID_OK:
            for value in dlg.selected_values:
                for data_table in self.data_table.values():
                    if data_table.columns and value in data_table.columns:
                        data_table.delete_column(value)
                endpoint_def_row = self.endpoint_defs.data['label'].index(
                    value)
                self.update_endpoints_in_dvh()
                self.endpoint_defs.delete_row(endpoint_def_row)
                self.update_endpoints_and_radbio_in_group_data()

            self.time_series.update_y_axis_options()
        dlg.Destroy()

        for group in self.group_data.values():
            if group['stats_data']:
                group['stats_data'].update_endpoints_and_radbio()
        self.regression.update_combo_box_choices()
        self.control_chart.update_combo_box_y_choices()

        for data_table in self.data_table.values():
            if data_table.column_count == 3:
                self.button['del'].Disable()
                self.button['exp'].Disable()

    def update_dvh(self, group_data):
        self.group_data = group_data
        self.update_endpoints_in_dvh()

    def update_endpoints_and_radbio_in_group_data(self):
        for grp, data in self.group_data.items():
            if data['stats_data']:
                data['stats_data'].update_endpoints_and_radbio()
                if grp == 2:
                    sync_variables_in_stats_data_objects(
                        self.group_data[1]['stats_data'],
                        self.group_data[2]['stats_data'])

    def update_endpoints_in_dvh(self):
        for group in [1, 2]:
            if self.group_data[group]['dvh']:
                self.group_data[group]['dvh'].endpoints[
                    'data'] = self.data_table[group].data
                self.group_data[group]['dvh'].endpoints[
                    'defs'] = self.endpoint_defs.data

    def clear_data(self):
        for data_table in self.data_table.values():
            data_table.delete_all_rows()
        self.endpoint_defs.delete_all_rows(
            force_delete_data=True)  # no attached layout, force delete

        for data_table in self.data_table.values():
            if data_table.data:
                for column in list(data_table.data):
                    if column not in self.initial_columns:
                        data_table.delete_column(column)

    def enable_buttons(self):
        for key in list(self.button):
            self.button[key].Enable()

    def disable_buttons(self):
        for key in list(self.button):
            self.button[key].Disable()

    def enable_initial_buttons(self):
        self.button['add'].Enable()

    def get_csv(self, selection=None):
        uid = {
            1: {
                'title': 'Study Instance UID',
                'data': self.group_data[1]['dvh'].uid
            }
        }
        csv = self.data_table[1].get_csv(extra_column_data=uid)
        if self.group_data[2]['dvh']:
            uid = {
                1: {
                    'title': 'Study Instance UID',
                    'data': self.group_data[2]['dvh'].uid
                }
            }
            csv = 'Group 1\n%s\n\nGroup 2\n%s' % (
                csv, self.data_table[2].get_csv(extra_column_data=uid))

        return csv

    def on_export_csv(self, evt):
        save_data_to_file(self.parent, "Export Endpoints to CSV",
                          self.get_csv())

    def get_save_data(self):
        return {
            'data_table_1': self.data_table[1].get_save_data(),
            'data_table_2': self.data_table[2].get_save_data(),
            'endpoint_defs': self.endpoint_defs.get_save_data()
        }

    def load_save_data(self, save_data):
        self.data_table[1].load_save_data(save_data['data_table_1'],
                                          ignore_layout=True)
        self.data_table[2].load_save_data(save_data['data_table_2'],
                                          ignore_layout=True)
        self.endpoint_defs.load_save_data(save_data['endpoint_defs'],
                                          ignore_layout=True)
        self.calculate_endpoints()

    @property
    def has_data(self):
        if self.endpoint_defs.data and self.endpoint_defs.data['label']:
            return True
        return False
Ejemplo n.º 2
0
class EndpointFrame:
    """
    Object to be passed into notebook panel for the Endpoint tab
    """
    def __init__(self, parent, dvh, time_series, regression, control_chart):
        """
        :param parent:  notebook panel in main view
        :type parent: Panel
        :param dvh: dvh data object
        :type dvh: DVH
        :param time_series: Time Series object in notebook
        :type time_series: TimeSeriesFrame
        :param regression: Regression frame object in notebook
        :type regression: RegressionFrame
        :param control_chart: Control Chart frame object in notebook
        :type control_chart: ControlChartFrame
        """

        self.parent = parent
        self.dvh = dvh
        self.time_series = time_series
        self.regression = regression
        self.control_chart = control_chart
        self.initial_columns = ['MRN', 'Tx Site', 'ROI Name', 'Volume (cc)']
        self.widths = [150, 150, 250, 100]

        self.button = {'add': wx.Button(self.parent, wx.ID_ANY, "Add Endpoint"),
                       'del': wx.Button(self.parent, wx.ID_ANY, "Delete Endpoint"),
                       'exp': wx.Button(self.parent, wx.ID_ANY, 'Export')}

        self.table = wx.ListCtrl(self.parent, wx.ID_ANY,
                                 style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES)
        self.table.SetMinSize(get_window_size(0.623, 0.762))
        self.data_table = DataTable(self.table)

        self.endpoint_defs = DataTable(None, columns=['label', 'output_type', 'input_type',
                                                      'input_value', 'units_in', 'units_out'])

        if dvh:
            self.dvh.endpoints['data'] = self.data_table.data
            self.dvh.endpoints['defs'] = self.endpoint_defs.data

        self.__do_bind()
        self.__set_properties()
        self.__do_layout()

        self.disable_buttons()

    def __do_bind(self):
        self.parent.Bind(wx.EVT_BUTTON, self.add_ep_button_click, id=self.button['add'].GetId())
        self.parent.Bind(wx.EVT_BUTTON, self.del_ep_button_click, id=self.button['del'].GetId())
        self.parent.Bind(wx.EVT_BUTTON, self.on_export_csv, id=self.button['exp'].GetId())

    def __set_properties(self):
        for i, column in enumerate(self.initial_columns):
            self.table.AppendColumn(column, format=wx.LIST_FORMAT_LEFT, width=self.widths[i])

    def __do_layout(self):
        sizer_wrapper = wx.BoxSizer(wx.VERTICAL)
        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        for key in list(self.button):
            hbox.Add(self.button[key], 0, wx.ALL, 5)
        vbox.Add(hbox, 0, wx.ALL | wx.EXPAND, 5)
        vbox.Add(self.table, 1, wx.EXPAND, 0)
        sizer_wrapper.Add(vbox, 1, wx.ALL | wx.EXPAND, 20)
        self.layout = sizer_wrapper

    def calculate_endpoints(self):

        columns = [c for c in self.initial_columns]
        if self.data_table.data:
            current_labels = [key for key in list(self.data_table.data) if key not in columns]
        else:
            current_labels = []

        ep = {'MRN': self.dvh.mrn,
              'Tx Site': self.dvh.get_plan_values('tx_site'),
              'ROI Name': self.dvh.roi_name,
              'Volume (cc)': self.dvh.volume}

        ep_defs = self.endpoint_defs.data
        if ep_defs:
            for i, ep_name in enumerate(ep_defs['label']):

                if ep_name not in columns:
                    columns.append(ep_name)

                    if ep_name in current_labels:
                        ep[ep_name] = deepcopy(self.data_table.data[ep_name])

                    else:
                        endpoint_input = ep_defs['input_type'][i]
                        endpoint_output = ep_defs['output_type'][i]

                        x = float(ep_defs['input_value'][i])
                        if endpoint_input == 'relative':
                            x /= 100.

                        if 'V' in ep_name:
                            ep[ep_name] = self.dvh.get_volume_of_dose(x, volume_scale=endpoint_output,
                                                                      dose_scale=endpoint_input)
                        else:
                            ep[ep_name] = self.dvh.get_dose_to_volume(x, dose_scale=endpoint_output,
                                                                      volume_scale=endpoint_input)

        self.data_table.set_data(ep, columns)
        self.data_table.set_column_width(0, 150)
        self.data_table.set_column_width(1, 150)
        self.data_table.set_column_width(2, 200)

    def add_ep_button_click(self, evt):
        dlg = AddEndpointDialog()
        res = dlg.ShowModal()
        if res == wx.ID_OK and dlg.is_endpoint_valid:
            self.endpoint_defs.append_row(dlg.endpoint_row)
            self.calculate_endpoints()
            self.enable_buttons()
            self.update_endpoints_in_dvh()
            self.time_series.update_y_axis_options()
        dlg.Destroy()
        self.regression.stats_data.update_endpoints_and_radbio()
        self.regression.update_combo_box_choices()
        self.control_chart.update_combo_box_y_choices()

    def del_ep_button_click(self, evt):
        dlg = DelEndpointDialog(self.data_table.columns)
        res = dlg.ShowModal()
        if res == wx.ID_OK:
            for value in dlg.selected_values:
                self.data_table.delete_column(value)
                endpoint_def_row = self.endpoint_defs.data['label'].index(value)
                self.update_endpoints_in_dvh()
                self.endpoint_defs.delete_row(endpoint_def_row)
            self.time_series.update_y_axis_options()
        dlg.Destroy()

        self.regression.stats_data.update_endpoints_and_radbio()
        self.regression.update_combo_box_choices()
        self.control_chart.update_combo_box_y_choices()

        if self.data_table.column_count == 3:
            self.button['del'].Disable()
            self.button['exp'].Disable()

    def update_dvh(self, dvh):
        self.dvh = dvh
        self.update_endpoints_in_dvh()

    def update_endpoints_in_dvh(self):
        if self.dvh:
            self.dvh.endpoints['data'] = self.data_table.data
            self.dvh.endpoints['defs'] = self.endpoint_defs.data

    def clear_data(self):
        self.data_table.delete_all_rows()
        self.endpoint_defs.delete_all_rows(force_delete_data=True)  # no attached layout, force delete

        if self.data_table.data:
            for column in list(self.data_table.data):
                if column not in self.initial_columns:
                    self.data_table.delete_column(column)

    def enable_buttons(self):
        for key in list(self.button):
            self.button[key].Enable()

    def disable_buttons(self):
        for key in list(self.button):
            self.button[key].Disable()

    def enable_initial_buttons(self):
        self.button['add'].Enable()

    def get_csv(self, selection=None):
        uid = {1: {'title': 'Study Instance UID',
                   'data': self.dvh.uid}}
        return self.data_table.get_csv(extra_column_data=uid)

    def on_export_csv(self, evt):
        save_data_to_file(self.parent, "Export Endpoints to CSV", self.get_csv())

    def get_save_data(self):
        return deepcopy({'data_table': self.data_table.get_save_data(),
                         'endpoint_defs': self.endpoint_defs.get_save_data()})

    def load_save_data(self, save_data):
        self.data_table.load_save_data(save_data['data_table'], ignore_layout=True)
        self.endpoint_defs.load_save_data(save_data['endpoint_defs'], ignore_layout=True)
        self.calculate_endpoints()

    @property
    def has_data(self):
        if self.endpoint_defs.data and self.endpoint_defs.data['label']:
            return True
        return False