1from AnyQt.QtWidgets import QTableView, QGridLayout, QWidget
2from AnyQt.QtCore import Qt, QSize
3
4from Orange.data import Table, Domain
5from Orange.widgets.settings import Setting, ContextSetting, PerfectDomainContextHandler
6from Orange.widgets.utils.itemmodels import TableModel
7from Orange.widgets.widget import OWWidget, Input, Output
8from Orange.widgets import gui
9from orangecontrib.datafusion.models import Relation
10from skfusion import fusion
11
12
13class OWTableToRelation(OWWidget):
14    name = "Table to Relation"
15    description = "Convert data table to relation matrix. Label matrix axis."
16    priority = 50000
17    icon = "icons/TableToRelation.svg"
18
19    class Inputs:
20        data = Input("Data", Table)
21
22    class Outputs:
23        relation = Output("Relation", Relation)
24
25    settingsHandler = PerfectDomainContextHandler()
26
27    data = None
28
29    relation_name = ContextSetting("")
30    transpose = ContextSetting(False)
31
32    row_type = ContextSetting("")
33    selected_meta = ContextSetting(0)
34    row_names = None
35
36    col_type = ContextSetting("")
37    col_names = None
38
39    auto_commit = Setting(True)
40
41    def __init__(self):
42        super().__init__()
43
44        self.model = None
45        self.view = None
46        self.row_names_combo = None
47        self.icons = gui.attributeIconDict
48        self.populate_control_area()
49        self.populate_main_area()
50
51    def populate_control_area(self):
52        rel = gui.widgetBox(self.controlArea, "Relation")
53        gui.lineEdit(rel, self, "relation_name", "Name", callbackOnType=True, callback=self.apply)
54        gui.checkBox(rel, self, "transpose", "Transpose", callback=self.apply)
55
56        col = gui.widgetBox(self.controlArea, "Column")
57        gui.lineEdit(col, self, "col_type", "Object Type", callbackOnType=True, callback=self.apply)
58
59        row = gui.widgetBox(self.controlArea, "Row")
60        gui.lineEdit(row, self, "row_type", "Object Type", callbackOnType=True, callback=self.apply)
61        self.row_names_combo = gui.comboBox(row, self, "selected_meta", label="Object Names",
62                                            callback=self.update_row_names)
63
64        gui.rubber(self.controlArea)
65        gui.auto_commit(self.controlArea, self, "auto_commit", "Send",
66                        checkbox_label='Auto-send',
67                        orientation='vertical')
68
69    def populate_main_area(self):
70        grid = QWidget()
71        grid.setLayout(QGridLayout(grid))
72        self.mainArea.layout().addWidget(grid)
73
74        col_type = gui.label(None, self, '%(col_type)s')
75
76        grid.layout().addWidget(col_type, 0, 1)
77        grid.layout().setAlignment(col_type, Qt.AlignHCenter)
78
79        row_type = gui.label(None, self, '%(row_type)s')
80        grid.layout().addWidget(row_type, 1, 0)
81        grid.layout().setAlignment(row_type, Qt.AlignVCenter)
82
83        self.view = QTableView()
84        self.model = None
85        grid.layout().addWidget(self.view, 1, 1)
86
87    def sizeHint(self):
88        return QSize(800, 500)
89
90    @Inputs.data
91    def set_data(self, data):
92        self.closeContext()
93        self.data = data
94        if data is not None:
95            self.init_attr_values(data.domain.metas)
96            self.openContext(self.data)
97            self.col_names = [str(a.name) for a in data.domain.attributes]
98            if hasattr(data, 'col_type'):
99                self.col_type = data.col_type
100        else:
101            self.init_attr_values(())
102        self.update_preview()
103        self.update_row_names()
104        self.unconditional_commit()
105
106    def init_attr_values(self, candidates):
107        self.col_type = ""
108        self.col_names = None
109
110        if candidates:
111            self.row_type = candidates[0].name
112            self.selected_meta = 1
113        else:
114            self.row_type = ""
115            self.selected_meta = 0
116            self.row_names = None
117
118        self.row_names_combo.clear()
119        self.row_names_combo.addItem('(None)')
120        for var in candidates:
121            self.row_names_combo.addItem(self.icons[var], var.name)
122        self.row_names_combo.setCurrentIndex(self.selected_meta)
123
124    def update_row_names(self):
125        if self.selected_meta:
126            self.row_names = list(self.data[:, -self.selected_meta].metas.flatten())
127        else:
128            self.row_names = None
129
130        if self.model:
131            self.model.headerDataChanged.emit(
132                Qt.Vertical, 0, self.model.rowCount() - 1)
133        self.commit()
134
135    def update_preview(self):
136        this = self
137
138        class MyTableModel(TableModel):
139            def headerData(self, section, orientation, role):
140                if orientation == Qt.Vertical and role == Qt.DisplayRole:
141                    if this.row_names:
142                        return this.row_names[section]
143                else:
144                    return super().headerData(section, orientation, role)
145
146        if self.data:
147            domain = Domain(self.data.domain.attributes)
148            preview_data = Table(domain, self.data)
149            self.model = MyTableModel(preview_data)
150        else:
151            self.model = None
152        self.view.setModel(self.model)
153
154    def apply(self):
155        self.commit()
156
157    def commit(self):
158        if self.data:
159            domain = self.data.domain
160            metadata_cols = list(domain.class_vars) + list(domain.metas)
161            metadata = [{var: var.to_val(value) for var, value in zip(metadata_cols, values.list)}
162                        for values in self.data[:, metadata_cols]]
163
164            if self.transpose:
165                relation = fusion.Relation(
166                    self.data.X.T, name=self.relation_name,
167                    row_type=fusion.ObjectType(self.col_type or 'Unknown'), row_names=self.col_names,
168                    col_type=fusion.ObjectType(self.row_type or 'Unknown'), col_names=self.row_names,
169                    col_metadata=metadata)
170            else:
171                relation = fusion.Relation(
172                    self.data.X, name=self.relation_name,
173                    row_type=fusion.ObjectType(self.row_type or 'Unknown'), row_names=self.row_names,
174                    row_metadata=metadata,
175                    col_type=fusion.ObjectType(self.col_type or 'Unknown'), col_names=self.col_names,
176                )
177            self.Outputs.relation.send(Relation(relation))
178
179
180if __name__ == '__main__':
181    from AnyQt.QtWidgets import QApplication
182    app = QApplication([])
183    ow = OWTableToRelation()
184    ow.set_data(Table('zoo'))
185    ow.show()
186    app.exec()
187    ow.saveSettings()
188