pyflutter-app 0.2.5__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.
Files changed (106) hide show
  1. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/pyflutter_app.iml +1 -1
  2. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/PKG-INFO +1 -5
  3. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/cartas/__init__.py +1 -1
  4. pyflutter_app-0.2.5/cartas/table_toi.py → pyflutter_app-0.2.7/cartas/_table_toi.py +7 -7
  5. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/cartas/relatorio.py +127 -67
  6. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/common.py +10 -6
  7. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/controller_forms.py +11 -1
  8. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/model_forms.py +8 -5
  9. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/service.py +103 -11
  10. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/mediator.py +4 -4
  11. pyflutter_app-0.2.7/ocrlib/__init__.py +54 -0
  12. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/ocrlib/_common.py +19 -25
  13. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/ocrlib/_pdf_convert.py +34 -30
  14. pyflutter_app-0.2.7/ocrlib/_table_ocr.py +578 -0
  15. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/ocrlib/_tesseract.py +28 -10
  16. pyflutter_app-0.2.7/ocrlib/erros.py +5 -0
  17. pyflutter_app-0.2.5/ocrlib/ocr.py → pyflutter_app-0.2.7/ocrlib/relatorio_ocr.py +283 -150
  18. pyflutter_app-0.2.7/ocrlib/util.py +29 -0
  19. pyflutter_app-0.2.7/pyproject.toml +34 -0
  20. pyflutter_app-0.2.5/pyproject.toml → pyflutter_app-0.2.7/pyproject.toml.back +8 -1
  21. pyflutter_app-0.2.7/table_stream/__init__.py +92 -0
  22. pyflutter_app-0.2.7/table_stream/erros.py +162 -0
  23. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/hash_map.py +53 -223
  24. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sysfiles.py +192 -169
  25. pyflutter_app-0.2.7/table_stream/table.py +835 -0
  26. pyflutter_app-0.2.7/test.py +36 -0
  27. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/uv.lock +5 -241
  28. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/whatsapp_files/_table_whatsapp.py +5 -5
  29. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/whatsapp_files/stream_files.py +3 -3
  30. pyflutter_app-0.2.5/ocrlib/__init__.py +0 -24
  31. pyflutter_app-0.2.5/ocrlib/_table_ocr.py +0 -526
  32. pyflutter_app-0.2.5/ocrlib/erros.py +0 -0
  33. pyflutter_app-0.2.5/ocrlib/util.py +0 -12
  34. pyflutter_app-0.2.5/table_stream/__init__.py +0 -33
  35. pyflutter_app-0.2.5/table_stream/erros.py +0 -115
  36. pyflutter_app-0.2.5/table_stream/table.py +0 -573
  37. pyflutter_app-0.2.5/test.py +0 -92
  38. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.gitignore +0 -0
  39. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/.gitignore +0 -0
  40. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  41. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  42. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/misc.xml +0 -0
  43. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/modules.xml +0 -0
  44. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/.idea/vcs.xml +0 -0
  45. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/README.md +0 -0
  46. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/cartas/common.py +0 -0
  47. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/cartas/erros.py +0 -0
  48. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/cartas/find_toi.py +0 -0
  49. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/__init__.py +0 -0
  50. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/__main__.py +0 -0
  51. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/assets/forms_config.json +0 -0
  52. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/config_app.py +0 -0
  53. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/home_page/__init__.py +0 -0
  54. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/home_page/_page_edit.py +0 -0
  55. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/home_page/controller.py +0 -0
  56. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/home_page/model.py +0 -0
  57. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/home_page/view.py +0 -0
  58. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/lib_toi.py +0 -0
  59. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/Diagnorstico.txt +0 -0
  60. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/__init__.py +0 -0
  61. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/config.py +0 -0
  62. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/diagnose_forms.py +0 -0
  63. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/page_forms/view_forms.py +0 -0
  64. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/run_app_convert.py +0 -0
  65. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/table_widget.py +0 -0
  66. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/util.py +0 -0
  67. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/whatsapp/__init__.py +0 -0
  68. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/converter/whatsapp/model.py +0 -0
  69. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/export_app.sh +0 -0
  70. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/local_build/linux.sh +0 -0
  71. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/local_build/send.sh +0 -0
  72. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/main.py +0 -0
  73. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/mvc/controller.py +0 -0
  74. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/mvc/model.py +0 -0
  75. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/mvc/view.py +0 -0
  76. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/new-export_app.sh +0 -0
  77. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/ocrlib/export_ocr.sh +0 -0
  78. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/__init__.py +0 -0
  79. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/app.py +0 -0
  80. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/file_picker.py +0 -0
  81. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/navigator.py +0 -0
  82. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/scheduler.py +0 -0
  83. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/theme.py +0 -0
  84. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/__init__.py +0 -0
  85. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/buttons.py +0 -0
  86. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/container.py +0 -0
  87. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/core.py +0 -0
  88. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/list_builder.py +0 -0
  89. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/progress.py +0 -0
  90. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/tables.py +0 -0
  91. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/pyflutter_ui/ui/widgets.py +0 -0
  92. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/notifier.py +0 -0
  93. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/pattner/__init__.py +0 -0
  94. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/pattner/adapter.py +0 -0
  95. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/__init__.py +0 -0
  96. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/check_mod.py +0 -0
  97. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/load.py +0 -0
  98. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/options.py +0 -0
  99. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/parse.py +0 -0
  100. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/sheet/save.py +0 -0
  101. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/state.py +0 -0
  102. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/thread.py +0 -0
  103. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/table_stream/workbook.py +0 -0
  104. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/whatsapp_files/__init__.py +0 -0
  105. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/whatsapp_files/common.py +0 -0
  106. {pyflutter_app-0.2.5 → pyflutter_app-0.2.7}/whatsapp_files/erros.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="uv (pyflutter)" jdkType="Python SDK" />
