pyflutter-app 0.3.13__tar.gz → 0.3.14__tar.gz
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.
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/PKG-INFO +1 -1
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/cartas/__init__.py +239 -8
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyproject.toml +1 -1
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/uv.lock +1 -1
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/whatsapp_files/__init__.py +103 -1
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.gitignore +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/.gitignore +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/misc.xml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/modules.xml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/pyflutter_app.iml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/vcs.xml +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/README.md +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/convert_stream/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/__main__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/assets/forms_config.json +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/config_app.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/_page_edit.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/controller.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/model.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/page_sheets.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/home_page/view.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/lib_toi.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/common.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/controller_circuit.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/model_circuit.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/service.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_circuit/view_circuit.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/Diagnorstico.txt +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/common.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/config.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/controller_forms.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/diagnose_forms.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/master_forms.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/model_forms.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/service.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_forms/view_forms.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/common.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/controller_image.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/model_image.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/progress.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/renderer.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/view_image.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_image/widgets.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_ocr/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_ocr/common.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_ocr/controller_ocr.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_ocr/model_ocr.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_ocr/view_ocr.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/PATCH_whatsapp_files.md +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/common.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/controller_whatsapp.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/model_whatsapp.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/view_whatsapp.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/run_app_convert.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/table_widget.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/util.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/local_build/linux.sh +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/local_build/send.sh +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/main.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/mvc/controller.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/mvc/model.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/mvc/view.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/new-export_app.sh +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/ocr_stream/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/app.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/file_picker.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/navigator.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/scheduler.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/theme.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/buttons.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/container.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/core.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/list_builder.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/progress.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/tables.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/pyflutter_ui/ui/widgets.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/table_stream/__init__.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/teste.py +0 -0
- {pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/update.sh +0 -0
|
@@ -9,6 +9,7 @@ from abc import ABC, abstractmethod
|
|
|
9
9
|
from os import rename as rename_file
|
|
10
10
|
from os import replace as replace_file
|
|
11
11
|
from datetime import datetime
|
|
12
|
+
import threading
|
|
12
13
|
|
|
13
14
|
import pandas as pd
|
|
14
15
|
|
|
@@ -33,7 +34,7 @@ from table_stream import (
|
|
|
33
34
|
load_file_hash,
|
|
34
35
|
)
|
|
35
36
|
|
|
36
|
-
from ocr_stream import HeaderOcr, BuilderConfigOcr, CreateRelatorioOcr
|
|
37
|
+
# from ocr_stream import HeaderOcr, BuilderConfigOcr, CreateRelatorioOcr
|
|
37
38
|
|
|
38
39
|
from whatsapp_files import (
|
|
39
40
|
RowWhatsApp,
|
|
@@ -41,6 +42,7 @@ from whatsapp_files import (
|
|
|
41
42
|
HeaderWhatsApp,
|
|
42
43
|
BuilderTableWhatsApp,
|
|
43
44
|
ControllerWhatsApp,
|
|
45
|
+
BuilderConfigWhatsApp, # NOVO: base RFA única (substitui BuilderConfigOcr)
|
|
44
46
|
move_path,
|
|
45
47
|
)
|
|
46
48
|
|
|
@@ -60,6 +62,111 @@ class ConfigTOI:
|
|
|
60
62
|
work_dir: Directory
|
|
61
63
|
|
|
62
64
|
|
|
65
|
+
# Mesmos nomes de arquivo gravados por ocr_stream. Se mudar lá, mude aqui.
|
|
66
|
+
_OCR_FILE_IMAGE = "tabela_ocr_imagens.xlsx"
|
|
67
|
+
_OCR_FILE_PDF = "tabela_ocr_pdf.xlsx"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class BuilderConfigTOI(object):
|
|
71
|
+
"""
|
|
72
|
+
Configuração de cartas SEM depender de ocr_stream.
|
|
73
|
+
Base única: <Documentos>/RFA (BuilderConfigWhatsApp.dir_base_root),
|
|
74
|
+
tabelas em <Documentos>/RFA/Tabelas (mesmos caminhos do ocr_stream).
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
78
|
+
|
|
79
|
+
def __new__(cls, *args, **kwargs):
|
|
80
|
+
if cls._instance is None:
|
|
81
|
+
cls._instance = super(BuilderConfigTOI, cls).__new__(cls)
|
|
82
|
+
return cls._instance
|
|
83
|
+
|
|
84
|
+
def __init__(self) -> None:
|
|
85
|
+
|
|
86
|
+
# Garante que __init__ não será executado mais de uma vez
|
|
87
|
+
if hasattr(self, "_initialized") and self._initialized:
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
super().__init__()
|
|
91
|
+
self._initialized: bool = True
|
|
92
|
+
self._builder_wp = BuilderConfigWhatsApp()
|
|
93
|
+
self._config = self._create()
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------- base RFA
|
|
96
|
+
@property
|
|
97
|
+
def dir_base_work(self) -> Directory:
|
|
98
|
+
return self._builder_wp.dir_base_root
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def dir_table(self) -> Directory:
|
|
102
|
+
"""<Documentos>/RFA/Tabelas"""
|
|
103
|
+
return self._builder_wp.dir_table
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------- tabelas OCR (somente leitura)
|
|
106
|
+
@property
|
|
107
|
+
def file_table_ocr_image(self) -> File:
|
|
108
|
+
return self.dir_table.join_file(_OCR_FILE_IMAGE)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def file_table_ocr_pdf(self) -> File:
|
|
112
|
+
return self.dir_table.join_file(_OCR_FILE_PDF)
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------- pastas TOI
|
|
115
|
+
@property
|
|
116
|
+
def dir_retorno_de_campo(self) -> Directory:
|
|
117
|
+
return self.dir_base_work.concat("Cartas-TOI", True).concat(
|
|
118
|
+
"Retorno-de-campo", True
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def dir_log_excel(self) -> Directory:
|
|
123
|
+
return self.dir_base_work.concat("Cartas-TOI", True).concat("Logs", True)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def dir_relatorios(self) -> Directory:
|
|
127
|
+
return self.dir_base_work.concat("Cartas-TOI", True).concat(
|
|
128
|
+
"Relatorios-toi", True
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# ---------------------------------------------------------- arquivos TOI
|
|
132
|
+
@property
|
|
133
|
+
def file_relatorio_toi(self) -> File:
|
|
134
|
+
return self.dir_table.join_file("Relatorio-tois.xlsx")
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def file_relatorio_forms(self) -> File:
|
|
138
|
+
return self.dir_table.join_file("cartas-toi-lancadas.xlsx")
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def file_master_forms(self) -> File:
|
|
142
|
+
return self.dir_table.join_file("master-forms-toi.xlsx")
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def file_table_toi(self) -> File:
|
|
146
|
+
return self.dir_table.join_file("tabela-entregas-tois.xlsx")
|
|
147
|
+
|
|
148
|
+
def _create(self) -> ConfigTOI:
|
|
149
|
+
return ConfigTOI(
|
|
150
|
+
work_dir=self.dir_base_work,
|
|
151
|
+
dir_base_toi=self.dir_retorno_de_campo,
|
|
152
|
+
file_relatorio_toi=self.file_relatorio_toi,
|
|
153
|
+
file_table_toi=self.file_table_toi,
|
|
154
|
+
file_cartas_lancadas=self.file_relatorio_forms,
|
|
155
|
+
dir_log_excel=self.dir_log_excel,
|
|
156
|
+
dir_relatorios=self.dir_relatorios,
|
|
157
|
+
file_master_forms=self.file_master_forms,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def reset_config(self) -> None:
|
|
161
|
+
self._config = None
|
|
162
|
+
|
|
163
|
+
def builder(self) -> ConfigTOI:
|
|
164
|
+
if self._config is None:
|
|
165
|
+
self._config = self._create()
|
|
166
|
+
return self._config
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
"""
|
|
63
170
|
class BuilderConfigTOI(object):
|
|
64
171
|
|
|
65
172
|
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
@@ -136,6 +243,131 @@ class BuilderConfigTOI(object):
|
|
|
136
243
|
if self._config is None:
|
|
137
244
|
self._config = self._create()
|
|
138
245
|
return self._config
|
|
246
|
+
"""
|
|
247
|
+
# ============================================================#
|
|
248
|
+
# Leitura das tabelas OCR direto dos .xlsx (sem ocr_stream)
|
|
249
|
+
# ============================================================#
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class HeaderOcr(StrEnum):
|
|
253
|
+
"""Colunas das tabelas OCR (mesmos nomes gravados pelo ocr_stream)."""
|
|
254
|
+
|
|
255
|
+
ID = "ID"
|
|
256
|
+
HASH_FILE = HeaderMetaData.HASH_FILE.value
|
|
257
|
+
NUM_PAG = "PÁGINA"
|
|
258
|
+
NUM_LINHA = "LINHA"
|
|
259
|
+
TEXT = "TEXTO"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
_OCR_COLUMNS: list[str] = [h.value for h in HeaderOcr]
|
|
263
|
+
_OCR_C_HASH = HeaderOcr.HASH_FILE.value
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class ReadTablesOcr(object):
|
|
267
|
+
"""
|
|
268
|
+
Leitor SOMENTE-LEITURA de RFA/Tabelas/tabela_ocr_imagens.xlsx e
|
|
269
|
+
tabela_ocr_pdf.xlsx. Equivale ao que cartas usava de CreateRelatorioOcr,
|
|
270
|
+
mas sem importar ocr_stream/tesseract/cv2.
|
|
271
|
+
|
|
272
|
+
Cache em memória por arquivo, invalidado quando o .xlsx muda
|
|
273
|
+
(mtime + tamanho). Assim, se outro processo/PC rodar o OCR, a próxima
|
|
274
|
+
consulta já enxerga o dado novo.
|
|
275
|
+
|
|
276
|
+
As tabelas devolvidas por load_table_* são o objeto do cache: trate
|
|
277
|
+
como SOMENTE LEITURA (nunca chame merge()/set() nelas).
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
KIND_IMAGE = "imagem"
|
|
281
|
+
KIND_PDF = "pdf"
|
|
282
|
+
|
|
283
|
+
def __init__(self) -> None:
|
|
284
|
+
self._lock = threading.RLock()
|
|
285
|
+
self._cache: dict[str, tuple[tuple[int, int] | None, TableData[HeaderOcr]]] = {}
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------- infra
|
|
288
|
+
@staticmethod
|
|
289
|
+
def _file_of(kind: str) -> File:
|
|
290
|
+
cfg = BuilderConfigTOI()
|
|
291
|
+
if kind == ReadTablesOcr.KIND_IMAGE:
|
|
292
|
+
return cfg.file_table_ocr_image
|
|
293
|
+
return cfg.file_table_ocr_pdf
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def _stamp(f: File) -> tuple[int, int] | None:
|
|
297
|
+
try:
|
|
298
|
+
st = os.stat(f.absolute())
|
|
299
|
+
except OSError:
|
|
300
|
+
return None
|
|
301
|
+
return (st.st_mtime_ns, st.st_size)
|
|
302
|
+
|
|
303
|
+
@staticmethod
|
|
304
|
+
def _void() -> TableData[HeaderOcr]:
|
|
305
|
+
return TableData(pd.DataFrame(columns=_OCR_COLUMNS))
|
|
306
|
+
|
|
307
|
+
@staticmethod
|
|
308
|
+
def _read(f: File) -> TableData[HeaderOcr]:
|
|
309
|
+
# dtype=str + fillna(""): célula vazia vira "" (e não o texto "nan").
|
|
310
|
+
df = pd.read_excel(f.absolute(), dtype=str).fillna("")
|
|
311
|
+
for c in _OCR_COLUMNS:
|
|
312
|
+
if c not in df.columns:
|
|
313
|
+
df[c] = ""
|
|
314
|
+
return TableData.create_from_data(
|
|
315
|
+
df[_OCR_COLUMNS].astype(str).reset_index(drop=True)
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def _load(self, kind: str) -> TableData[HeaderOcr]:
|
|
319
|
+
f = self._file_of(kind)
|
|
320
|
+
stamp = self._stamp(f)
|
|
321
|
+
|
|
322
|
+
with self._lock:
|
|
323
|
+
cached = self._cache.get(kind)
|
|
324
|
+
if cached is not None and cached[0] == stamp:
|
|
325
|
+
return cached[1]
|
|
326
|
+
|
|
327
|
+
if stamp is None: # arquivo ainda não existe (OCR nunca rodou)
|
|
328
|
+
tb = self._void()
|
|
329
|
+
else:
|
|
330
|
+
try:
|
|
331
|
+
print(f"Lendo tabela OCR ({kind}) ... {f.absolute()}")
|
|
332
|
+
tb = self._read(f)
|
|
333
|
+
except Exception as e:
|
|
334
|
+
print(f"[AVISO] falha ao ler {f.absolute()}: {e}")
|
|
335
|
+
# mantém o cache anterior (se houver) e tenta de novo depois
|
|
336
|
+
return cached[1] if cached is not None else self._void()
|
|
337
|
+
|
|
338
|
+
self._cache[kind] = (stamp, tb)
|
|
339
|
+
return tb
|
|
340
|
+
|
|
341
|
+
# ---------------------------------------------------------- READ
|
|
342
|
+
def load_table_image(self) -> TableData[HeaderOcr]:
|
|
343
|
+
return self._load(self.KIND_IMAGE)
|
|
344
|
+
|
|
345
|
+
def load_table_pdf(self) -> TableData[HeaderOcr]:
|
|
346
|
+
return self._load(self.KIND_PDF)
|
|
347
|
+
|
|
348
|
+
def create_from_hashs(self, hashes: Iterable[str]) -> TableData[HeaderOcr] | None:
|
|
349
|
+
"""
|
|
350
|
+
Linhas OCR (imagem + pdf) das hashs informadas, SEM aplicar OCR.
|
|
351
|
+
None se nada for encontrado. O resultado é uma cópia (pode alterar).
|
|
352
|
+
"""
|
|
353
|
+
wanted = {str(h) for h in hashes}
|
|
354
|
+
if not wanted:
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
frames: list[pd.DataFrame] = []
|
|
358
|
+
for kind in (self.KIND_IMAGE, self.KIND_PDF):
|
|
359
|
+
df = self._load(kind).get_data()
|
|
360
|
+
if df.empty:
|
|
361
|
+
continue
|
|
362
|
+
sub = df[df[_OCR_C_HASH].isin(wanted)]
|
|
363
|
+
if not sub.empty:
|
|
364
|
+
frames.append(sub)
|
|
365
|
+
|
|
366
|
+
if not frames:
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
final = pd.concat(frames, ignore_index=True)
|
|
370
|
+
return TableData.create_from_data(final[_OCR_COLUMNS].astype(str))
|
|
139
371
|
|
|
140
372
|
|
|
141
373
|
# ============================================================#
|
|
@@ -296,7 +528,8 @@ class BuilderEntregaTOI(BuilderTOI):
|
|
|
296
528
|
|
|
297
529
|
def __init__(self) -> None:
|
|
298
530
|
super().__init__()
|
|
299
|
-
self.relatorio_ocr = CreateRelatorioOcr()
|
|
531
|
+
# self.relatorio_ocr = CreateRelatorioOcr()
|
|
532
|
+
self.relatorio_ocr = ReadTablesOcr()
|
|
300
533
|
self.relatorio_wp = ControllerWhatsApp()
|
|
301
534
|
|
|
302
535
|
self.default_cols_toi = ArrayList[HeaderEntregaTOI].from_list(
|
|
@@ -1257,7 +1490,7 @@ class ControllerTOI(object):
|
|
|
1257
1490
|
return self.get_factory_toi().relatorio_wp
|
|
1258
1491
|
|
|
1259
1492
|
@property
|
|
1260
|
-
def relatorio_ocr(self) ->
|
|
1493
|
+
def relatorio_ocr(self) -> ReadTablesOcr:
|
|
1261
1494
|
return self.get_factory_toi().relatorio_ocr
|
|
1262
1495
|
|
|
1263
1496
|
def get_factory_toi(self) -> BuilderEntregaTOI:
|
|
@@ -1375,11 +1608,9 @@ class ControllerTOI(object):
|
|
|
1375
1608
|
return None
|
|
1376
1609
|
|
|
1377
1610
|
# Filtrar
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
cache_ocr: TableData[HeaderOcr] | None = tb_cache_ocr.search_all(
|
|
1382
|
-
HeaderOcr.HASH_FILE, hash_files.tolist()
|
|
1611
|
+
# Filtrar: imagem + pdf, só as hashs pedidas (cópia, sem mexer no cache)
|
|
1612
|
+
cache_ocr: TableData[HeaderOcr] | None = self.relatorio_ocr.create_from_hashs(
|
|
1613
|
+
hash_files
|
|
1383
1614
|
)
|
|
1384
1615
|
|
|
1385
1616
|
if cache_ocr is None:
|
|
@@ -20,6 +20,7 @@ from table_stream import (
|
|
|
20
20
|
HeaderMetaData,
|
|
21
21
|
ContentFiles,
|
|
22
22
|
EnumDocuments,
|
|
23
|
+
UserFileSystem,
|
|
23
24
|
extract_zip_files,
|
|
24
25
|
load_file_hash,
|
|
25
26
|
# HashMap
|
|
@@ -39,7 +40,9 @@ from table_stream import (
|
|
|
39
40
|
BaseObject,
|
|
40
41
|
)
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# Remover essa dependencia e ocr_stream, mas usar os mesmos caminhos para acesssar
|
|
44
|
+
# as tabelas ocr, apartir da mesma base UserFileSystem().get_user_documents().concat("RFA", True)
|
|
45
|
+
# from ocr_stream import BuilderConfigOcr
|
|
43
46
|
|
|
44
47
|
__version__ = "0.1"
|
|
45
48
|
|
|
@@ -57,6 +60,7 @@ class InvalidTxtFile(ValueError):
|
|
|
57
60
|
def __init__(self, *args: object) -> None:
|
|
58
61
|
super().__init__(*args)
|
|
59
62
|
|
|
63
|
+
|
|
60
64
|
@dataclass
|
|
61
65
|
class ResultChatImport:
|
|
62
66
|
"""Resultado da importação de UM chat."""
|
|
@@ -68,6 +72,7 @@ class ResultChatImport:
|
|
|
68
72
|
hashed: int = 0 # anexos encontrados em disco (hash calculado)
|
|
69
73
|
added: int = 0 # linhas NOVAS gravadas no cache
|
|
70
74
|
|
|
75
|
+
|
|
71
76
|
def move_path(src: PathElement, dest: PathElement) -> bool:
|
|
72
77
|
|
|
73
78
|
try:
|
|
@@ -90,6 +95,97 @@ class ConfigWhatsApp:
|
|
|
90
95
|
work_dir: Directory
|
|
91
96
|
|
|
92
97
|
|
|
98
|
+
# Mesmos nomes usados por ocr_stream.BuilderConfigOcr. Se mudar lá, mude aqui.
|
|
99
|
+
_DIR_ROOT_NAME = "RFA"
|
|
100
|
+
_DIR_TABLES_NAME = "Tabelas"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class BuilderConfigWhatsApp(object):
|
|
104
|
+
"""
|
|
105
|
+
Configuração do WhatsApp SEM depender de ocr_stream/tesseract.
|
|
106
|
+
|
|
107
|
+
Usa a mesma base que o ocr_stream:
|
|
108
|
+
<Documentos>/RFA/ -> dir_base_root
|
|
109
|
+
<Documentos>/RFA/Tabelas -> dir_table (onde ficam as tabelas OCR, TOI etc.)
|
|
110
|
+
<Documentos>/RFA/WhatsApp -> dir_base_work
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
114
|
+
|
|
115
|
+
def __new__(cls, *args, **kwargs):
|
|
116
|
+
if cls._instance is None:
|
|
117
|
+
cls._instance = super(BuilderConfigWhatsApp, cls).__new__(cls)
|
|
118
|
+
return cls._instance
|
|
119
|
+
|
|
120
|
+
def __init__(self) -> None:
|
|
121
|
+
|
|
122
|
+
# Garante que __init__ não será executado mais de uma vez
|
|
123
|
+
if hasattr(self, "_initialized") and self._initialized:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
super().__init__()
|
|
127
|
+
self._initialized: bool = True
|
|
128
|
+
self._root_dir: Directory | None = None
|
|
129
|
+
self._config = None
|
|
130
|
+
|
|
131
|
+
# ---------------------------------------------------------- base RFA
|
|
132
|
+
@property
|
|
133
|
+
def dir_base_root(self) -> Directory:
|
|
134
|
+
"""<Documentos>/RFA (mesma base do ocr_stream)."""
|
|
135
|
+
if self._root_dir is None:
|
|
136
|
+
self._root_dir = (
|
|
137
|
+
UserFileSystem().get_user_documents().concat(_DIR_ROOT_NAME, True)
|
|
138
|
+
)
|
|
139
|
+
return self._root_dir
|
|
140
|
+
|
|
141
|
+
def set_root_dir(self, d: Directory) -> None:
|
|
142
|
+
"""Troca a base (testes / outro PC). Descarta a configuração em cache."""
|
|
143
|
+
d.mkdir()
|
|
144
|
+
self._root_dir = d
|
|
145
|
+
self.reset_config()
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def dir_base_work(self) -> Directory:
|
|
149
|
+
return self.dir_base_root.concat("WhatsApp", True)
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def dir_table(self) -> Directory:
|
|
153
|
+
"""<Documentos>/RFA/Tabelas (mesma pasta das tabelas OCR)."""
|
|
154
|
+
d = self.dir_base_root.concat(_DIR_TABLES_NAME)
|
|
155
|
+
d.mkdir()
|
|
156
|
+
return d
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def dir_chats_organizados(self) -> Directory:
|
|
160
|
+
return self.dir_base_work.concat("ConversasOrganizadas", True)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def dir_chats_compactados(self) -> Directory:
|
|
164
|
+
return self.dir_base_work.concat("ConversasCompactadas", True)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def dir_chats_descompactados(self) -> Directory:
|
|
168
|
+
return self.dir_base_work.concat("ConversasDescompactadas", True)
|
|
169
|
+
|
|
170
|
+
def reset_config(self) -> None:
|
|
171
|
+
self._config = None
|
|
172
|
+
|
|
173
|
+
def builder(self) -> ConfigWhatsApp:
|
|
174
|
+
if self._config is None:
|
|
175
|
+
|
|
176
|
+
self._config = ConfigWhatsApp(
|
|
177
|
+
file_relatorio=self.dir_table.join_file("relatorio-whatsapp.xlsx"),
|
|
178
|
+
file_table=self.dir_table.join_file("tabela-whatsapp.xlsx"),
|
|
179
|
+
dir_unzip_chats=self.dir_chats_descompactados,
|
|
180
|
+
dir_zip_chats=self.dir_chats_compactados,
|
|
181
|
+
dir_export_files=self.dir_chats_organizados,
|
|
182
|
+
work_dir=self.dir_base_work,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return self._config
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
"""
|
|
93
189
|
class BuilderConfigWhatsApp(object):
|
|
94
190
|
|
|
95
191
|
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
@@ -146,6 +242,7 @@ class BuilderConfigWhatsApp(object):
|
|
|
146
242
|
)
|
|
147
243
|
|
|
148
244
|
return self._config
|
|
245
|
+
"""
|
|
149
246
|
|
|
150
247
|
|
|
151
248
|
class HeaderWhatsApp(StrEnum):
|
|
@@ -626,6 +723,7 @@ def get_file_chat_from_dir(d: Directory) -> File | None:
|
|
|
626
723
|
return None
|
|
627
724
|
return txts.get_first()
|
|
628
725
|
|
|
726
|
+
|
|
629
727
|
_NO_HASH = {"", "nan", "NULL_TEXT"}
|
|
630
728
|
|
|
631
729
|
# Uma mensagem é identificada por estes campos + a ocorrência dentro do minuto.
|
|
@@ -683,6 +781,8 @@ def merge_chat_frames(old: pd.DataFrame, new: pd.DataFrame) -> pd.DataFrame:
|
|
|
683
781
|
both = both.drop_duplicates(subset=sub, keep="first")
|
|
684
782
|
both = both.sort_values([c_chat, c_dt, "_ord"], kind="stable")
|
|
685
783
|
return both[cols].reset_index(drop=True)
|
|
784
|
+
|
|
785
|
+
|
|
686
786
|
class ReadWriteWhatsApp(BaseObject):
|
|
687
787
|
|
|
688
788
|
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
@@ -759,6 +859,7 @@ class ReadWriteWhatsApp(BaseObject):
|
|
|
759
859
|
df.to_excel(tmp_file, index=False)
|
|
760
860
|
os.replace(tmp_file, path) # atômico e portável
|
|
761
861
|
|
|
862
|
+
|
|
762
863
|
# ==============================================================#
|
|
763
864
|
# Commandos
|
|
764
865
|
# ==============================================================#
|
|
@@ -1077,6 +1178,7 @@ class ControllerWhatsApp(BaseObject):
|
|
|
1077
1178
|
|
|
1078
1179
|
def merge_table_cache(self, tb: TableWhatsApp) -> None:
|
|
1079
1180
|
self.rw.merge(tb)
|
|
1181
|
+
|
|
1080
1182
|
def increment_chat_file(self, file_txt: File) -> ResultChatImport:
|
|
1081
1183
|
"""
|
|
1082
1184
|
Adicionar dados de um CHAT (.txt) na tabela principal.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/.idea/inspectionProfiles/profiles_settings.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/PATCH_whatsapp_files.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyflutter_app-0.3.13 → pyflutter_app-0.3.14}/converter/page_whatsapp/controller_whatsapp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|