pyflutter-app 0.2.4__tar.gz → 0.2.7__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.2.4 → pyflutter_app-0.2.7}/PKG-INFO +1 -1
- pyflutter_app-0.2.7/cartas/__init__.py +6 -0
- pyflutter_app-0.2.7/cartas/_table_toi.py +151 -0
- pyflutter_app-0.2.7/cartas/common.py +68 -0
- pyflutter_app-0.2.7/cartas/find_toi.py +27 -0
- pyflutter_app-0.2.7/cartas/relatorio.py +350 -0
- pyflutter_app-0.2.7/converter/__init__.py +1 -0
- pyflutter_app-0.2.7/converter/__main__.py +9 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/home_page/_page_edit.py +1 -1
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/home_page/view.py +6 -7
- pyflutter_app-0.2.7/converter/page_forms/Diagnorstico.txt +108 -0
- pyflutter_app-0.2.7/converter/page_forms/__init__.py +5 -0
- pyflutter_app-0.2.7/converter/page_forms/common.py +177 -0
- pyflutter_app-0.2.7/converter/page_forms/config.py +68 -0
- pyflutter_app-0.2.7/converter/page_forms/controller_forms.py +234 -0
- pyflutter_app-0.2.7/converter/page_forms/diagnose_forms.py +64 -0
- pyflutter_app-0.2.7/converter/page_forms/model_forms.py +186 -0
- pyflutter_app-0.2.7/converter/page_forms/service.py +347 -0
- pyflutter_app-0.2.7/converter/page_forms/view_forms.py +104 -0
- pyflutter_app-0.2.4/converter/run.py → pyflutter_app-0.2.7/converter/run_app_convert.py +4 -8
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/util.py +6 -3
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/export_app.sh +14 -10
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/main.py +5 -5
- pyflutter_app-0.2.7/mediator.py +247 -0
- pyflutter_app-0.2.7/mvc/controller.py +60 -0
- pyflutter_app-0.2.7/mvc/model.py +40 -0
- pyflutter_app-0.2.7/mvc/view.py +133 -0
- pyflutter_app-0.2.7/new-export_app.sh +60 -0
- pyflutter_app-0.2.7/ocrlib/__init__.py +54 -0
- pyflutter_app-0.2.7/ocrlib/_common.py +282 -0
- pyflutter_app-0.2.4/ocrlib/pdf_convert.py → pyflutter_app-0.2.7/ocrlib/_pdf_convert.py +126 -87
- pyflutter_app-0.2.7/ocrlib/_table_ocr.py +578 -0
- pyflutter_app-0.2.7/ocrlib/_tesseract.py +356 -0
- pyflutter_app-0.2.7/ocrlib/erros.py +5 -0
- pyflutter_app-0.2.7/ocrlib/relatorio_ocr.py +578 -0
- pyflutter_app-0.2.7/ocrlib/util.py +29 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/file_picker.py +8 -7
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/core.py +1 -1
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyproject.toml +11 -2
- pyflutter_app-0.2.7/pyproject.toml.back +35 -0
- pyflutter_app-0.2.7/table_stream/__init__.py +92 -0
- pyflutter_app-0.2.7/table_stream/erros.py +162 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/hash_map.py +85 -24
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/notifier.py +5 -5
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/pattner/adapter.py +2 -3
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sysfiles.py +221 -180
- pyflutter_app-0.2.7/table_stream/table.py +835 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/thread.py +1 -0
- pyflutter_app-0.2.7/test.py +36 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/uv.lock +1 -1
- pyflutter_app-0.2.7/whatsapp_files/__init__.py +12 -0
- pyflutter_app-0.2.7/whatsapp_files/_table_whatsapp.py +461 -0
- pyflutter_app-0.2.7/whatsapp_files/common.py +120 -0
- pyflutter_app-0.2.7/whatsapp_files/erros.py +11 -0
- pyflutter_app-0.2.7/whatsapp_files/stream_files.py +390 -0
- pyflutter_app-0.2.4/converter/__init__.py +0 -1
- pyflutter_app-0.2.4/converter/__main__.py +0 -9
- pyflutter_app-0.2.4/converter/page_forms/__init__.py +0 -191
- pyflutter_app-0.2.4/converter/page_forms/common.py +0 -155
- pyflutter_app-0.2.4/converter/page_forms/config.py +0 -199
- pyflutter_app-0.2.4/converter/page_forms/controller_forms.py +0 -193
- pyflutter_app-0.2.4/converter/page_forms/model_forms.py +0 -68
- pyflutter_app-0.2.4/converter/page_forms/service.py +0 -207
- pyflutter_app-0.2.4/ocrlib/modules.py +0 -178
- pyflutter_app-0.2.4/ocrlib/recognize.py +0 -1034
- pyflutter_app-0.2.4/ocrlib/util.py +0 -6
- pyflutter_app-0.2.4/table_stream/__init__.py +0 -22
- pyflutter_app-0.2.4/table_stream/erros.py +0 -102
- pyflutter_app-0.2.4/test.py +0 -63
- pyflutter_app-0.2.4/whatsapp_files/file_names.py +0 -678
- pyflutter_app-0.2.4/whatsapp_files/stream_files.py +0 -417
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.gitignore +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/.gitignore +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/misc.xml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/modules.xml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/pyflutter_app.iml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/.idea/vcs.xml +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/README.md +0 -0
- pyflutter_app-0.2.4/converter/home_page/__init__.py → pyflutter_app-0.2.7/cartas/erros.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/assets/forms_config.json +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/config_app.py +0 -0
- {pyflutter_app-0.2.4/converter/whatsapp → pyflutter_app-0.2.7/converter/home_page}/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/home_page/controller.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/home_page/model.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/lib_toi.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/table_widget.py +0 -0
- {pyflutter_app-0.2.4/whatsapp_files → pyflutter_app-0.2.7/converter/whatsapp}/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/converter/whatsapp/model.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/local_build/linux.sh +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/local_build/send.sh +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/ocrlib/export_ocr.sh +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/app.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/navigator.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/scheduler.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/theme.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/buttons.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/container.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/list_builder.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/progress.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/tables.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/pyflutter_ui/ui/widgets.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/pattner/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/__init__.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/check_mod.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/load.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/options.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/parse.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/sheet/save.py +0 -0
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/state.py +1 -1
- {pyflutter_app-0.2.4 → pyflutter_app-0.2.7}/table_stream/workbook.py +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Any, Callable, TypeVar
|
|
4
|
+
|
|
5
|
+
from pandas import DataFrame
|
|
6
|
+
|
|
7
|
+
from table_stream import (
|
|
8
|
+
ArrayList, HashMapDict, TableMap, ColumnV, RowH, PathElement,
|
|
9
|
+
Directory, File, HeaderMetaData, concat_tables, ColumnNotFoundError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from cartas.common import ConfigTOI, BuilderConfigTOI
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HeaderTOI(StrEnum):
|
|
16
|
+
|
|
17
|
+
UC = "uc"
|
|
18
|
+
TOI = "toi"
|
|
19
|
+
DAY = "day"
|
|
20
|
+
MON = "mon"
|
|
21
|
+
YEAR = "year"
|
|
22
|
+
DATE = "data_entrega"
|
|
23
|
+
LOCAL_ENTREGA = "local_entrega"
|
|
24
|
+
TIPO_DOC = "tipo_carta"
|
|
25
|
+
IS_FILE = "arquivo?"
|
|
26
|
+
|
|
27
|
+
FULL_PATH = HeaderMetaData.FILE_PATH.value
|
|
28
|
+
SRC = HeaderMetaData.PARENT_DIR.value
|
|
29
|
+
FILE_EXTENSION = HeaderMetaData.FILE_EXTENSION.value
|
|
30
|
+
|
|
31
|
+
HASH_FILE = HeaderMetaData.HASH_FILE.value
|
|
32
|
+
FILE_BASENAME = "basename"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TipoCarta(StrEnum):
|
|
36
|
+
|
|
37
|
+
CARTA_CALCULO = "CARTA-CALCULO"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class LocalEntregaCarta(StrEnum):
|
|
41
|
+
|
|
42
|
+
OUTRO = "OUTRO"
|
|
43
|
+
MAOS = "MÃOS"
|
|
44
|
+
CORREIOS = "CORREIOS"
|
|
45
|
+
PORTAO = "PORTÃO"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TableTOI(TableMap[HeaderTOI]):
|
|
49
|
+
"""
|
|
50
|
+
Tabela para representar dados de cartas TOI.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, table_map: HashMapDict[HeaderTOI, ColumnV] | None = None) -> None:
|
|
54
|
+
super().__init__(table_map)
|
|
55
|
+
|
|
56
|
+
def rows(self) -> ArrayList[RowH[HeaderTOI, Any]]:
|
|
57
|
+
return super().rows()
|
|
58
|
+
|
|
59
|
+
def select_columns(self, header: list[HeaderTOI]) -> TableTOI:
|
|
60
|
+
final = TableTOI()
|
|
61
|
+
|
|
62
|
+
for h in header:
|
|
63
|
+
if not self.header().contains(h):
|
|
64
|
+
raise ColumnNotFoundError(f"Coluna não encontrada: {h}")
|
|
65
|
+
final.add(self.get(h))
|
|
66
|
+
return final
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def create_map_from_filename(filename: str) -> HashMapDict[HeaderTOI, str]:
|
|
70
|
+
data = HashMapDict()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
values = filename.split(" ")
|
|
74
|
+
if len(values) < 5:
|
|
75
|
+
return data
|
|
76
|
+
except:
|
|
77
|
+
return data
|
|
78
|
+
else:
|
|
79
|
+
for x in values:
|
|
80
|
+
if not x.isnumeric():
|
|
81
|
+
return data
|
|
82
|
+
|
|
83
|
+
data[HeaderTOI.UC] = values[0] # UC
|
|
84
|
+
data[HeaderTOI.TOI] = values[1] # TOI
|
|
85
|
+
data[HeaderTOI.DAY] = values[2] # DIA
|
|
86
|
+
data[HeaderTOI.MON] = values[3] # MÊS
|
|
87
|
+
data[HeaderTOI.YEAR] = values[4] # ANO
|
|
88
|
+
data[HeaderTOI.DATE] = f"{values[2]}/{values[3]}/{values[4]}"
|
|
89
|
+
return data
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def create_table_from_path(cls, path: PathElement) -> TableTOI:
|
|
93
|
+
mp = cls.create_map_from_filename(path.name())
|
|
94
|
+
tb = cls.create_void_table()
|
|
95
|
+
|
|
96
|
+
for h in mp.header():
|
|
97
|
+
e = mp[h]
|
|
98
|
+
tb.get(h).add(e)
|
|
99
|
+
|
|
100
|
+
tb.get(HeaderTOI.FULL_PATH).add(path.absolute())
|
|
101
|
+
tb.get(HeaderTOI.IS_FILE).add("S" if path.is_file() else "N")
|
|
102
|
+
tb.get(HeaderTOI.TIPO_DOC).add(TipoCarta.CARTA_CALCULO.value)
|
|
103
|
+
tb.get(HeaderTOI.LOCAL_ENTREGA).add(LocalEntregaCarta.OUTRO.value)
|
|
104
|
+
tb.get(HeaderTOI.SRC).add(path.parent().absolute())
|
|
105
|
+
tb.get(HeaderTOI.FILE_EXTENSION).add(path.extension())
|
|
106
|
+
return tb
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def create_void_table(cls) -> TableTOI:
|
|
110
|
+
"""
|
|
111
|
+
Cria uma tabela toi vazia com todos os elementos de HeaderTOI
|
|
112
|
+
"""
|
|
113
|
+
cols = [x for x in HeaderTOI]
|
|
114
|
+
columns_v = [ColumnV(c) for c in cols]
|
|
115
|
+
tb = cls()
|
|
116
|
+
|
|
117
|
+
for c in columns_v:
|
|
118
|
+
tb.add(c)
|
|
119
|
+
return tb
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def create_from_files(cls, elements: ArrayList[PathElement]) -> TableTOI:
|
|
123
|
+
|
|
124
|
+
_path_element: PathElement
|
|
125
|
+
list_tables = ArrayList[TableTOI]()
|
|
126
|
+
|
|
127
|
+
for _path_element in elements:
|
|
128
|
+
current_tb = cls.create_table_from_path(_path_element)
|
|
129
|
+
if not current_tb.empty():
|
|
130
|
+
list_tables.append(current_tb)
|
|
131
|
+
|
|
132
|
+
if list_tables.empty():
|
|
133
|
+
return cls()
|
|
134
|
+
|
|
135
|
+
final = concat_tables(list_tables)
|
|
136
|
+
return cls(final._table_dict)
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def create_from_data(cls, data: DataFrame) -> TableTOI:
|
|
140
|
+
columns_df = ArrayList(data.columns.tolist())
|
|
141
|
+
columns_toi = [x for x in HeaderTOI]
|
|
142
|
+
tb = cls()
|
|
143
|
+
|
|
144
|
+
for col_toi in columns_toi:
|
|
145
|
+
if not columns_df.contains(col_toi):
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
elements = data[col_toi].astype("str").values.tolist()
|
|
149
|
+
col_v = ColumnV(col_toi, ArrayList(elements))
|
|
150
|
+
tb.add(col_v)
|
|
151
|
+
return tb
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from table_stream import File, Directory, UserFileSystem
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class ConfigTOI:
|
|
9
|
+
|
|
10
|
+
file_relatorio_toi: File
|
|
11
|
+
file_table_toi: File
|
|
12
|
+
dir_base_toi: Directory
|
|
13
|
+
work_dir: Directory
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BuilderConfigTOI(object):
|
|
17
|
+
|
|
18
|
+
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
19
|
+
|
|
20
|
+
def __new__(cls, *args, **kwargs):
|
|
21
|
+
if cls._instance is None:
|
|
22
|
+
cls._instance = super(BuilderConfigTOI, cls).__new__(cls)
|
|
23
|
+
return cls._instance
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
|
|
27
|
+
# Garante que __init__ não será executado mais de uma vez
|
|
28
|
+
if hasattr(self, "_initialized") and self._initialized:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
super().__init__()
|
|
32
|
+
self._initialized: bool = True
|
|
33
|
+
self._work_dir: Directory = (
|
|
34
|
+
UserFileSystem()
|
|
35
|
+
.get_user_documents()
|
|
36
|
+
.concat("RFA", True)
|
|
37
|
+
.concat("Cartas-TOI")
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self._config = self._create()
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def dir_retorno_de_campo(self) -> Directory:
|
|
44
|
+
return self._work_dir.concat("Retorno-de-campo", True)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def file_relatorio_toi(self) -> File:
|
|
48
|
+
return self._work_dir.concat("Relatorios", True).join_file("Relatorio-tois.xlsx")
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def file_table_toi(self) -> File:
|
|
52
|
+
return self._work_dir.concat("Relatorios", True).join_file("Tabela-tois.xlsx")
|
|
53
|
+
|
|
54
|
+
def _create(self) -> ConfigTOI:
|
|
55
|
+
return ConfigTOI(
|
|
56
|
+
work_dir=self._work_dir,
|
|
57
|
+
dir_base_toi=self.dir_retorno_de_campo,
|
|
58
|
+
file_relatorio_toi=self.file_relatorio_toi,
|
|
59
|
+
file_table_toi=self.file_table_toi,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def set_work_dir(self, d: Directory) -> BuilderConfigTOI:
|
|
63
|
+
self._work_dir = d
|
|
64
|
+
self._config = self._create()
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def builder(self) -> ConfigTOI:
|
|
68
|
+
return self._config
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# ============================================================#
|
|
5
|
+
# Extração de UC/TOI tolerante a falhas de OCR
|
|
6
|
+
# ============================================================#
|
|
7
|
+
#
|
|
8
|
+
# UC: 1305934 TOI: 193166923 ...
|
|
9
|
+
# UC: 1305934 TO! 193166923 ... (TOI virou TO!, ":" some)
|
|
10
|
+
# UC| 1305934 T0I: 193166923 ... (0 no lugar do O)
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
UC_TOI_PATTERN = re.compile(
|
|
14
|
+
r"U[C0]\s*[:;|!]?\s*(\d{4,10})" r".{0,60}?" r"T[O0][I1L!l]?\s*[:;|!]?\s*(\d{4,10})",
|
|
15
|
+
re.IGNORECASE | re.DOTALL,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def extrair_uc_toi(texto: str) -> tuple[str, str] | None:
|
|
20
|
+
"""
|
|
21
|
+
Retorna (uc, toi) a partir do texto OCR completo de um arquivo,
|
|
22
|
+
ou None se o padrão não for encontrado.
|
|
23
|
+
"""
|
|
24
|
+
m = UC_TOI_PATTERN.search(texto)
|
|
25
|
+
if not m:
|
|
26
|
+
return None
|
|
27
|
+
return m.group(1), m.group(2)
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
from ocrlib import (
|
|
2
|
+
TableOcr,
|
|
3
|
+
HeaderOcr,
|
|
4
|
+
DocumentInputStream,
|
|
5
|
+
ReadWriteTableOCR,
|
|
6
|
+
ControllerRelatorioOcr,
|
|
7
|
+
ColumnV,
|
|
8
|
+
ControllerOcr,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from whatsapp_files import TableWhatsApp, HeaderWhatsApp, CreateRelatorioWhatsApp
|
|
12
|
+
|
|
13
|
+
from table_stream import (
|
|
14
|
+
File,
|
|
15
|
+
Directory,
|
|
16
|
+
HashMapDict,
|
|
17
|
+
hashFile,
|
|
18
|
+
ColumnNotFoundError,
|
|
19
|
+
ArrayList,
|
|
20
|
+
PathElement,
|
|
21
|
+
TableMap,
|
|
22
|
+
RowH,
|
|
23
|
+
load_file_hash,
|
|
24
|
+
concat_tables,
|
|
25
|
+
create_table_from_file,
|
|
26
|
+
create_void_table_files,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
import pandas as pd
|
|
30
|
+
from cartas._table_toi import HeaderTOI, TableTOI, TipoCarta, LocalEntregaCarta
|
|
31
|
+
from cartas.find_toi import extrair_uc_toi
|
|
32
|
+
from cartas.common import ConfigTOI, BuilderConfigTOI
|
|
33
|
+
from os import rename as rename_file
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ReadWriteTableTOI(object):
|
|
37
|
+
"""
|
|
38
|
+
Leitura e escrita de dados na tabela OCR.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
_instance = None # Atributo de classe para armazenar a instância singleton
|
|
42
|
+
|
|
43
|
+
def __new__(cls, *args, **kwargs):
|
|
44
|
+
if cls._instance is None:
|
|
45
|
+
cls._instance = super(ReadWriteTableTOI, cls).__new__(cls)
|
|
46
|
+
return cls._instance
|
|
47
|
+
|
|
48
|
+
def __init__(self) -> None:
|
|
49
|
+
|
|
50
|
+
# Garante que __init__ não será executado mais de uma vez
|
|
51
|
+
if hasattr(self, "_initialized") and self._initialized:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
super().__init__()
|
|
55
|
+
self._initialized: bool = True
|
|
56
|
+
self.builder: BuilderConfigTOI = BuilderConfigTOI()
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def file_table_cache(self) -> File:
|
|
60
|
+
return BuilderConfigTOI().builder().file_table_toi
|
|
61
|
+
|
|
62
|
+
def load_table_cache(self) -> TableTOI:
|
|
63
|
+
|
|
64
|
+
f = self.file_table_cache
|
|
65
|
+
|
|
66
|
+
if not f.exists():
|
|
67
|
+
tb = TableTOI()
|
|
68
|
+
tb.add(ColumnV(HeaderTOI.UC))
|
|
69
|
+
tb.add(ColumnV(HeaderTOI.TOI))
|
|
70
|
+
tb.add(ColumnV(HeaderTOI.YEAR))
|
|
71
|
+
tb.add(ColumnV(HeaderTOI.MON))
|
|
72
|
+
tb.add(ColumnV(HeaderTOI.DAY))
|
|
73
|
+
tb.add(ColumnV(HeaderTOI.DATE))
|
|
74
|
+
tb.add(ColumnV(HeaderTOI.LOCAL_ENTREGA))
|
|
75
|
+
tb.add(ColumnV(HeaderTOI.TIPO_DOC))
|
|
76
|
+
tb.add(ColumnV(HeaderTOI.HASH_FILE))
|
|
77
|
+
return tb
|
|
78
|
+
|
|
79
|
+
data = pd.read_excel(f.absolute())
|
|
80
|
+
return TableTOI.create_from_data(data.astype("str"))
|
|
81
|
+
|
|
82
|
+
def save_table_cache(self, tb: TableTOI) -> None:
|
|
83
|
+
data = tb.to_dataframe()
|
|
84
|
+
path = self.file_table_cache.absolute()
|
|
85
|
+
tmp_file = f"{path}-tmp.xlsx"
|
|
86
|
+
|
|
87
|
+
print(f"Salvando ... {path}")
|
|
88
|
+
data.to_excel(tmp_file, index=False)
|
|
89
|
+
rename_file(tmp_file, path)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class CreateRelatorioTOI(object):
|
|
93
|
+
"""
|
|
94
|
+
Cria uma tabela de Cartas TOI apartir dos chats do WhatsApp.
|
|
95
|
+
Conectando os arquivos das conversas aos textos extraídos via OCR.
|
|
96
|
+
|
|
97
|
+
TableWhatsApp() é usado para obter DATA e HORA do envio de um arquivo
|
|
98
|
+
no chat, pois a data de envio no chat e a data de entrega de uma carta em campo.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
MAP_OCR_TO_TOI: HashMapDict[HeaderOcr, HeaderTOI] = HashMapDict(
|
|
102
|
+
{
|
|
103
|
+
HeaderOcr.FILE_PATH: HeaderTOI.FULL_PATH,
|
|
104
|
+
HeaderOcr.FILE_EXTENSION: HeaderTOI.FILE_EXTENSION,
|
|
105
|
+
HeaderOcr.FILE_BASENAME: HeaderTOI.FILE_BASENAME,
|
|
106
|
+
HeaderOcr.HASH_FILE: HeaderTOI.HASH_FILE,
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def __init__(self) -> None:
|
|
111
|
+
|
|
112
|
+
self.relatorio_ocr: ControllerRelatorioOcr = ControllerRelatorioOcr()
|
|
113
|
+
self.relatorio_whatsapp: CreateRelatorioWhatsApp = CreateRelatorioWhatsApp()
|
|
114
|
+
|
|
115
|
+
def load_table_whatsapp(self) -> TableWhatsApp:
|
|
116
|
+
return self.relatorio_whatsapp.load_cache_table()
|
|
117
|
+
|
|
118
|
+
def find_on_cache(self, map: HashMapDict[hashFile, File]) -> TableTOI | None:
|
|
119
|
+
"""
|
|
120
|
+
Gera uma tabela TOI apartir da tabela TOI em cache sem executar
|
|
121
|
+
o filtro de texto em OCR com expressão regular. Apenas gera uma
|
|
122
|
+
tabela consultando a base TOI em cache
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
if map.is_empty():
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
tb_cache = ReadWriteTableTOI().load_table_cache()
|
|
129
|
+
final = TableTOI()
|
|
130
|
+
|
|
131
|
+
header = map.header()
|
|
132
|
+
h: hashFile
|
|
133
|
+
|
|
134
|
+
for h in header:
|
|
135
|
+
f = map.get_value(h)
|
|
136
|
+
|
|
137
|
+
content = tb_cache.search(HeaderTOI.HASH_FILE, h)
|
|
138
|
+
if content.empty():
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
content_row = content.get_row_at(0)
|
|
142
|
+
content_row.set(HeaderTOI.FILE_EXTENSION, f.extension() or ".nan")
|
|
143
|
+
content_row.set(HeaderTOI.FILE_BASENAME, f.basename())
|
|
144
|
+
content_row.set(HeaderTOI.IS_FILE, "S" if f.is_file() else "N")
|
|
145
|
+
content_row.set(HeaderTOI.FULL_PATH, f.absolute())
|
|
146
|
+
content_row.set(HeaderTOI.SRC, f.parent().absolute())
|
|
147
|
+
final.add_row(content_row)
|
|
148
|
+
|
|
149
|
+
return final
|
|
150
|
+
|
|
151
|
+
def create_from_map(
|
|
152
|
+
self, map: HashMapDict[hashFile, File], *, ignore_null: bool = True
|
|
153
|
+
) -> TableTOI | None:
|
|
154
|
+
"""
|
|
155
|
+
I) Usa a HASH MD5 dos arquivos para buscar a data de envio do arquivo na
|
|
156
|
+
tabela whatsapp;
|
|
157
|
+
|
|
158
|
+
II) Gera um relatório OCR [CreateRelatorioExcelOcr()] com os metadados básicos
|
|
159
|
+
de cada arquivo;
|
|
160
|
+
|
|
161
|
+
III) Obtem a UC e TOI de cada arquivo.
|
|
162
|
+
|
|
163
|
+
IV) Retorna uma tabela apartir dos dados gerados.
|
|
164
|
+
|
|
165
|
+
Observação:
|
|
166
|
+
Se o parametro ignore_null for True as linhas que contém UC e TOI inválidos
|
|
167
|
+
serão ignorados.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
if map.is_empty():
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
# Dados OCR completos de cada arquivo do parâmetro map, esses arquivos
|
|
174
|
+
# serão comparados com a tabela whatsapp para filtar a data de envio no chat.
|
|
175
|
+
table_ocr: TableOcr = self.relatorio_ocr.create_all(map)
|
|
176
|
+
table_whatsapp: TableWhatsApp = self.load_table_whatsapp()
|
|
177
|
+
|
|
178
|
+
if table_ocr is None:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
if table_ocr.is_empty():
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
# Cada arquivo gera/extrai a sua própria tabela, ao final essa
|
|
185
|
+
# lista de tabelas serão concatenadas em uma tabela única.
|
|
186
|
+
list_tables_toi: ArrayList[TableTOI] = ArrayList()
|
|
187
|
+
keys = map.header()
|
|
188
|
+
total = keys.size()
|
|
189
|
+
|
|
190
|
+
print(f"Criando relatório TOI de {total} arquivos")
|
|
191
|
+
k: hashFile
|
|
192
|
+
f: File
|
|
193
|
+
|
|
194
|
+
for n, k in enumerate(keys):
|
|
195
|
+
f = map.get_value(k)
|
|
196
|
+
|
|
197
|
+
print(f"[FILTRANDO] [{n+1}/{total}] {f.basename()}")
|
|
198
|
+
|
|
199
|
+
current_table_ocr: TableOcr = table_ocr.search(HeaderOcr.HASH_FILE, k)
|
|
200
|
+
text_ocr: str = None
|
|
201
|
+
|
|
202
|
+
if not current_table_ocr.is_empty():
|
|
203
|
+
text_ocr = current_table_ocr.to_string(HeaderOcr.TEXT)
|
|
204
|
+
|
|
205
|
+
txt_uc_toi: tuple | None = (
|
|
206
|
+
extrair_uc_toi(text_ocr) if text_ocr is not None else None
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if txt_uc_toi is None:
|
|
210
|
+
if ignore_null:
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
uc = TableTOI.NULL_STRING
|
|
214
|
+
toi = TableTOI.NULL_STRING
|
|
215
|
+
year = TableTOI.NULL_STRING
|
|
216
|
+
mon = TableTOI.NULL_STRING
|
|
217
|
+
day = TableTOI.NULL_STRING
|
|
218
|
+
dt: str = TableTOI.NULL_STRING
|
|
219
|
+
|
|
220
|
+
if txt_uc_toi is not None:
|
|
221
|
+
uc = txt_uc_toi[0]
|
|
222
|
+
toi = txt_uc_toi[1]
|
|
223
|
+
|
|
224
|
+
current_table_whatsapp = table_whatsapp.search(HeaderWhatsApp.hash_md5, k)
|
|
225
|
+
|
|
226
|
+
if not current_table_whatsapp.empty():
|
|
227
|
+
dt = current_table_whatsapp.get_row_at(0).get_date().get_first()
|
|
228
|
+
day = dt.split(" ")[0]
|
|
229
|
+
mon = dt.split(" ")[1]
|
|
230
|
+
year = dt.split(" ")[2]
|
|
231
|
+
|
|
232
|
+
tb_toi = TableTOI()
|
|
233
|
+
|
|
234
|
+
tb_toi.add(ColumnV(HeaderTOI.UC, ArrayList([uc])))
|
|
235
|
+
tb_toi.add(ColumnV(HeaderTOI.TOI, ArrayList([toi])))
|
|
236
|
+
tb_toi.add(ColumnV(HeaderTOI.YEAR, ArrayList([year])))
|
|
237
|
+
tb_toi.add(ColumnV(HeaderTOI.MON, ArrayList([mon])))
|
|
238
|
+
tb_toi.add(ColumnV(HeaderTOI.DAY, ArrayList([day])))
|
|
239
|
+
tb_toi.add(ColumnV(HeaderTOI.DATE, ArrayList([dt])))
|
|
240
|
+
|
|
241
|
+
tb_toi.add(
|
|
242
|
+
ColumnV(HeaderTOI.FILE_EXTENSION, ArrayList([f.extension() or ".nan"]))
|
|
243
|
+
)
|
|
244
|
+
tb_toi.add(ColumnV(HeaderTOI.FILE_BASENAME, ArrayList([f.basename()])))
|
|
245
|
+
tb_toi.add(ColumnV(HeaderTOI.HASH_FILE, ArrayList([k])))
|
|
246
|
+
tb_toi.add(ColumnV(HeaderTOI.FULL_PATH, ArrayList([f.absolute()])))
|
|
247
|
+
tb_toi.add(
|
|
248
|
+
ColumnV(HeaderTOI.IS_FILE, ArrayList(["S" if f.is_file() else "N"]))
|
|
249
|
+
)
|
|
250
|
+
tb_toi.add(ColumnV(HeaderTOI.SRC, ArrayList([f.dirname()])))
|
|
251
|
+
|
|
252
|
+
tb_toi.add(
|
|
253
|
+
ColumnV(HeaderTOI.TIPO_DOC, ArrayList([TipoCarta.CARTA_CALCULO]))
|
|
254
|
+
)
|
|
255
|
+
tb_toi.add(
|
|
256
|
+
ColumnV(HeaderTOI.LOCAL_ENTREGA, ArrayList([LocalEntregaCarta.OUTRO]))
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
list_tables_toi.add(tb_toi)
|
|
260
|
+
|
|
261
|
+
if list_tables_toi.empty():
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
final = TableTOI.create_void_table()
|
|
265
|
+
list_tables_toi.for_each(lambda t: final.merge(t))
|
|
266
|
+
return final
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def filter_uc_toi(hf: hashFile, table_ocr_file: TableOcr) -> tuple[str, str] | None:
|
|
270
|
+
"""
|
|
271
|
+
Filtra UC e TOI de uma carta apartir do texto OCR extraído.
|
|
272
|
+
"""
|
|
273
|
+
tb_current_hash = table_ocr_file.search(HeaderOcr.HASH_FILE, hf)
|
|
274
|
+
if tb_current_hash.is_empty():
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
values = tb_current_hash.get(HeaderOcr.TEXT)._elements
|
|
278
|
+
|
|
279
|
+
for v in values:
|
|
280
|
+
txt = extrair_uc_toi(v)
|
|
281
|
+
if txt is not None:
|
|
282
|
+
return txt
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def load_files_toi(files: ArrayList[File]) -> TableTOI | None:
|
|
287
|
+
"""
|
|
288
|
+
Analisa arquivos TOI apartir da HASH MD5, compara o texto na tabela OCR e inclui
|
|
289
|
+
UC e TOI na tabela TOI principal e ao final retorna os dados no formato de tabela TOI.
|
|
290
|
+
"""
|
|
291
|
+
if files.empty():
|
|
292
|
+
print(f"[DEBUG]: load_files_toi() lista de arquivos vazia!")
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
map = HashMapDict[hashFile, File]()
|
|
296
|
+
files.for_each(lambda f: map.set_value(load_file_hash(f), f))
|
|
297
|
+
|
|
298
|
+
content_table = CreateRelatorioTOI().create_from_map(map)
|
|
299
|
+
if content_table is None:
|
|
300
|
+
return None
|
|
301
|
+
if content_table.empty():
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
# Selecionar apenas algumas colunas para salvar em cache.
|
|
305
|
+
content_tb_merge = content_table.select_columns(
|
|
306
|
+
[
|
|
307
|
+
HeaderTOI.UC,
|
|
308
|
+
HeaderTOI.TOI,
|
|
309
|
+
HeaderTOI.YEAR,
|
|
310
|
+
HeaderTOI.MON,
|
|
311
|
+
HeaderTOI.DAY,
|
|
312
|
+
HeaderTOI.DATE,
|
|
313
|
+
HeaderTOI.LOCAL_ENTREGA,
|
|
314
|
+
HeaderTOI.TIPO_DOC,
|
|
315
|
+
HeaderTOI.HASH_FILE,
|
|
316
|
+
]
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Filtar UCs validas
|
|
320
|
+
df_merge = content_tb_merge.to_dataframe().astype(str)
|
|
321
|
+
df_merge = df_merge[df_merge[HeaderTOI.UC] != TableTOI.NULL_STRING]
|
|
322
|
+
df_merge.drop_duplicates(subset=[HeaderTOI.HASH_FILE], inplace=True)
|
|
323
|
+
|
|
324
|
+
rw = ReadWriteTableTOI()
|
|
325
|
+
tb_cache_toi: TableTOI = rw.load_table_cache()
|
|
326
|
+
df_cache_toi = (
|
|
327
|
+
pd.DataFrame()
|
|
328
|
+
if tb_cache_toi.empty()
|
|
329
|
+
else tb_cache_toi.to_dataframe().astype(str)
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
df_cache = (
|
|
333
|
+
pd.concat([df_cache_toi, df_merge.astype(str)])
|
|
334
|
+
if not df_cache_toi.empty
|
|
335
|
+
else df_merge.astype(str)
|
|
336
|
+
)
|
|
337
|
+
df_cache.drop_duplicates(subset=[HeaderTOI.HASH_FILE]).astype(str)
|
|
338
|
+
|
|
339
|
+
rw.save_table_cache(TableTOI.create_from_data(df_cache))
|
|
340
|
+
return content_table
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class ControllerToi(object):
|
|
344
|
+
|
|
345
|
+
def __init__(self) -> None:
|
|
346
|
+
self.conf: ConfigTOI = BuilderConfigTOI().builder()
|
|
347
|
+
self.ocr: ControllerOcr = ControllerOcr()
|
|
348
|
+
|
|
349
|
+
def load_files_toi(self):
|
|
350
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .run_app_convert import main as run_ui_app_convert
|
|
@@ -207,13 +207,12 @@ class _HomePageState(StatelessWidget):
|
|
|
207
207
|
Seta o DataFrame() da página atual como propriedade do ControllerForms()
|
|
208
208
|
antes de carregar a página.
|
|
209
209
|
"""
|
|
210
|
-
|
|
211
|
-
forms_controller: ControllerForms = context.read(ControllerForms)
|
|
212
|
-
forms_controller.set_data(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return
|
|
210
|
+
#df = controller.data_notifier.value
|
|
211
|
+
#forms_controller: ControllerForms = context.read(ControllerForms)
|
|
212
|
+
#forms_controller.set_data(df)
|
|
213
|
+
#if not forms_controller.forms_state.data_is_valid():
|
|
214
|
+
# messagebox.showerror("Erro", "Tabela TOI inválida")
|
|
215
|
+
# return
|
|
217
216
|
|
|
218
217
|
context.navigator.push(PageRespondsForms())
|
|
219
218
|
|