7
+ <orderEntry type="jdk" jdkName="~/GIT/pyflutter/.venv" jdkType="Python SDK" />
8
8
  <orderEntry type="sourceFolder" forTests="false" />
9
9
  </component>
10
10
  <component name="PyDocumentationSettings">
@@ -1,15 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyflutter_app
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: Bibliotecas Python para conversão de documentos, interface e processamento de tabelas.
5
5
  Requires-Python: >=3.12
6
- Requires-Dist: opencv-python>=5.0.0.93
7
6
  Requires-Dist: openpyxl>=3.1.5
8
7
  Requires-Dist: pandas-stubs~=3.0.3
9
8
  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
13
9
  Description-Content-Type: text/markdown
14
10
 
15
11
  # pyflutter
@@ -1,5 +1,5 @@
1
1
  from .find_toi import extrair_uc_toi
2
- from .table_toi import HeaderTOI, TableTOI, LocalEntregaCarta, TipoCarta
2
+ from ._table_toi import HeaderTOI, TableTOI, LocalEntregaCarta, TipoCarta
3
3
  from .common import BuilderConfigTOI, ConfigTOI
4
4
  from .relatorio import (
5
5
  CreateRelatorioTOI, TableTOI, ReadWriteTableTOI, load_files_toi
@@ -5,7 +5,7 @@ from typing import Any, Callable, TypeVar
5
5
  from pandas import DataFrame
6
6
 
7
7
  from table_stream import (
8
- ArrayList, HashMapDict, TableElements, ColumnV, RowH, PathElement,
8
+ ArrayList, HashMapDict, TableMap, ColumnV, RowH, PathElement,
9
9
  Directory, File, HeaderMetaData, concat_tables, ColumnNotFoundError,
10
10
  )
11
11
 
@@ -24,11 +24,11 @@ class HeaderTOI(StrEnum):
24
24
  TIPO_DOC = "tipo_carta"
25
25
  IS_FILE = "arquivo?"
26
26
 
27
- FULL_PATH = HeaderMetaData.arquivo.value
28
- SRC = HeaderMetaData.pasta_pai.value
29
- FILE_EXTENSION = HeaderMetaData.extensao_arquivo.value
27
+ FULL_PATH = HeaderMetaData.FILE_PATH.value
28
+ SRC = HeaderMetaData.PARENT_DIR.value
29
+ FILE_EXTENSION = HeaderMetaData.FILE_EXTENSION.value
30
30
 
31
- HASH_FILE = HeaderMetaData.md5.value
31
+ HASH_FILE = HeaderMetaData.HASH_FILE.value
32
32
  FILE_BASENAME = "basename"
33
33
 
34
34
 
@@ -45,7 +45,7 @@ class LocalEntregaCarta(StrEnum):
45
45
  PORTAO = "PORTÃO"
46
46
 
47
47
 
48
- class TableTOI(TableElements[HeaderTOI]):
48
+ class TableTOI(TableMap[HeaderTOI]):
49
49
  """
50
50
  Tabela para representar dados de cartas TOI.
51
51
  """
@@ -133,7 +133,7 @@ class TableTOI(TableElements[HeaderTOI]):
133
133
  return cls()
134
134
 
135
135
  final = concat_tables(list_tables)
136
- return cls(final.tb_map)
136
+ return cls(final._table_dict)
137
137
 
138
138
  @classmethod
139
139
  def create_from_data(cls, data: DataFrame) -> TableTOI:
@@ -1,18 +1,33 @@
1
1
  from ocrlib import (
2
- TableOcr, HeaderOcr, DocumentInputStream, ReadWriteTableOCR,
3
- CreateRelatorioExcelOcr, ColumnV,
2
+ TableOcr,
3
+ HeaderOcr,
4
+ DocumentInputStream,
5
+ ReadWriteTableOCR,
6
+ ControllerRelatorioOcr,
7
+ ColumnV,
8
+ ControllerOcr,
4
9
  )
5
10
 
6
11
  from whatsapp_files import TableWhatsApp, HeaderWhatsApp, CreateRelatorioWhatsApp
7
12
 
8
13
  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
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,
12
27
  )
