pyflutter-app 0.2.3__tar.gz → 0.2.5__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.
Files changed (111) hide show
  1. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/pyflutter_app.iml +1 -1
  2. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/PKG-INFO +4 -1
  3. pyflutter_app-0.2.5/cartas/__init__.py +6 -0
  4. pyflutter_app-0.2.5/cartas/common.py +68 -0
  5. pyflutter_app-0.2.5/cartas/find_toi.py +27 -0
  6. pyflutter_app-0.2.5/cartas/relatorio.py +290 -0
  7. pyflutter_app-0.2.5/cartas/table_toi.py +151 -0
  8. pyflutter_app-0.2.5/converter/__init__.py +1 -0
  9. pyflutter_app-0.2.5/converter/__main__.py +9 -0
  10. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/home_page/_page_edit.py +1 -1
  11. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/home_page/view.py +6 -7
  12. pyflutter_app-0.2.5/converter/page_forms/Diagnorstico.txt +108 -0
  13. pyflutter_app-0.2.5/converter/page_forms/__init__.py +5 -0
  14. pyflutter_app-0.2.5/converter/page_forms/common.py +173 -0
  15. pyflutter_app-0.2.5/converter/page_forms/config.py +68 -0
  16. pyflutter_app-0.2.5/converter/page_forms/controller_forms.py +224 -0
  17. pyflutter_app-0.2.5/converter/page_forms/diagnose_forms.py +64 -0
  18. pyflutter_app-0.2.5/converter/page_forms/model_forms.py +183 -0
  19. pyflutter_app-0.2.5/converter/page_forms/service.py +255 -0
  20. pyflutter_app-0.2.5/converter/page_forms/view_forms.py +104 -0
  21. pyflutter_app-0.2.3/converter/run.py → pyflutter_app-0.2.5/converter/run_app_convert.py +4 -8
  22. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/util.py +6 -3
  23. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/export_app.sh +14 -10
  24. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/main.py +5 -5
  25. pyflutter_app-0.2.5/mediator.py +247 -0
  26. pyflutter_app-0.2.5/mvc/controller.py +60 -0
  27. pyflutter_app-0.2.5/mvc/model.py +40 -0
  28. pyflutter_app-0.2.5/mvc/view.py +133 -0
  29. pyflutter_app-0.2.5/new-export_app.sh +60 -0
  30. pyflutter_app-0.2.5/ocrlib/__init__.py +24 -0
  31. pyflutter_app-0.2.5/ocrlib/_common.py +288 -0
  32. pyflutter_app-0.2.3/ocrlib/pdf_convert.py → pyflutter_app-0.2.5/ocrlib/_pdf_convert.py +99 -64
  33. pyflutter_app-0.2.5/ocrlib/_table_ocr.py +526 -0
  34. pyflutter_app-0.2.5/ocrlib/_tesseract.py +338 -0
  35. pyflutter_app-0.2.5/ocrlib/erros.py +0 -0
  36. pyflutter_app-0.2.5/ocrlib/ocr.py +445 -0
  37. pyflutter_app-0.2.5/ocrlib/util.py +12 -0
  38. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/file_picker.py +8 -7
  39. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/core.py +1 -1
  40. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyproject.toml +4 -1
  41. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/__init__.py +13 -2
  42. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/erros.py +14 -1
  43. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/hash_map.py +233 -2
  44. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/notifier.py +5 -5
  45. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/pattner/adapter.py +2 -3
  46. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sysfiles.py +65 -47
  47. pyflutter_app-0.2.5/table_stream/table.py +573 -0
  48. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/thread.py +1 -0
  49. pyflutter_app-0.2.5/test.py +92 -0
  50. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/uv.lock +220 -5
  51. pyflutter_app-0.2.5/whatsapp_files/__init__.py +12 -0
  52. pyflutter_app-0.2.5/whatsapp_files/_table_whatsapp.py +461 -0
  53. pyflutter_app-0.2.5/whatsapp_files/common.py +120 -0
  54. pyflutter_app-0.2.5/whatsapp_files/erros.py +11 -0
  55. pyflutter_app-0.2.5/whatsapp_files/stream_files.py +390 -0
  56. pyflutter_app-0.2.3/converter/__init__.py +0 -1
  57. pyflutter_app-0.2.3/converter/__main__.py +0 -9
  58. pyflutter_app-0.2.3/converter/page_forms/__init__.py +0 -191
  59. pyflutter_app-0.2.3/converter/page_forms/common.py +0 -155
  60. pyflutter_app-0.2.3/converter/page_forms/config.py +0 -199
  61. pyflutter_app-0.2.3/converter/page_forms/controller_forms.py +0 -193
  62. pyflutter_app-0.2.3/converter/page_forms/model_forms.py +0 -68
  63. pyflutter_app-0.2.3/converter/page_forms/service.py +0 -207
  64. pyflutter_app-0.2.3/ocrlib/modules.py +0 -178
  65. pyflutter_app-0.2.3/ocrlib/recognize.py +0 -1034
  66. pyflutter_app-0.2.3/ocrlib/util.py +0 -6
  67. pyflutter_app-0.2.3/test.py +0 -63
  68. pyflutter_app-0.2.3/whatsapp_files/file_names.py +0 -678
  69. pyflutter_app-0.2.3/whatsapp_files/stream_files.py +0 -417
  70. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.gitignore +0 -0
  71. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/.gitignore +0 -0
  72. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  73. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  74. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/misc.xml +0 -0
  75. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/modules.xml +0 -0
  76. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/.idea/vcs.xml +0 -0
  77. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/README.md +0 -0
  78. pyflutter_app-0.2.3/converter/home_page/__init__.py → pyflutter_app-0.2.5/cartas/erros.py +0 -0
  79. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/assets/forms_config.json +0 -0
  80. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/config_app.py +0 -0
  81. {pyflutter_app-0.2.3/converter/whatsapp → pyflutter_app-0.2.5/converter/home_page}/__init__.py +0 -0
  82. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/home_page/controller.py +0 -0
  83. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/home_page/model.py +0 -0
  84. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/lib_toi.py +0 -0
  85. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/table_widget.py +0 -0
  86. {pyflutter_app-0.2.3/whatsapp_files → pyflutter_app-0.2.5/converter/whatsapp}/__init__.py +0 -0
  87. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/converter/whatsapp/model.py +0 -0
  88. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/local_build/linux.sh +0 -0
  89. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/local_build/send.sh +0 -0
  90. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/ocrlib/export_ocr.sh +0 -0
  91. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/__init__.py +0 -0
  92. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/app.py +0 -0
  93. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/navigator.py +0 -0
  94. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/scheduler.py +0 -0
  95. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/theme.py +0 -0
  96. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/__init__.py +0 -0
  97. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/buttons.py +0 -0
  98. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/container.py +0 -0
  99. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/list_builder.py +0 -0
  100. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/progress.py +0 -0
  101. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/tables.py +0 -0
  102. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/pyflutter_ui/ui/widgets.py +0 -0
  103. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/pattner/__init__.py +0 -0
  104. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/__init__.py +0 -0
  105. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/check_mod.py +0 -0
  106. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/load.py +0 -0
  107. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/options.py +0 -0
  108. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/parse.py +0 -0
  109. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/sheet/save.py +0 -0
  110. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/state.py +1 -1
  111. {pyflutter_app-0.2.3 → pyflutter_app-0.2.5}/table_stream/workbook.py +0 -0
