pyflutter-app 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- converter/__init__.py +0 -0
- converter/__main__.py +33 -0
- converter/assets/forms_config.json +57 -0
- converter/excel/__init__.py +0 -0
- converter/excel/_page_edit.py +199 -0
- converter/excel/controller.py +297 -0
- converter/excel/model.py +14 -0
- converter/excel/view.py +278 -0
- converter/lib_toi.py +797 -0
- converter/page_forms/__init__.py +195 -0
- converter/page_forms/common.py +81 -0
- converter/page_forms/config.py +129 -0
- converter/page_forms/controller_forms.py +186 -0
- converter/page_forms/model_forms.py +52 -0
- converter/page_forms/service.py +207 -0
- converter/table_widget.py +138 -0
- converter/whatsapp/__init__.py +0 -0
- converter/whatsapp/model.py +25 -0
- pyflutter_app-0.1.1.dist-info/METADATA +104 -0
- pyflutter_app-0.1.1.dist-info/RECORD +45 -0
- pyflutter_app-0.1.1.dist-info/WHEEL +4 -0
- pyflutter_ui/__init__.py +25 -0
- pyflutter_ui/app.py +305 -0
- pyflutter_ui/core.py +791 -0
- pyflutter_ui/file_picker.py +268 -0
- pyflutter_ui/navigator.py +95 -0
- pyflutter_ui/theme.py +1059 -0
- pyflutter_ui/widgets.py +1874 -0
- table_stream/__init__.py +12 -0
- table_stream/erros.py +102 -0
- table_stream/hash_map.py +307 -0
- table_stream/notifier.py +170 -0
- table_stream/pattner/__init__.py +7 -0
- table_stream/pattner/adapter.py +40 -0
- table_stream/pattner/observer.py +279 -0
- table_stream/sheet/__init__.py +10 -0
- table_stream/sheet/check_mod.py +9 -0
- table_stream/sheet/load.py +322 -0
- table_stream/sheet/options.py +72 -0
- table_stream/sheet/parse.py +207 -0
- table_stream/sheet/save.py +146 -0
- table_stream/state.py +284 -0
- table_stream/sysfiles.py +530 -0
- table_stream/thread.py +171 -0
- table_stream/workbook.py +19 -0
converter/__init__.py
ADDED
|
File without changes
|
converter/__main__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pyflutter_ui import (
|
|
2
|
+
Provider, TkMaterialApp, run_app
|
|
3
|
+
)
|
|
4
|
+
from converter.excel.view import HomePage
|
|
5
|
+
from converter.excel.controller import ControllerHomePage
|
|
6
|
+
from converter.excel.model import ExcelModel
|
|
7
|
+
|
|
8
|
+
from converter.page_forms import ControllerForms
|
|
9
|
+
from converter.page_forms.model_forms import ModelForms
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> None:
|
|
14
|
+
model = ExcelModel()
|
|
15
|
+
model_forms = ModelForms()
|
|
16
|
+
|
|
17
|
+
controller_excel = ControllerHomePage(model)
|
|
18
|
+
controller_forms = ControllerForms(model_forms)
|
|
19
|
+
|
|
20
|
+
app = TkMaterialApp(home=HomePage())
|
|
21
|
+
#app.register_notifier(controller)
|
|
22
|
+
Provider.add_state(controller_excel)
|
|
23
|
+
Provider.add_state(controller_excel.data_columns)
|
|
24
|
+
Provider.add_state(controller_forms)
|
|
25
|
+
|
|
26
|
+
app.set_width(760)
|
|
27
|
+
app.set_height(480)
|
|
28
|
+
run_app(app)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == '__main__':
|
|
32
|
+
main()
|
|
33
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"form_url": "https://forms.office.com/pages/responsepage.aspx?id=hoDG-Fy5y0mdOO03lAq-9Q3te9L0aHxBnNMUUIYjxH9UMVFGS0ozVEVKVU84RlBDNTJXVlQ1UUZZRy4u",
|
|
3
|
+
"success_text": "Sua resposta foi enviada",
|
|
4
|
+
"wait_after_submit_ms": 2500,
|
|
5
|
+
"fields": [
|
|
6
|
+
{
|
|
7
|
+
"name": "uc",
|
|
8
|
+
"question_text": "uc",
|
|
9
|
+
"field_type": "text",
|
|
10
|
+
"column": null,
|
|
11
|
+
"options": [],
|
|
12
|
+
"multiple_files": false,
|
|
13
|
+
"date_format": "%d/%m/%Y",
|
|
14
|
+
"required": true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "toi",
|
|
18
|
+
"question_text": "toi",
|
|
19
|
+
"field_type": "text",
|
|
20
|
+
"column": null,
|
|
21
|
+
"options": [],
|
|
22
|
+
"multiple_files": false,
|
|
23
|
+
"date_format": "%d/%m/%Y",
|
|
24
|
+
"required": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "local",
|
|
28
|
+
"question_text": "local de entrega",
|
|
29
|
+
"field_type": "radio",
|
|
30
|
+
"column": null,
|
|
31
|
+
"options": ["em mãos", "caixa de correios", "outro"],
|
|
32
|
+
"multiple_files": false,
|
|
33
|
+
"date_format": "%d/%m/%Y",
|
|
34
|
+
"required": true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "tipo",
|
|
38
|
+
"question_text": "tipo de documento",
|
|
39
|
+
"field_type": "dropdown",
|
|
40
|
+
"column": null,
|
|
41
|
+
"options": ["carta calculo fatura", "via toi", "cumprimento sentença"],
|
|
42
|
+
"multiple_files": false,
|
|
43
|
+
"date_format": "%d/%m/%Y",
|
|
44
|
+
"required": true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "data",
|
|
48
|
+
"question_text": "data",
|
|
49
|
+
"field_type": "date",
|
|
50
|
+
"column": null,
|
|
51
|
+
"options": [],
|
|
52
|
+
"multiple_files": false,
|
|
53
|
+
"date_format": "%d/%m/%Y",
|
|
54
|
+
"required": true
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# converter/excel/page_edit.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from pyflutter_ui import (
|
|
6
|
+
BuildContext, FlutterWidget, StatelessWidget,
|
|
7
|
+
Consumer, Column, Row, Expanded, Divider,
|
|
8
|
+
ElevatedButton, Text, SizedBox, Scaffold, AppBar,
|
|
9
|
+
MainAxisAlignment, CrossAxisAlignment,
|
|
10
|
+
SnackBar, SnackBarType, TableView, ValueNotifier,
|
|
11
|
+
ValueListenableBuilder, Provider, DropdownButton
|
|
12
|
+
)
|
|
13
|
+
from pyflutter_ui.widgets import ElevatedButton
|
|
14
|
+
|
|
15
|
+
from converter.excel.controller import ControllerHomePage, DataNotifer, CommandEditColumns
|
|
16
|
+
from converter.table_widget import TableOrProgress
|
|
17
|
+
from table_stream import ArrayList
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PageEditExcel(StatelessWidget):
|
|
21
|
+
"""
|
|
22
|
+
Página de edição de dados — acessada via Navigator.push().
|
|
23
|
+
Exibe o DataFrame carregado e permite operações básicas.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, key: str | None = None) -> None:
|
|
27
|
+
super().__init__(key)
|
|
28
|
+
|
|
29
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
30
|
+
return _PageEditState()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _PageEditState(StatelessWidget):
|
|
34
|
+
|
|
35
|
+
def __init__(self) -> None:
|
|
36
|
+
super().__init__()
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def controller(self) -> ControllerHomePage:
|
|
40
|
+
return Provider.read(ControllerHomePage)
|
|
41
|
+
|
|
42
|
+
def add_work_col(self):
|
|
43
|
+
"""
|
|
44
|
+
Ação do botão que adiciona uma coluna a lista de trabalho
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
_selected: str = self.controller.data_notifier.selectedColumnSheet
|
|
48
|
+
self.controller.data_columns.add_column_edit(_selected)
|
|
49
|
+
|
|
50
|
+
print(f'{__class__.__name__} [DEBUG] coluna adicionada ... {_selected}')
|
|
51
|
+
|
|
52
|
+
def action_back_page(self, context: BuildContext) -> None:
|
|
53
|
+
print(context.name)
|
|
54
|
+
context.navigator.pop()
|
|
55
|
+
|
|
56
|
+
def action_drop_column(self, context: BuildContext, columns: list[str]) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Ação para apagar uma lista de colunas.
|
|
59
|
+
"""
|
|
60
|
+
self.controller.data_columns.delete_columns(ArrayList(columns))
|
|
61
|
+
|
|
62
|
+
def action_load_data(self, context: BuildContext) -> None:
|
|
63
|
+
|
|
64
|
+
if self.controller.get_file_sheet() is None:
|
|
65
|
+
SnackBar.of(context).show(
|
|
66
|
+
"Importe um arquivo na página anterior para prosseguir!",
|
|
67
|
+
type=SnackBarType.INFO,
|
|
68
|
+
)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
self.controller.command_load_data()
|
|
72
|
+
|
|
73
|
+
def action_change_selected_column(self, col: str):
|
|
74
|
+
self.controller.data_columns.selectedColumn = col
|
|
75
|
+
|
|
76
|
+
def build_selected_columns(self, context: BuildContext, controller: ControllerHomePage):
|
|
77
|
+
"""
|
|
78
|
+
Colunas para edição apagar/concatenar etc.
|
|
79
|
+
"""
|
|
80
|
+
columns: ArrayList[str] = controller.data_columns.value
|
|
81
|
+
current = columns.get_first() if columns.size() > 0 else "-"
|
|
82
|
+
|
|
83
|
+
return DropdownButton(
|
|
84
|
+
columns,
|
|
85
|
+
current,
|
|
86
|
+
on_changed=self.action_change_selected_column
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def build_value_selected_columns(self, context: BuildContext, columns: ArrayList[str]):
|
|
90
|
+
"""
|
|
91
|
+
Colunas para edição apagar/concatenar etc.
|
|
92
|
+
"""
|
|
93
|
+
current = columns.get_first() if columns.size() > 0 else "-"
|
|
94
|
+
|
|
95
|
+
return DropdownButton(
|
|
96
|
+
columns,
|
|
97
|
+
current,
|
|
98
|
+
on_changed=self.action_change_selected_column
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def build_edit_options(self, context: BuildContext, controller_columns: CommandEditColumns) -> FlutterWidget:
|
|
102
|
+
"""
|
|
103
|
+
Botões para editar o DataFrame()
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
df: pd.DataFrame = controller_columns.controller.data_notifier.value
|
|
107
|
+
if df.empty:
|
|
108
|
+
return Text("Nenhum dado carregado")
|
|
109
|
+
|
|
110
|
+
cols: ArrayList[str] = ArrayList(df.columns.tolist())
|
|
111
|
+
|
|
112
|
+
return Row(
|
|
113
|
+
children=[
|
|
114
|
+
ElevatedButton("Adicionar coluna", on_pressed=self.add_work_col),
|
|
115
|
+
SizedBox(width=8),
|
|
116
|
+
DropdownButton(
|
|
117
|
+
cols,
|
|
118
|
+
on_changed=lambda x: controller_columns.controller.data_notifier.set_selected_column_sheet(x),
|
|
119
|
+
),
|
|
120
|
+
Divider(),
|
|
121
|
+
#Consumer(ExcelController, self.build_selected_columns,),
|
|
122
|
+
ValueListenableBuilder[ArrayList[str]](
|
|
123
|
+
controller_columns, self.build_value_selected_columns
|
|
124
|
+
),
|
|
125
|
+
],
|
|
126
|
+
main_axis_alignment=MainAxisAlignment.START,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def build_table(self, context: BuildContext, ctrl: ControllerHomePage) -> FlutterWidget:
|
|
130
|
+
|
|
131
|
+
df = ctrl.data_notifier.value
|
|
132
|
+
if df.empty:
|
|
133
|
+
df = pd.DataFrame({"A": ["Sem dados"]})
|
|
134
|
+
return TableOrProgress(
|
|
135
|
+
running=ctrl.data_notifier.running,
|
|
136
|
+
df=df,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def build_info_sheet(self, context: BuildContext, controller: ControllerHomePage) -> FlutterWidget:
|
|
140
|
+
print(f'_build_info: {context.name}')
|
|
141
|
+
|
|
142
|
+
df = controller.data_notifier.value
|
|
143
|
+
if df.empty:
|
|
144
|
+
return Text("Nenhum dado disponível")
|
|
145
|
+
|
|
146
|
+
linhas, colunas = df.shape
|
|
147
|
+
arquivo = controller.get_file_sheet()
|
|
148
|
+
nome = arquivo.basename() if arquivo else "—"
|
|
149
|
+
|
|
150
|
+
return Row(
|
|
151
|
+
children=[
|
|
152
|
+
Text(f"Arquivo: {nome}"),
|
|
153
|
+
Divider(orientation="horizontal"),
|
|
154
|
+
Text(f"{linhas} linhas"),
|
|
155
|
+
Divider(orientation="horizontal"),
|
|
156
|
+
Text(f"{colunas} colunas"),
|
|
157
|
+
],
|
|
158
|
+
main_axis_alignment=MainAxisAlignment.START,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def build_body_app(self, context: BuildContext, ctrl: ControllerHomePage) -> FlutterWidget:
|
|
162
|
+
|
|
163
|
+
return Column(
|
|
164
|
+
children=[
|
|
165
|
+
# ── barra de ações ──────────────────────────
|
|
166
|
+
Row(
|
|
167
|
+
children=[
|
|
168
|
+
ElevatedButton(
|
|
169
|
+
"← Voltar",
|
|
170
|
+
on_pressed=lambda: self.action_back_page(context),
|
|
171
|
+
),
|
|
172
|
+
Divider(orientation="horizontal"),
|
|
173
|
+
ElevatedButton(
|
|
174
|
+
"Recarregar dados",
|
|
175
|
+
on_pressed=lambda: self.action_load_data(context),
|
|
176
|
+
),
|
|
177
|
+
# ── info do dataset ─────────────────────────
|
|
178
|
+
Consumer(ControllerHomePage, self.build_info_sheet),
|
|
179
|
+
],
|
|
180
|
+
main_axis_alignment=MainAxisAlignment.START,
|
|
181
|
+
),
|
|
182
|
+
Divider(),
|
|
183
|
+
|
|
184
|
+
Divider(),
|
|
185
|
+
# ── seletor de coluna para remoção ──────────
|
|
186
|
+
Consumer(CommandEditColumns, self.build_edit_options),
|
|
187
|
+
Divider(),
|
|
188
|
+
# ── tabela ──────────────────────────────────
|
|
189
|
+
Expanded(Consumer(ControllerHomePage, self.build_table)),
|
|
190
|
+
],
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
194
|
+
context.name = "Build do Edit"
|
|
195
|
+
|
|
196
|
+
return Scaffold(
|
|
197
|
+
Consumer(ControllerHomePage, builder=self.build_body_app),
|
|
198
|
+
#app_bar=AppBar("Edição de dados"),
|
|
199
|
+
)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from pandas import DataFrame
|
|
6
|
+
|
|
7
|
+
from table_stream import (
|
|
8
|
+
SheetLoader, SheetType, SaveData, csvEncoding, csvSeparator,
|
|
9
|
+
CsvOptions, OptionsIOSheet, ParserData, FactorySaveData,
|
|
10
|
+
FactorySheetLoader, ArrayList, HashMapDict, ControllerThread,
|
|
11
|
+
ConsumerThread, ThreadStateContext, ContextState
|
|
12
|
+
)
|
|
13
|
+
from table_stream.sysfiles import Directory, File
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from pyflutter_ui import ChangeNotifier, CommandNotifier, ValueNotifier
|
|
17
|
+
from converter.excel.model import ExcelModel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FileNotifier(ValueNotifier[HashMapDict[str, File]]):
|
|
21
|
+
"""
|
|
22
|
+
Controller/Notificador
|
|
23
|
+
|
|
24
|
+
Notifica sobre a alteração de arquivos selecionados/adicionados pelo usuário.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def __init__(self, value: Any) -> None:
|
|
29
|
+
super().__init__(value)
|
|
30
|
+
|
|
31
|
+
def get_file_from_name(self, name: str) -> File:
|
|
32
|
+
if not self.value.header().contains(name):
|
|
33
|
+
raise ValueError()
|
|
34
|
+
return self.value.get_value(name)
|
|
35
|
+
|
|
36
|
+
def clear(self) -> None:
|
|
37
|
+
self._value.clear()
|
|
38
|
+
|
|
39
|
+
def delete_file(self, name: str):
|
|
40
|
+
self._value.delete_items([name])
|
|
41
|
+
|
|
42
|
+
def add_files(self, files: list[File]) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Adiciona apenas arquivos NÃO EXISTENTES. Se o basename de um
|
|
45
|
+
arquivo da lista for igual o basename de outro arquivo já existente
|
|
46
|
+
tal arquivo será ignorado, mantendo-se apenas o arquivo já existente.
|
|
47
|
+
"""
|
|
48
|
+
file: File
|
|
49
|
+
for file in files:
|
|
50
|
+
if not self._value.header().contains(file.basename()):
|
|
51
|
+
self._value.set_value(file.basename(), file)
|
|
52
|
+
self.notify_listeners()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DataNotifer(ValueNotifier[pd.DataFrame]):
|
|
56
|
+
|
|
57
|
+
def __init__(self, value: DataFrame) -> None:
|
|
58
|
+
super().__init__(value)
|
|
59
|
+
self.running: bool = False
|
|
60
|
+
cols = ArrayList(value.columns.tolist())
|
|
61
|
+
self.selectedColumnSheet: str = cols.get_first() if cols.size() > 0 else "-"
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def value(self) -> DataFrame:
|
|
65
|
+
return self._value
|
|
66
|
+
|
|
67
|
+
@value.setter
|
|
68
|
+
def value(self, value: pd.DataFrame) -> None:
|
|
69
|
+
self._value = value
|
|
70
|
+
|
|
71
|
+
if (self.selectedColumnSheet is None) or (self.selectedColumnSheet == "-"):
|
|
72
|
+
columns = self.value.columns.tolist()
|
|
73
|
+
if len(columns) > 0:
|
|
74
|
+
self.selectedColumnSheet = columns[0]
|
|
75
|
+
|
|
76
|
+
self.notify_listeners()
|
|
77
|
+
|
|
78
|
+
def set_selected_column_sheet(self, col: str):
|
|
79
|
+
self.selectedColumnSheet = col
|
|
80
|
+
print(f'{__class__.__name__} coluna alterada: {col}')
|
|
81
|
+
|
|
82
|
+
def clear(self):
|
|
83
|
+
self.running = False
|
|
84
|
+
self.value = pd.DataFrame()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CommandEditColumns(ValueNotifier[ArrayList[str]]):
|
|
88
|
+
|
|
89
|
+
def __init__(self, value: ArrayList[str], controller: ControllerHomePage) -> None:
|
|
90
|
+
super().__init__(value)
|
|
91
|
+
self.running: bool = False
|
|
92
|
+
self.controller: ControllerHomePage = controller
|
|
93
|
+
self._columnsEdit: ArrayList[str] = value
|
|
94
|
+
self.selectedColumn: str | None = None
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def columns(self) -> ArrayList[str]:
|
|
98
|
+
return ArrayList(self.controller.data_notifier.value.columns.tolist())
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def data(self) -> pd.DataFrame:
|
|
102
|
+
return self.controller.data_notifier.value
|
|
103
|
+
|
|
104
|
+
@data.setter
|
|
105
|
+
def data(self, data: pd.DataFrame):
|
|
106
|
+
self.controller.data_notifier.value = data
|
|
107
|
+
|
|
108
|
+
def get_columns_edit(self) -> ArrayList[str]:
|
|
109
|
+
return self._columnsEdit
|
|
110
|
+
|
|
111
|
+
def set_columns_edit(self, cols: ArrayList[str]):
|
|
112
|
+
self._columnsEdit = cols
|
|
113
|
+
|
|
114
|
+
def add_column_edit(self, col: str):
|
|
115
|
+
if col is None:
|
|
116
|
+
return
|
|
117
|
+
if not self.columns.contains(col):
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if not self.get_columns_edit().contains(col):
|
|
121
|
+
self._columnsEdit.append(col)
|
|
122
|
+
self.notify_listeners()
|
|
123
|
+
print(f'* [DEBUG] coluna adicionada ==> {col}')
|
|
124
|
+
|
|
125
|
+
def delete_columns(self, cols: ArrayList[str]):
|
|
126
|
+
self.running = True
|
|
127
|
+
self.notify_listeners()
|
|
128
|
+
|
|
129
|
+
remove_list = ArrayList()
|
|
130
|
+
for col in cols:
|
|
131
|
+
if self.columns.contains(col):
|
|
132
|
+
remove_list.append(col)
|
|
133
|
+
|
|
134
|
+
if remove_list.size() < 1:
|
|
135
|
+
self.running = False
|
|
136
|
+
self.notify_listeners()
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
parser = ParserData(self.data)
|
|
140
|
+
parser.delete_columns(remove_list)
|
|
141
|
+
self.data = parser.get_data()
|
|
142
|
+
self.running = False
|
|
143
|
+
self.notify_listeners()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ControllerHomePage(ValueNotifier[DataFrame]):
|
|
147
|
+
|
|
148
|
+
def __init__(self, model: ExcelModel, *, value: pd.DataFrame = None) -> None:
|
|
149
|
+
if value is None:
|
|
150
|
+
value = pd.DataFrame()
|
|
151
|
+
super().__init__(value)
|
|
152
|
+
self.model: ExcelModel = model
|
|
153
|
+
self.operation_state: ContextState = ContextState()
|
|
154
|
+
|
|
155
|
+
# Opções para leitura de uma planilha
|
|
156
|
+
self.csv_options = CsvOptions()
|
|
157
|
+
self._file: File = None
|
|
158
|
+
self._sheet_name: str = None
|
|
159
|
+
self.sheet_names: ArrayList[str] = ArrayList()
|
|
160
|
+
|
|
161
|
+
# Mapa de arquivos importados pelo usuário
|
|
162
|
+
self.files_notifier: FileNotifier = FileNotifier(HashMapDict())
|
|
163
|
+
|
|
164
|
+
# Opções de exportação
|
|
165
|
+
self._options_save: OptionsIOSheet = OptionsIOSheet(SheetType.EXCEL)
|
|
166
|
+
|
|
167
|
+
#======================================================#
|
|
168
|
+
# Leitura dos dados
|
|
169
|
+
#======================================================#
|
|
170
|
+
self.data_notifier = DataNotifer(pd.DataFrame())
|
|
171
|
+
self.data_columns = CommandEditColumns(ArrayList(), self)
|
|
172
|
+
|
|
173
|
+
# Thread
|
|
174
|
+
self._currentThread: ControllerThread = None
|
|
175
|
+
self.thread_context: ThreadStateContext = ThreadStateContext()
|
|
176
|
+
# Menssagens
|
|
177
|
+
self.msg_erro = None
|
|
178
|
+
self.msg_info = None
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def sheet_name(self) -> str:
|
|
182
|
+
return self._sheet_name
|
|
183
|
+
|
|
184
|
+
@sheet_name.setter
|
|
185
|
+
def sheet_name(self, new: str):
|
|
186
|
+
if not self.sheet_names.contains(new):
|
|
187
|
+
print(f'[DEBUG] Aba ... {new} não está disponível para o arquivo: {self._file}')
|
|
188
|
+
return
|
|
189
|
+
self._sheet_name = new
|
|
190
|
+
self.msg_info = f"Aba atualizada ... {self._sheet_name}"
|
|
191
|
+
self.notify_listeners()
|
|
192
|
+
self.command_load_data()
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def options_load(self) -> OptionsIOSheet | None:
|
|
196
|
+
if self._file is None:
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
opt_load = OptionsIOSheet(SheetType.EXCEL if self._file.is_excel() else SheetType.CSV)
|
|
200
|
+
|
|
201
|
+
if self._file.is_ods():
|
|
202
|
+
opt_load.sheet_type = SheetType.ODS
|
|
203
|
+
if self._sheet_name is not None:
|
|
204
|
+
opt_load.sheet_name = self._sheet_name
|
|
205
|
+
|
|
206
|
+
return opt_load
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def sheet_loader(self) -> SheetLoader:
|
|
210
|
+
if self._file is None:
|
|
211
|
+
raise ValueError('Nenhum arquivo disponível')
|
|
212
|
+
return SheetLoader.create(self.options_load, self._file)
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def running(self) -> bool:
|
|
216
|
+
return self.operation_state.state.is_running()
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def files(self) -> ArrayList[File]:
|
|
220
|
+
return self.files_notifier.value.values()
|
|
221
|
+
|
|
222
|
+
def create_consumer(self, on_finish: Callable[[ThreadStateContext]]) -> ConsumerThread:
|
|
223
|
+
consumer = ConsumerThread(ThreadStateContext, on_finish=on_finish)
|
|
224
|
+
self.thread_context.add_listener(consumer)
|
|
225
|
+
return consumer
|
|
226
|
+
|
|
227
|
+
def init_thread(self) -> None:
|
|
228
|
+
self._currentThread._state_context = self.thread_context
|
|
229
|
+
self._currentThread.start()
|
|
230
|
+
self.operation_state.start()
|
|
231
|
+
self.notify_listeners()
|
|
232
|
+
|
|
233
|
+
def set_file_sheet(self, file: File):
|
|
234
|
+
self._file = file
|
|
235
|
+
self.sheet_names = self.sheet_loader.get_sheet_names()
|
|
236
|
+
self.notify_listeners()
|
|
237
|
+
self.sheet_name = self.sheet_names.get_first()
|
|
238
|
+
|
|
239
|
+
def get_file_sheet(self) -> File:
|
|
240
|
+
return self._file
|
|
241
|
+
|
|
242
|
+
def add_files(self, files: ArrayList[File]):
|
|
243
|
+
self.files_notifier.add_files(files)
|
|
244
|
+
self.notify_listeners()
|
|
245
|
+
|
|
246
|
+
def command_delete_columns(self):
|
|
247
|
+
if self.running:
|
|
248
|
+
raise RuntimeError("Já existe outra operação de leitura em andamento...")
|
|
249
|
+
|
|
250
|
+
self._currentThread = ControllerThread(
|
|
251
|
+
target=self.data_columns.delete_columns, args=self.data_columns.value
|
|
252
|
+
)
|
|
253
|
+
self.create_consumer(lambda context: self.operation_state.finish(context.is_error()))
|
|
254
|
+
|
|
255
|
+
self.init_thread()
|
|
256
|
+
|
|
257
|
+
def action_finish_delete_columns(self, result: ThreadStateContext[None]):
|
|
258
|
+
self.operation_state.finish(result.is_error())
|
|
259
|
+
self.notify_listeners()
|
|
260
|
+
|
|
261
|
+
def command_load_data(self) -> None:
|
|
262
|
+
if self.running:
|
|
263
|
+
raise RuntimeError("Já existe outra operação de leitura em andamento...")
|
|
264
|
+
|
|
265
|
+
loader = self.sheet_loader
|
|
266
|
+
self._currentThread = ControllerThread(target=loader.load)
|
|
267
|
+
c = self.create_consumer(self.action_finish_load)
|
|
268
|
+
c.on_error = self._action_error_load_data
|
|
269
|
+
|
|
270
|
+
self.data_notifier.running = True
|
|
271
|
+
self.data_notifier.value = pd.DataFrame()
|
|
272
|
+
self.msg_info = "Carregando dados..."
|
|
273
|
+
self.init_thread()
|
|
274
|
+
|
|
275
|
+
def _action_error_load_data(self, result: ThreadStateContext):
|
|
276
|
+
self.data_notifier.running = False
|
|
277
|
+
self.msg_erro = f'{result.get_exception()}'
|
|
278
|
+
|
|
279
|
+
def action_finish_load(self, result: ThreadStateContext[pd.DataFrame]) -> None:
|
|
280
|
+
self.data_notifier.running = False
|
|
281
|
+
self.data_notifier.value = result.value
|
|
282
|
+
self._currentThread = None
|
|
283
|
+
|
|
284
|
+
if not self.data_notifier.value.empty:
|
|
285
|
+
self.msg_info = "Planilha carregada com sucesso..."
|
|
286
|
+
|
|
287
|
+
self.operation_state.finish(result.is_error())
|
|
288
|
+
self.notify_listeners()
|
|
289
|
+
|
|
290
|
+
def init_state(self, data: HashMapDict = None):
|
|
291
|
+
if data is not None:
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
def dispose(self):
|
|
295
|
+
self._options_load = None
|
|
296
|
+
self._options_save = None
|
|
297
|
+
self.files_notifier.value.clear()
|
converter/excel/model.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from pandas import DataFrame
|
|
3
|
+
from table_stream.hash_map import ArrayList, HashMapDict
|
|
4
|
+
from table_stream.sheet import (
|
|
5
|
+
SheetLoader, SheetType, SaveData, csvEncoding, csvSeparator,
|
|
6
|
+
CsvOptions, OptionsIOSheet, ParserData, FactorySaveData,
|
|
7
|
+
FactorySheetLoader,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ExcelModel(object):
|
|
12
|
+
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
pass
|