13
28
 
14
29
  import pandas as pd
15
- from cartas.table_toi import HeaderTOI, TableTOI, TipoCarta, LocalEntregaCarta
30
+ from cartas._table_toi import HeaderTOI, TableTOI, TipoCarta, LocalEntregaCarta
16
31
  from cartas.find_toi import extrair_uc_toi
17
32
  from cartas.common import ConfigTOI, BuilderConfigTOI
18
33
  from os import rename as rename_file
@@ -39,11 +54,11 @@ class ReadWriteTableTOI(object):
39
54
  super().__init__()
40
55
  self._initialized: bool = True
41
56
  self.builder: BuilderConfigTOI = BuilderConfigTOI()
42
-
57
+
43
58
  @property
44
59
  def file_table_cache(self) -> File:
45
60
  return BuilderConfigTOI().builder().file_table_toi
46
-
61
+
47
62
  def load_table_cache(self) -> TableTOI:
48
63
 
49
64
  f = self.file_table_cache
@@ -79,7 +94,7 @@ class CreateRelatorioTOI(object):
79
94
  Cria uma tabela de Cartas TOI apartir dos chats do WhatsApp.
80
95
  Conectando os arquivos das conversas aos textos extraídos via OCR.
81
96
 
82
- TableWhatsApp() é usado para obter DATA e HORA do envio de um arquivo
97
+ TableWhatsApp() é usado para obter DATA e HORA do envio de um arquivo
83
98
  no chat, pois a data de envio no chat e a data de entrega de uma carta em campo.