@@ -4,7 +4,7 @@
4
4
  <content url="file://$MODULE_DIR$">
5
5
  <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
6
  </content>
7
- <orderEntry type="jdk" jdkName="~/GIT/pyflutter/.venv" jdkType="Python SDK" />
7
+ <orderEntry type="jdk" jdkName="uv (pyflutter)" jdkType="Python SDK" />
8
8
  <orderEntry type="sourceFolder" forTests="false" />
9
9
  </component>
10
10
  <component name="PyDocumentationSettings">
@@ -1,12 +1,15 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyflutter_app
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Bibliotecas Python para conversão de documentos, interface e processamento de tabelas.
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: opencv-python>=5.0.0.93
7
7
  Requires-Dist: openpyxl>=3.1.5
8
8
  Requires-Dist: pandas-stubs~=3.0.3
9
9
  Requires-Dist: pandas>=3.0.0
10
+ Requires-Dist: playwright>=1.63.0
11
+ Requires-Dist: pymupdf>=1.28.2
12
+ Requires-Dist: pytesseract>=0.3.13
10
13
  Description-Content-Type: text/markdown
11
14
 
12
15
  # pyflutter
@@ -0,0 +1,6 @@
1
+ from .find_toi import extrair_uc_toi
2
+ from .table_toi import HeaderTOI, TableTOI, LocalEntregaCarta, TipoCarta
3
+ from .common import BuilderConfigTOI, ConfigTOI
4
+ from .relatorio import (
5
+ CreateRelatorioTOI, TableTOI, ReadWriteTableTOI, load_files_toi
6
+ )
@@ -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,290 @@
1
+ from ocrlib import (
2
+ TableOcr, HeaderOcr, DocumentInputStream, ReadWriteTableOCR,
3
+ CreateRelatorioExcelOcr, ColumnV,
4
+ )
5
+
6
+ from whatsapp_files import TableWhatsApp, HeaderWhatsApp, CreateRelatorioWhatsApp
7
+
8
+ from table_stream import (
9
+ File, Directory, HashMapDict, hashFile, ColumnNotFoundError,
10
+ ArrayList, PathElement, TableElements, RowH, load_file_hash,
11
+ concat_tables, create_table_from_file, create_void_table_files, BaseObjetct
12
+ )
13
+
14
+ import pandas as pd
15
+ from cartas.table_toi import HeaderTOI, TableTOI, TipoCarta, LocalEntregaCarta
16
+ from cartas.find_toi import extrair_uc_toi
17
+ from cartas.common import ConfigTOI, BuilderConfigTOI
18
+ from os import rename as rename_file
19
+
20
+
21
+ class ReadWriteTableTOI(object):
22
+ """
23
+ Leitura e escrita de dados na tabela OCR.
24
+ """
25
+
26
+ _instance = None # Atributo de classe para armazenar a instância singleton
27
+
28
+ def __new__(cls, *args, **kwargs):
29
+ if cls._instance is None:
30
+ cls._instance = super(ReadWriteTableTOI, cls).__new__(cls)
31
+ return cls._instance
32
+
33
+ def __init__(self) -> None:
34
+
35
+ # Garante que __init__ não será executado mais de uma vez
36
+ if hasattr(self, "_initialized") and self._initialized:
37
+ return
38
+
39
+ super().__init__()
40
+ self._initialized: bool = True
41
+ self.builder: BuilderConfigTOI = BuilderConfigTOI()
42
+
43
+ @property
44
+ def file_table_cache(self) -> File:
45
+ return BuilderConfigTOI().builder().file_table_toi
46
+
47
+ def load_table_cache(self) -> TableTOI:
48
+
49
+ f = self.file_table_cache
50
+
51
+ if not f.exists():
52
+ tb = TableTOI()
53
+ tb.add(ColumnV(HeaderTOI.UC))
54
+ tb.add(ColumnV(HeaderTOI.TOI))
55
+ tb.add(ColumnV(HeaderTOI.YEAR))
56
+ tb.add(ColumnV(HeaderTOI.MON))
57
+ tb.add(ColumnV(HeaderTOI.DAY))
58
+ tb.add(ColumnV(HeaderTOI.DATE))
59
+ tb.add(ColumnV(HeaderTOI.LOCAL_ENTREGA))
60
+ tb.add(ColumnV(HeaderTOI.TIPO_DOC))
61
+ tb.add(ColumnV(HeaderTOI.HASH_FILE))
62
+ return tb
63
+
64
+ data = pd.read_excel(f.absolute())
65
+ return TableTOI.create_from_data(data.astype("str"))
66
+
67
+ def save_table_cache(self, tb: TableTOI) -> None:
68
+ data = tb.to_dataframe()
69
+ path = self.file_table_cache.absolute()
70
+ tmp_file = f"{path}-tmp.xlsx"
71
+
72
+ print(f"Salvando ... {path}")
73
+ data.to_excel(tmp_file, index=False)
74
+ rename_file(tmp_file, path)
75
+
76
+
77
+ class CreateRelatorioTOI(object):
78
+ """
79
+ Cria uma tabela de Cartas TOI apartir dos chats do WhatsApp.
80
+ Conectando os arquivos das conversas aos textos extraídos via OCR.
81
+
82
+ TableWhatsApp() é usado para obter DATA e HORA do envio de um arquivo
83
+ no chat, pois a data de envio no chat e a data de entrega de uma carta em campo.
84
+ """
85
+
86
+ MAP_OCR_TO_TOI: HashMapDict[HeaderOcr, HeaderTOI] = HashMapDict(
87
+ {
88
+ HeaderOcr.FILE_PATH: HeaderTOI.FULL_PATH,
89
+ HeaderOcr.FILE_EXTENSION: HeaderTOI.FILE_EXTENSION,
90
+ HeaderOcr.FILE_BASENAME: HeaderTOI.FILE_BASENAME,
91
+ HeaderOcr.HASH_FILE: HeaderTOI.HASH_FILE,
92
+ }
93
+ )
94
+
95
+ def __init__(self) -> None:
96
+
97
+ self.relatorio_ocr: CreateRelatorioExcelOcr = CreateRelatorioExcelOcr()
98
+ self.relatorio_whatsapp: CreateRelatorioWhatsApp = CreateRelatorioWhatsApp()
99
+
100
+ def load_table_whatsapp(self) -> TableWhatsApp:
101
+ return self.relatorio_whatsapp.load_cache_table()
102
+
103
+ def find_on_cache(self, map: HashMapDict[hashFile, File]) -> TableTOI | None:
104
+ """
105
+ Gera uma tabela TOI apartir da tabela TOI em cache sem executar
106
+ o filtro de texto em OCR com expressão regular.
107
+ """
108
+
109
+ if map.is_empty():
110
+ return None
111
+
112
+ tb_cache = ReadWriteTableTOI().load_table_cache()
113
+ final = TableTOI()
114
+
115
+ header = map.header()
116
+ h: hashFile
117
+
118
+ for h in header:
119
+ f = map.get_value(h)
120
+
121
+ content = tb_cache.seach(HeaderTOI.HASH_FILE, h)
122
+ if content.empty():
123
+ continue
124
+
125
+ content_row = content.get_row_at(0)
126
+ content_row.set(HeaderTOI.FILE_EXTENSION, f.extension() or '.nan')
127
+ content_row.set(HeaderTOI.FILE_BASENAME, f.basename())
128
+ content_row.set(HeaderTOI.IS_FILE, "S" if f.is_file() else "N")
129
+ content_row.set(HeaderTOI.FULL_PATH, f.absolute())
130
+ content_row.set(HeaderTOI.SRC, f.parent().absolute())
131
+ final.add_row(content_row)
132
+
133
+ return final
134
+
135
+ def create_from_map(self, map: HashMapDict[hashFile, File], *, ignore_null: bool = True) -> TableTOI | None:
136
+ """
137
+ I) Usa a HASH MD5 dos arquivos para buscar a data de envio do arquivo na
138
+ tabela whatsapp;
139
+
140
+ II) Gera um relatório OCR [CreateRelatorioExcelOcr()] com os metadados básicos
141
+ de cada arquivo;
142
+
143
+ III) Obtem a UC e TOI de cada arquivo.
144
+ """
145
+
146
+ if map.is_empty():
147
+ return None
148
+
149
+ # Dados OCR completos de cada arquivo do parâmetro map, esses arquivos
150
+ # serão comparados com a tabela whatsapp para filtar a data de envio no chat.
151
+ table_ocr: TableOcr = self.relatorio_ocr.create_all(map)
152
+ table_whatsapp: TableWhatsApp = self.load_table_whatsapp()
153
+
154
+ if table_ocr is None:
155
+ return None
156
+
157
+ if table_ocr.empty():
158
+ return None
159
+
160
+ # Cada arquivo gera/extrai a sua própria tabela, ao final essa
161
+ # lista de tabelas serão concatenadas em uma tabela única.
162
+ list_tables_toi: ArrayList[TableTOI] = ArrayList()
163
+ keys = map.header()
164
+ total = keys.size()
165
+
166
+ print(f'Criando relatório TOI de {total} arquivos')
167
+ k: hashFile
168
+ f: File
169
+
170
+ for n, k in enumerate(keys):
171
+ f = map.get_value(k)
172
+
173
+ print(f"[FILTRANDO] [{n+1}/{total}] {f.basename()}")
174
+
175
+ current_table_ocr: TableOcr = table_ocr.seach(HeaderOcr.HASH_FILE, k)
176
+ text_ocr: str = None
177
+
178
+ if not current_table_ocr.empty():
179
+ text_ocr = current_table_ocr.to_string(HeaderOcr.TEXT)
180
+
181
+ txt_uc_toi: tuple | None = extrair_uc_toi(text_ocr) if text_ocr is not None else None
182
+
183
+ if txt_uc_toi is None:
184
+ if ignore_null:
185
+ continue
186
+
187
+ uc = TableTOI.NULL_STRING
188
+ toi = TableTOI.NULL_STRING
189
+ year = TableTOI.NULL_STRING
190
+ mon = TableTOI.NULL_STRING
191
+ day = TableTOI.NULL_STRING
192
+ dt: str = TableTOI.NULL_STRING
193
+
194
+ if txt_uc_toi is not None:
195
+ uc = txt_uc_toi[0]
196
+ toi = txt_uc_toi[1]
197
+
198
+ current_table_whatsapp = table_whatsapp.seach(HeaderWhatsApp.hash_md5, k)
199
+
200
+ if not current_table_whatsapp.empty():
201
+ dt = current_table_whatsapp.get_row_at(0).get_date().get_first()
202
+ day = dt.split(' ')[0]
203
+ mon = dt.split(' ')[1]
204
+ year = dt.split(' ')[2]
205
+
206
+ tb_toi = TableTOI()
207
+
208
+ tb_toi.add(ColumnV(HeaderTOI.UC, ArrayList([uc])))
209
+ tb_toi.add(ColumnV(HeaderTOI.TOI, ArrayList([toi])))
210
+ tb_toi.add(ColumnV(HeaderTOI.YEAR, ArrayList([year])))
211
+ tb_toi.add(ColumnV(HeaderTOI.MON, ArrayList([mon])))
212
+ tb_toi.add(ColumnV(HeaderTOI.DAY, ArrayList([day])))
213
+ tb_toi.add(ColumnV(HeaderTOI.DATE, ArrayList([dt])))
214
+
215
+ tb_toi.add(ColumnV(HeaderTOI.FILE_EXTENSION, ArrayList([f.extension() or '.nan'])))
216
+ tb_toi.add(ColumnV(HeaderTOI.FILE_BASENAME, ArrayList([f.basename()])))
217
+ tb_toi.add(ColumnV(HeaderTOI.HASH_FILE, ArrayList([k])))
218
+ tb_toi.add(ColumnV(HeaderTOI.FULL_PATH, ArrayList([f.absolute()])))
219
+ tb_toi.add(ColumnV(HeaderTOI.IS_FILE, ArrayList(['S' if f.is_file() else "N"])))
220
+ tb_toi.add(ColumnV(HeaderTOI.SRC, ArrayList([f.dirname()])))
221
+
222
+ tb_toi.add(ColumnV(HeaderTOI.TIPO_DOC, ArrayList([TipoCarta.CARTA_CALCULO])))
223
+ tb_toi.add(ColumnV(HeaderTOI.LOCAL_ENTREGA, ArrayList([LocalEntregaCarta.OUTRO])))
224
+
225
+ list_tables_toi.add(tb_toi)
226
+
227
+ if list_tables_toi.empty():
228
+ return None
229
+
230
+ final = TableTOI.create_void_table()
231
+ list_tables_toi.for_each(lambda t: final.merge(t))
232
+ return final
233
+
234
+ @staticmethod
235
+ def filter_uc_toi(hf: hashFile, table_ocr_file: TableOcr) -> tuple[str, str] | None:
236
+ """
237
+ Filtra UC e TOI de uma carta apartir do texto OCR extraído.
238
+ """
239
+ tb_current_hash = table_ocr_file.seach(HeaderOcr.HASH_FILE, hf)
240
+ if tb_current_hash.empty():
241
+ return None
242
+
243
+ values = tb_current_hash.get(HeaderOcr.TEXT).elements
244
+
245
+ for v in values:
246
+ txt = extrair_uc_toi(v)
247
+ if txt is not None:
248
+ return txt
249
+ return None
250
+
251
+
252
+ def load_files_toi(files: ArrayList[File]) -> TableTOI | None:
253
+ """
254
+ Analisa arquivos TOI apartir da HASH MD5, compara o texto na tabela OCR e inclui
255
+ UC e TOI na tabela TOI principal e ao final retorna os dados no formato de tabela TOI.
256
+ """
257
+ if files.empty():
258
+ print(f"[DEBUG]: load_files_toi() lista de arquivos vazia!")
259
+ return None
260
+
261
+ map = HashMapDict[hashFile, File]()
262
+ files.for_each(lambda f: map.set_value(load_file_hash(f), f))
263
+
264
+ content_table = CreateRelatorioTOI().create_from_map(map)
265
+ if content_table is None:
266
+ return None
267
+ if content_table.empty():
268
+ return None
269
+
270
+ # Selecionar apenas algumas colunas para salvar em cache.
271
+ content_tb_merge = content_table.select_columns([
272
+ HeaderTOI.UC, HeaderTOI.TOI, HeaderTOI.YEAR, HeaderTOI.MON,
273
+ HeaderTOI.DAY, HeaderTOI.DATE, HeaderTOI.LOCAL_ENTREGA,
274
+ HeaderTOI.TIPO_DOC, HeaderTOI.HASH_FILE,
275
+ ])
276
+
277
+ # Filtar UCs validas
278
+ df_merge = content_tb_merge.to_dataframe().astype(str)
279
+ df_merge = df_merge[df_merge[HeaderTOI.UC] != TableTOI.NULL_STRING]
280
+ df_merge.drop_duplicates(subset=[HeaderTOI.HASH_FILE], inplace=True)
281
+
282
+ rw = ReadWriteTableTOI()
283
+ tb_cache_toi: TableTOI = rw.load_table_cache()
284
+ df_cache_toi = pd.DataFrame() if tb_cache_toi.empty() else tb_cache_toi.to_dataframe().astype(str)
285
+
286
+ df_cache = pd.concat([df_cache_toi, df_merge.astype(str)]) if not df_cache_toi.empty else df_merge.astype(str)
287
+ df_cache.drop_duplicates(subset=[HeaderTOI.HASH_FILE]).astype(str)
288
+
289
+ rw.save_table_cache(TableTOI.create_from_data(df_cache))
290
+ return content_table
@@ -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, TableElements, 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.arquivo.value
28
+ SRC = HeaderMetaData.pasta_pai.value
29
+ FILE_EXTENSION = HeaderMetaData.extensao_arquivo.value
30
+
31
+ HASH_FILE = HeaderMetaData.md5.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(TableElements[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.tb_map)
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 @@
1
+ from .run_app_convert import main as run_ui_app_convert
@@ -0,0 +1,9 @@
1
+ from converter import run_app_convert
2
+
3
+ def main() -> None:
4
+ run_app_convert.main()
5
+
6
+
7
+ if __name__ == '__main__':
8
+ main()
9
+
@@ -1,4 +1,4 @@
1
- # converter/excel/page_edit.py
1
+ # converter/home_page/_page_edit.py
2
2
  from __future__ import annotations
3
3
 
4
4
  from converter.util import (
@@ -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(controller.data_notifier.value)
213
-
214
- if not forms_controller.forms_state.data_is_valid():
215
- messagebox.showerror("Erro", "Tabela TOI inválida")
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