84
99
  """
85
100
 
@@ -94,53 +109,62 @@ class CreateRelatorioTOI(object):
94
109
 
95
110
  def __init__(self) -> None:
96
111
 
97
- self.relatorio_ocr: CreateRelatorioExcelOcr = CreateRelatorioExcelOcr()
112
+ self.relatorio_ocr: ControllerRelatorioOcr = ControllerRelatorioOcr()
98
113
  self.relatorio_whatsapp: CreateRelatorioWhatsApp = CreateRelatorioWhatsApp()
99
114
 
100
115
  def load_table_whatsapp(self) -> TableWhatsApp:
101
116
  return self.relatorio_whatsapp.load_cache_table()
102
-
117
+
103
118
  def find_on_cache(self, map: HashMapDict[hashFile, File]) -> TableTOI | None:
104
119
  """
105
120
  Gera uma tabela TOI apartir da tabela TOI em cache sem executar
106
- o filtro de texto em OCR com expressão regular.
121
+ o filtro de texto em OCR com expressão regular. Apenas gera uma
122
+ tabela consultando a base TOI em cache
107
123
  """
108
-
124
+
109
125
  if map.is_empty():
110
126
  return None
111
-
127
+
112
128
  tb_cache = ReadWriteTableTOI().load_table_cache()
113
129
  final = TableTOI()
114
-
130
+
115
131
  header = map.header()
116
132
  h: hashFile
117
-
133
+
118
134
  for h in header:
119
135
  f = map.get_value(h)
120
-
121
- content = tb_cache.seach(HeaderTOI.HASH_FILE, h)
136
+
137
+ content = tb_cache.search(HeaderTOI.HASH_FILE, h)
122
138
  if content.empty():
123
139
  continue
124
-
140
+
125
141
  content_row = content.get_row_at(0)
126
- content_row.set(HeaderTOI.FILE_EXTENSION, f.extension() or '.nan')
142
+ content_row.set(HeaderTOI.FILE_EXTENSION, f.extension() or ".nan")
127
143
  content_row.set(HeaderTOI.FILE_BASENAME, f.basename())
128
144
  content_row.set(HeaderTOI.IS_FILE, "S" if f.is_file() else "N")
129
145
  content_row.set(HeaderTOI.FULL_PATH, f.absolute())
130
146
  content_row.set(HeaderTOI.SRC, f.parent().absolute())
131
147
  final.add_row(content_row)
132
-
148
+
133
149
  return final
134
150
 
135
- def create_from_map(self, map: HashMapDict[hashFile, File], *, ignore_null: bool = True) -> TableTOI | None:
151
+ def create_from_map(
152
+ self, map: HashMapDict[hashFile, File], *, ignore_null: bool = True
153
+ ) -> TableTOI | None:
136
154
  """
137
155
  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
-
156
+ tabela whatsapp;
157
+
158
+ II) Gera um relatório OCR [CreateRelatorioExcelOcr()] com os metadados básicos
159
+ de cada arquivo;
160
+
143
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.
144
168
  """
145
169
 
146
170
  if map.is_empty():
@@ -154,7 +178,7 @@ class CreateRelatorioTOI(object):
154
178
  if table_ocr is None:
155
179
  return None
156
180
 
157
- if table_ocr.empty():
181
+ if table_ocr.is_empty():
158
182
  return None
159
183
 
160
184
  # Cada arquivo gera/extrai a sua própria tabela, ao final essa
@@ -163,7 +187,7 @@ class CreateRelatorioTOI(object):
163
187
  keys = map.header()
164
188
  total = keys.size()
165
189
 
166
- print(f'Criando relatório TOI de {total} arquivos')
190
+ print(f"Criando relatório TOI de {total} arquivos")
167
191
  k: hashFile
168
192
  f: File
169
193
 
@@ -172,14 +196,16 @@ class CreateRelatorioTOI(object):
172
196
 
173
197
  print(f"[FILTRANDO] [{n+1}/{total}] {f.basename()}")
174
198
 
175
- current_table_ocr: TableOcr = table_ocr.seach(HeaderOcr.HASH_FILE, k)
199
+ current_table_ocr: TableOcr = table_ocr.search(HeaderOcr.HASH_FILE, k)
176
200
  text_ocr: str = None
177
-
178
- if not current_table_ocr.empty():
201
+
202
+ if not current_table_ocr.is_empty():
179
203
  text_ocr = current_table_ocr.to_string(HeaderOcr.TEXT)
180
204
 
181
- txt_uc_toi: tuple | None = extrair_uc_toi(text_ocr) if text_ocr is not None else None
182
-
205
+ txt_uc_toi: tuple | None = (
206
+ extrair_uc_toi(text_ocr) if text_ocr is not None else None
207
+ )
208
+
183
209
  if txt_uc_toi is None:
184
210
  if ignore_null:
185
211
  continue
@@ -194,39 +220,47 @@ class CreateRelatorioTOI(object):
194
220
  if txt_uc_toi is not None:
195
221
  uc = txt_uc_toi[0]
196
222
  toi = txt_uc_toi[1]
197
-
198
- current_table_whatsapp = table_whatsapp.seach(HeaderWhatsApp.hash_md5, k)
199
-
223
+
224
+ current_table_whatsapp = table_whatsapp.search(HeaderWhatsApp.hash_md5, k)
225
+
200
226
  if not current_table_whatsapp.empty():
201
227
  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]
228
+ day = dt.split(" ")[0]
229
+ mon = dt.split(" ")[1]
230
+ year = dt.split(" ")[2]
205
231
 
206
232
  tb_toi = TableTOI()
207
-
233
+
208
234
  tb_toi.add(ColumnV(HeaderTOI.UC, ArrayList([uc])))
209
235
  tb_toi.add(ColumnV(HeaderTOI.TOI, ArrayList([toi])))
210
236
  tb_toi.add(ColumnV(HeaderTOI.YEAR, ArrayList([year])))
211
237
  tb_toi.add(ColumnV(HeaderTOI.MON, ArrayList([mon])))
212
238
  tb_toi.add(ColumnV(HeaderTOI.DAY, ArrayList([day])))
213
239
  tb_toi.add(ColumnV(HeaderTOI.DATE, ArrayList([dt])))
214
-
215
- tb_toi.add(ColumnV(HeaderTOI.FILE_EXTENSION, ArrayList([f.extension() or '.nan'])))
240
+
241
+ tb_toi.add(
242
+ ColumnV(HeaderTOI.FILE_EXTENSION, ArrayList([f.extension() or ".nan"]))
243
+ )
216
244
  tb_toi.add(ColumnV(HeaderTOI.FILE_BASENAME, ArrayList([f.basename()])))
217
245
  tb_toi.add(ColumnV(HeaderTOI.HASH_FILE, ArrayList([k])))
218
246
  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"])))
247
+ tb_toi.add(
248
+ ColumnV(HeaderTOI.IS_FILE, ArrayList(["S" if f.is_file() else "N"]))
249
+ )
220
250
  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
-
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
+
225
259
  list_tables_toi.add(tb_toi)
226
-
260
+
227
261
  if list_tables_toi.empty():
228
262
  return None
229
-
263
+
230
264
  final = TableTOI.create_void_table()
231
265
  list_tables_toi.for_each(lambda t: final.merge(t))
232
266
  return final
@@ -236,11 +270,11 @@ class CreateRelatorioTOI(object):
236
270
  """
237
271
  Filtra UC e TOI de uma carta apartir do texto OCR extraído.
238
272
  """
239
- tb_current_hash = table_ocr_file.seach(HeaderOcr.HASH_FILE, hf)
240
- if tb_current_hash.empty():
273
+ tb_current_hash = table_ocr_file.search(HeaderOcr.HASH_FILE, hf)
274
+ if tb_current_hash.is_empty():
241
275
  return None
242
276
 
243
- values = tb_current_hash.get(HeaderOcr.TEXT).elements
277
+ values = tb_current_hash.get(HeaderOcr.TEXT)._elements
244
278
 
245
279
  for v in values:
246
280
  txt = extrair_uc_toi(v)
@@ -257,34 +291,60 @@ def load_files_toi(files: ArrayList[File]) -> TableTOI | None:
257
291
  if files.empty():
258
292
  print(f"[DEBUG]: load_files_toi() lista de arquivos vazia!")
259
293
  return None
260
-
294
+
261
295
  map = HashMapDict[hashFile, File]()
262
296
  files.for_each(lambda f: map.set_value(load_file_hash(f), f))
263
-
297
+
264
298
  content_table = CreateRelatorioTOI().create_from_map(map)
265
299
  if content_table is None:
266
300
  return None
267
301
  if content_table.empty():
268
302
  return None
269
-
303
+
270
304
  # 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
-
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
+
277
319
  # Filtar UCs validas
278
320
  df_merge = content_tb_merge.to_dataframe().astype(str)
279
321
  df_merge = df_merge[df_merge[HeaderTOI.UC] != TableTOI.NULL_STRING]
280
322
  df_merge.drop_duplicates(subset=[HeaderTOI.HASH_FILE], inplace=True)
281
-
323
+
282
324
  rw = ReadWriteTableTOI()
283
325
  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)
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
+ )
287
337
  df_cache.drop_duplicates(subset=[HeaderTOI.HASH_FILE]).astype(str)
288
-
338
+
289
339
  rw.save_table_cache(TableTOI.create_from_data(df_cache))
290
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
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  import pandas as pd
9
9
 
10
- from table_stream import HashMapDict, ArrayList, File, TableElements, RowH
10
+ from table_stream import HashMapDict, ArrayList, File, TableMap, RowH
11
11
  from cartas import TableTOI, HeaderTOI, TipoCarta, LocalEntregaCarta
12
12
 
13
13
 
@@ -43,7 +43,7 @@ class ResultStatus:
43
43
 
44
44
 
45
45
  # ---------------------------------------------------------------------- normalização
46
- _EMPTY = {"", "nan", "NaN", "None", "NaT", TableElements.NULL_STRING}
46
+ _EMPTY = {"", "nan", "NaN", "None", "NaT", TableMap.NULL_STRING}
47
47
  _DATE_FORMATS = (
48
48
  "%d %m %Y", # DateValues.DATE_TOI (saída do relatório TOI)
49
49
  "%d/%m/%Y",
@@ -70,11 +70,13 @@ def normalize_date(value: Any) -> str:
70
70
  s = clean_text(value)
71
71
  if not s:
72
72
  raise ValueError("data de entrega vazia")
73
+
73
74
  for fmt in _DATE_FORMATS:
74
75
  try:
75
76
  return datetime.strptime(s, fmt).strftime("%d/%m/%Y")
76
77
  except ValueError:
77
78
  continue
79
+
78
80
  raise ValueError(f"data de entrega inválida: {s}")
79
81
 
80
82
 
@@ -104,8 +106,8 @@ class MapRows(HashMapDict[int, RowH[HeaderForms, Any]]):
104
106
  self.update_status(idx, StatusForms.PENDING)
105
107
 
106
108
  # ---- conversões
107
- def to_table(self) -> TableElements[HeaderForms]:
108
- tb = TableElements()
109
+ def to_table(self) -> TableMap[HeaderForms]:
110
+ tb = TableMap()
109
111
  for idx in self.header():
110
112
  tb.add_row(self.get_value(idx))
111
113
  return tb
@@ -117,9 +119,11 @@ class MapRows(HashMapDict[int, RowH[HeaderForms, Any]]):
117
119
  def to_view_data(self) -> pd.DataFrame:
118
120
  """DataFrame enxuto para exibir na tabela da UI."""
119
121
  records = []
122
+
120
123
  for idx in self.header():
121
124
  r = self[idx]
122
125
  path = clean_text(r.get(HeaderForms.FULL_PATH))
126
+
123
127
  records.append({
124
128
  "#": idx + 1,
125
129
  "STATUS": str(r.get(HeaderForms.STATUS)),
@@ -131,12 +135,14 @@ class MapRows(HashMapDict[int, RowH[HeaderForms, Any]]):
131
135
  "TIPO": r.get(HeaderForms.TIPO),
132
136
  "ARQUIVO": os.path.basename(path) if path else "-",
133
137
  })
138
+
134
139
  return pd.DataFrame(records)
135
140
 
136
141
  def save(self, file: File) -> None:
137
142
  df = self.to_data()
138
143
  if df is None:
139
144
  return
145
+
140
146
  df.to_excel(file.absolute(), index=False)
141
147
 
142
148
  # ---- criação a partir da tabela TOI
@@ -165,8 +171,6 @@ class MapRows(HashMapDict[int, RowH[HeaderForms, Any]]):
165
171
 
166
172
  @classmethod
167
173
  def create_from_table(cls, tb: TableTOI) -> MapRows:
168
- # NOTA: não usa tb.rows(): TableElements.rows() usa range(total-1)
169
- # e descarta a ÚLTIMA linha da tabela.
170
174
  mp = cls()
171
175
  for n in range(tb.size()):
172
176
  mp.set_value(n, cls.convert_row(tb.get_row_at(n)))
@@ -197,19 +197,29 @@ class ControllerForms(ChangeNotifier):
197
197
 
198
198
  self.save_result(silent=True)
199
199
  self.notify_listeners()
200
+
201
+ def get_dir_datetime(self) -> str:
202
+ t = datetime.now()
203
+ d = f"{t.year}-{t.month}-{t.day}"
204
+ return d
200
205
 
201
206
  # ------------------------------------------------------------------ resultado
202
207
  def save_result(self, *, silent: bool = False) -> None:
208
+
203
209
  if self.map_rows.is_empty():
204
210
  if not silent:
205
211
  self.show_info("Nada para salvar.")
206
212
  return
207
213
 
208
214
  out = self.result_file
215
+
209
216
  if out is None:
210
217
  out_dir: Directory = self.get_config().value.work_dir.concat("Forms", True).concat("Resultados", True)
211
- out = out_dir.join_file(f"resultado-forms-{datetime.now():%Y%m%d-%H%M%S}.xlsx")
218
+ out_dir = out_dir.concat(self.get_dir_datetime(), True)
219
+
220
+ out = out_dir.join_file(f"LogForms-{datetime.now():%Y%m%d-%H%M%S}.xlsx")
212
221
  self.result_file = out
222
+
213
223
  try:
214
224
  # grava em arquivo temporário e renomeia: nunca deixa um .xlsx corrompido/0 bytes
215
225
  tmp = File(f"{out.absolute()}-tmp.xlsx")
@@ -127,14 +127,17 @@ class ModelForms(object):
127
127
 
128
128
  # ------------------------------------------------------------------ execução
129
129
  def start(
130
- self,
131
- jobs: dict[int, FormsSubmission],
132
- config: FormsConfig,
133
- cb: FormsCallbacks,
134
- ) -> None:
130
+ self,
131
+ jobs: dict[int, FormsSubmission],
132
+ config: FormsConfig,
133
+ cb: FormsCallbacks,
134
+ ) -> None:
135
+
135
136
  if self.is_running():
136
137
  raise RuntimeError("Já existe um lançamento em andamento")
138
+
137
139
  self._stop.clear()
140
+
138
141
  self._thread = threading.Thread(
139
142
  target=self._worker, args=(jobs, config, cb), daemon=True, name="forms-worker",
140
143
  )