pyflutter-app 0.1.7__tar.gz → 0.1.8__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 (63) hide show
  1. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/PKG-INFO +1 -1
  2. pyflutter_app-0.1.8/converter/file_documents.py +234 -0
  3. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/lib_toi.py +94 -161
  4. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/model_forms.py +1 -1
  5. pyflutter_app-0.1.8/main.py +40 -0
  6. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyproject.toml +1 -1
  7. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/__init__.py +4 -0
  8. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/hash_map.py +4 -0
  9. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sysfiles.py +96 -39
  10. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/uv.lock +5 -5
  11. pyflutter_app-0.1.7/main.py +0 -21
  12. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.gitignore +0 -0
  13. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/.gitignore +0 -0
  14. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  15. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  16. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/misc.xml +0 -0
  17. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/modules.xml +0 -0
  18. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/pyflutter.iml +0 -0
  19. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/.idea/vcs.xml +0 -0
  20. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/README.md +0 -0
  21. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/__init__.py +0 -0
  22. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/__main__.py +0 -0
  23. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/assets/forms_config.json +0 -0
  24. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/config_app.py +0 -0
  25. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/home_page/__init__.py +0 -0
  26. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/home_page/_page_edit.py +0 -0
  27. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/home_page/controller.py +0 -0
  28. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/home_page/model.py +0 -0
  29. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/home_page/view.py +0 -0
  30. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/__init__.py +0 -0
  31. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/common.py +0 -0
  32. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/config.py +0 -0
  33. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/controller_forms.py +0 -0
  34. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/page_forms/service.py +0 -0
  35. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/run.py +0 -0
  36. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/table_widget.py +0 -0
  37. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/whatsapp/__init__.py +0 -0
  38. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/converter/whatsapp/model.py +0 -0
  39. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/export_app.sh +0 -0
  40. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/local_build/linux.sh +0 -0
  41. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/local_build/send.sh +0 -0
  42. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/__init__.py +0 -0
  43. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/app.py +0 -0
  44. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/core.py +0 -0
  45. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/file_picker.py +0 -0
  46. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/list_builder.py +0 -0
  47. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/navigator.py +0 -0
  48. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/theme.py +0 -0
  49. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/pyflutter_ui/widgets.py +0 -0
  50. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/erros.py +0 -0
  51. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/notifier.py +0 -0
  52. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/pattner/__init__.py +0 -0
  53. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/pattner/adapter.py +0 -0
  54. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/pattner/observer.py +0 -0
  55. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/__init__.py +0 -0
  56. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/check_mod.py +0 -0
  57. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/load.py +0 -0
  58. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/options.py +0 -0
  59. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/parse.py +0 -0
  60. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/sheet/save.py +0 -0
  61. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/state.py +0 -0
  62. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/thread.py +0 -0
  63. {pyflutter_app-0.1.7 → pyflutter_app-0.1.8}/table_stream/workbook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyflutter_app
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Bibliotecas Python para conversão de documentos, interface e processamento de tabelas.
5
5
  Requires-Python: >=3.14
6
6
  Requires-Dist: openpyxl>=3.1.5
@@ -0,0 +1,234 @@
1
+ #
2
+ from __future__ import annotations
3
+ from datetime import datetime
4
+ from enum import Enum, StrEnum, auto
5
+ from abc import ABC, abstractmethod
6
+ from table_stream import ArrayList, File, Directory, HashMapDict, ContentFiles, PathElement
7
+ import shutil
8
+
9
+
10
+ class ORGANIZABLE_DOC_NAME(StrEnum):
11
+
12
+ IMG_WP = auto()
13
+ IMG_WP_DOWNLOADED = auto()
14
+ FILE_TOI = auto()
15
+ FILE_DIGITALIZED = auto()
16
+ FILE_CAM_SCANNER = auto()
17
+
18
+
19
+ class NameDocumentOrganizable(ABC):
20
+
21
+ def __init__(self, name: str) -> None:
22
+ super().__init__()
23
+ self._name: str = name
24
+
25
+ @property
26
+ def date(self) -> str:
27
+ return self.date_time().strftime("%Y-%m-%d")
28
+
29
+ @property
30
+ def year(self) -> str:
31
+ return f"{self.date_time().year}"
32
+
33
+ @property
34
+ def mon(self) -> str:
35
+ return f"{self.date_time().month}"
36
+
37
+ @property
38
+ def day(self) -> str:
39
+ return f"{self.date_time().day}"
40
+
41
+ def __hash__(self) -> int:
42
+ return self._name.__hash__()
43
+
44
+ def hash(self) -> int:
45
+ return self.__hash__()
46
+
47
+ def get_name_list(self, separator: str) -> ArrayList[str]:
48
+ return ArrayList.from_string(self._name, separator)
49
+
50
+ @abstractmethod
51
+ def hour(self) -> str | None:
52
+ pass
53
+
54
+ @abstractmethod
55
+ def key_id(self) -> str:
56
+ pass
57
+
58
+ @abstractmethod
59
+ def date_time(self) -> datetime:
60
+ pass
61
+
62
+
63
+ class NameImageWhatsApp(NameDocumentOrganizable):
64
+
65
+ def __init__(self, name: str) -> None:
66
+ super().__init__(name)
67
+
68
+ def hour(self) -> str | None:
69
+ return None
70
+
71
+ def key_id(self) -> str:
72
+ return self._name.split("-")[2]
73
+
74
+ def date_time(self) -> datetime:
75
+ date_str = self.get_name_list("-")[1]
76
+ y = date_str[0:4]
77
+ m = date_str[4:6]
78
+ d = date_str[6:8]
79
+ return datetime.strptime(f"{y}-{m}-{d}", "%Y-%m-%d")
80
+
81
+
82
+ class NameImageWhatsAppDownloaded(NameDocumentOrganizable):
83
+
84
+ def __init__(self, name: str) -> None:
85
+ super().__init__(name)
86
+
87
+ def hour(self) -> str | None:
88
+ hour_list = self.get_name_list(" ")
89
+ hour_base:str = hour_list[4]
90
+ return hour_base.replace('.', ':')
91
+
92
+ def key_id(self) -> str:
93
+ h = self.hour().replace(':', '_')
94
+ d = self.date
95
+ return f'{d}-{h}'
96
+
97
+ def date_time(self) -> datetime:
98
+ dt = self.get_name_list(" ")[2]
99
+ return datetime.strptime(dt, "%Y-%m-%d")
100
+
101
+
102
+ class NameDocumentToi(NameDocumentOrganizable):
103
+
104
+ def __init__(self, name: str) -> None:
105
+ super().__init__(name)
106
+
107
+ def hour(self) -> str | None:
108
+ return None
109
+
110
+ def key_id(self) -> str:
111
+ name_list = self.get_name_list(" ")
112
+ return f"{name_list[0]}-{name_list[1]}"
113
+
114
+ def date_time(self) -> datetime:
115
+ name_list = self.get_name_list(" ")[2:5]
116
+ y = name_list[2]
117
+ m = name_list[1]
118
+ d = name_list[0]
119
+ return datetime.strptime(f'{y}-{m}-{d}', '%Y-%m-%d')
120
+
121
+
122
+ class FactoryNameDocument(object):
123
+
124
+ def __init__(self, doc: ORGANIZABLE_DOC_NAME) -> None:
125
+ self.doc: ORGANIZABLE_DOC_NAME = doc
126
+
127
+ def check_name(self, name: str) -> bool:
128
+ if self.doc == ORGANIZABLE_DOC_NAME.IMG_WP:
129
+ if len(name.split("-")) < 3:
130
+ return False
131
+ elif self.doc == ORGANIZABLE_DOC_NAME.IMG_WP_DOWNLOADED:
132
+ if len(name.split(" ")) < 5:
133
+ return False
134
+ elif self.doc == ORGANIZABLE_DOC_NAME.FILE_TOI:
135
+ if len(name.split(" ")) < 5:
136
+ return False
137
+ return True
138
+
139
+ def create(self, name: str) -> NameDocumentOrganizable | None:
140
+ if not self.check_name(name):
141
+ return None
142
+
143
+ if self.doc == ORGANIZABLE_DOC_NAME.IMG_WP:
144
+ return NameImageWhatsApp(name)
145
+ elif self.doc == ORGANIZABLE_DOC_NAME.IMG_WP_DOWNLOADED:
146
+ return NameImageWhatsAppDownloaded(name)
147
+ elif self.doc == ORGANIZABLE_DOC_NAME.FILE_TOI:
148
+ return NameDocumentToi(name)
149
+
150
+ return None
151
+
152
+
153
+ class FactoryCreateMapDocuments(object):
154
+
155
+ def __init__(self, src: Directory) -> None:
156
+ self.src: Directory = src
157
+
158
+ def get_dirs(self, recursive: bool = False) -> ArrayList[Directory]:
159
+ return self.src.content_dirs(recursive)
160
+
161
+ def get_files(self, recursive: bool = False) -> ArrayList[File]:
162
+ content = ContentFiles(self.src)
163
+
164
+ imgs = content.get_images(recursive)
165
+ imgs.extend(
166
+ content.get_pdfs(recursive)
167
+ )
168
+ return imgs
169
+
170
+ def create_map_names(self, doc: ORGANIZABLE_DOC_NAME) -> HashMapDict[NameDocumentOrganizable, PathElement]:
171
+ tb = HashMapDict()
172
+ factory_names = FactoryNameDocument(doc)
173
+ files = self.get_files()
174
+ files.extend(self.get_dirs())
175
+
176
+ print(f'Arquivos ... {files.size()}')
177
+ print(f'Diretórios ... {self.get_dirs().size()}')
178
+
179
+ f: PathElement
180
+ n: NameDocumentOrganizable | None
181
+ for f in files:
182
+ n = factory_names.create(f.name())
183
+ if n is None:
184
+ print(f'Pulando ... {f.basename()}')
185
+ continue
186
+
187
+ print(f'Adicionando ... {f.basename()}')
188
+ tb[n] = f
189
+ return tb
190
+
191
+
192
+ class OrganizeDocumentsNames(object):
193
+
194
+ def __init__(self, output: Directory) -> None:
195
+ self.output: Directory = output
196
+
197
+ def get_final_dir(self, doc: NameDocumentOrganizable) -> Directory:
198
+ y = doc.year
199
+ m = doc.mon
200
+ d = doc.day
201
+ dt = f'{y}-{m}'
202
+
203
+ return self.output.concat(dt).concat(d, True)
204
+
205
+ def move(self, src: PathElement, dest: PathElement):
206
+ print(f'[MOVENDO] ... {dest.absolute()}')
207
+ shutil.move(src.absolute(), dest.absolute())
208
+
209
+ def organize(self, map: HashMapDict[NameDocumentOrganizable, PathElement]):
210
+
211
+ k: NameDocumentOrganizable
212
+ v: PathElement
213
+
214
+ for k, v in map.get_real_hash_map().items():
215
+ final_dir = self.get_final_dir(k)
216
+ new_path = final_dir.concat(v.basename())
217
+
218
+ self.move(v, new_path)
219
+
220
+
221
+ def main():
222
+
223
+ src = Directory("/home/bruno/Documentos/Arquivos WhatsApp/CARTAS TOI RURAL")
224
+ out = Directory('/home/bruno/Downloads/test')
225
+
226
+ map = FactoryCreateMapDocuments(src).create_map_names(ORGANIZABLE_DOC_NAME.IMG_WP)
227
+ value = OrganizeDocumentsNames(out)
228
+ value.organize(map)
229
+
230
+
231
+ if __name__ == '__main__':
232
+ main()
233
+
234
+
@@ -131,7 +131,7 @@ class PathFile(FilePath):
131
131
 
132
132
 
133
133
  #===================================================================#
134
- # Mapas para planilhas, TOIs e roteiros.
134
+ # Map para colunas de analitico
135
135
  #===================================================================#
136
136
 
137
137
  class MapColumnsAnalitico(TypedDict):
@@ -139,11 +139,53 @@ class MapColumnsAnalitico(TypedDict):
139
139
  livro:str
140
140
  local: str
141
141
  rota: str
142
+ roteiro: str
142
143
  latitude: str
143
144
  longitude: str
144
145
 
145
146
 
147
+ columnsAnalitico: MapColumnsAnalitico = {
148
+ "uc": "numcdc",
149
+ "livro": "numliv",
150
+ "local": "codlcd",
151
+ "rota": "numrota",
152
+ "latitude": "vlr_coordenada_latitude",
153
+ "longitude": "vlr_coordenada_longitude",
154
+ "roteiro": "concatenar",
155
+ }
156
+
157
+
158
+
159
+ def columns_analitico_rotas() -> MapColumnsAnalitico:
160
+ """
161
+ Retorna um mapa de colunas, formatado para o analitico
162
+ de rotas.
163
+ """
164
+ return {
165
+ "uc": "numcdc",
166
+ "livro": "numliv",
167
+ "local": "codlcd",
168
+ "rota": "numrota",
169
+ "latitude": "vlr_coordenada_latitude",
170
+ "longitude": "vlr_coordenada_longitude",
171
+ "roteiro": "concatenar",
172
+ }
173
+
174
+
175
+ def map_columns_analitico_repasse() -> MapColumnsAnalitico:
176
+ """
177
+ Retorna um mapa de colunas, formatado para o analitico de repasse.
178
+ """
179
+ columnsAnalitico["uc"] = "num_uc"
180
+ columnsAnalitico["livro"] = "num_livro"
181
+ columnsAnalitico["local"] = "cod_localidade"
182
+ columnsAnalitico["rota"] = "num_rota"
183
+ return columnsAnalitico
184
+
185
+
186
+ #===================================================================#
146
187
  # Identificar uc, toi, data e etc pelo nome de arquivo.
188
+ #===================================================================#
147
189
  class MapIdDocuments(TypedDict):
148
190
 
149
191
  uc: int
@@ -186,7 +228,7 @@ def map_id_documents() -> MapIdDocuments:
186
228
  }
187
229
 
188
230
 
189
- class MapNameDocument(object):
231
+ class NameDocument(object):
190
232
 
191
233
  def __init__(self, path: FilePath, get_map_id: Callable[[], MapIdDocuments]) -> None:
192
234
  self.path: FilePath = path
@@ -226,7 +268,7 @@ class MapNameDocument(object):
226
268
  return f'{self.day}/{self.mon}/{self.year}'
227
269
 
228
270
  @classmethod
229
- def from_file_path(cls, fp: FilePath) -> MapNameDocument:
271
+ def from_file_path(cls, fp: FilePath) -> NameDocument:
230
272
  return cls(fp, map_id_documents)
231
273
 
232
274
  @staticmethod
@@ -243,11 +285,11 @@ class MapNameDocument(object):
243
285
  return final
244
286
 
245
287
  @staticmethod
246
- def create_list_map_names(d: Directory) -> ArrayList[MapNameDocument]:
247
- paths: ArrayList[FilePath] = MapNameDocument.get_valid_documents(
248
- MapNameDocument.create_list_paths(d)
288
+ def create_list_map_names(d: Directory) -> ArrayList[NameDocument]:
289
+ paths: ArrayList[FilePath] = NameDocument.get_valid_documents(
290
+ NameDocument.create_list_paths(d)
249
291
  )
250
- return ArrayList([MapNameDocument.from_file_path(x) for x in paths])
292
+ return ArrayList([NameDocument.from_file_path(x) for x in paths])
251
293
 
252
294
  @staticmethod
253
295
  def is_valid_document_name(doc: FilePath) -> bool:
@@ -258,36 +300,14 @@ class MapNameDocument(object):
258
300
  final = ArrayList[FilePath]()
259
301
  p: FilePath
260
302
  for p in values:
261
- if MapNameDocument.is_valid_document_name(p):
303
+ if NameDocument.is_valid_document_name(p):
262
304
  final.append(p)
263
305
  return final
264
306
 
265
-
266
- columnsAnalitico: MapColumnsAnalitico = {
267
- "uc": "numcdc",
268
- "livro": "numliv",
269
- "local": "codlcd",
270
- "rota": "numrota",
271
- "latitude": "vlr_coordenada_latitude",
272
- "longitude": "vlr_coordenada_longitude"
273
- }
274
-
275
-
276
- def columns_analitico_rotas() -> MapColumnsAnalitico:
277
- """
278
- Retorna um mapa de colunas, formatado para o analitico
279
- de rotas.
280
- """
281
- return columnsAnalitico
282
-
283
-
284
- def map_columns_analitico_repasse() -> MapColumnsAnalitico:
285
- columnsAnalitico["uc"] = "num_uc"
286
- columnsAnalitico["livro"] = "num_livro"
287
- columnsAnalitico["local"] = "cod_localidade"
288
- columnsAnalitico["rota"] = "num_rota"
289
- return columnsAnalitico
290
-
307
+
308
+ #===================================================================#
309
+ # Dividir um DataFrame apartir dos valores de uma coluna
310
+ #===================================================================#
291
311
 
292
312
  class SplitDataToMap(object):
293
313
 
@@ -442,13 +462,12 @@ class ElementUC(object):
442
462
 
443
463
  class TableAnalitico(HashMapDataFrame):
444
464
 
445
- def __init__(self, df: pd.DataFrame) -> None:
465
+ def __init__(self, df: pd.DataFrame, map_analitico: MapColumnsAnalitico = None) -> None:
446
466
  super().__init__(df)
447
- self.col_uc: str = 'numcdc'
448
- self.col_livro: str = 'numliv'
449
- self.col_local: str = 'codlcd'
450
- self.col_rota: str = 'numrota'
451
- self.col_roteiro: str = 'concatenar'
467
+ if map_analitico is None:
468
+ map_analitico = columns_analitico_rotas()
469
+
470
+ self.map_analitico: MapColumnsAnalitico = map_analitico
452
471
  self.separator_roteiro: str = '_'
453
472
 
454
473
  @property
@@ -472,7 +491,8 @@ class TableAnalitico(HashMapDataFrame):
472
491
  return ParserData(self.get_real_hash_map())
473
492
 
474
493
  def get_ucs(self) -> ArrayList[UC]:
475
- col: pd.Series = self.get_value(self.col_uc).drop_duplicates()
494
+ col: pd.Series = self.get_value(self.map_analitico["uc"]).drop_duplicates()
495
+
476
496
  if (col is None) or (len(col) < 1):
477
497
  return ArrayList()
478
498
 
@@ -481,10 +501,10 @@ class TableAnalitico(HashMapDataFrame):
481
501
 
482
502
  def get_roteiros(self) -> ArrayList[Roteiro]:
483
503
 
484
- if not self.header().contains(self.col_roteiro):
485
- raise ValueError(f"[FALHA] ... a coluna não exite ... {self.col_roteiro}")
504
+ if not self.header().contains(self.map_analitico["roteiro"]):
505
+ raise ValueError(f"[FALHA] ... a coluna não exite ... {self.map_analitico["roteiro"]}")
486
506
 
487
- rotas: list[str] = self.get_value(self.col_roteiro).drop_duplicates().astype('str')
507
+ rotas: list[str] = self.get_value(self.map_analitico["roteiro"]).drop_duplicates().astype('str')
488
508
  final = ArrayList[Roteiro]()
489
509
  for i in rotas:
490
510
  final.append(
@@ -513,11 +533,11 @@ class TableAnalitico(HashMapDataFrame):
513
533
  final = ArrayList[ElementUC]()
514
534
  uc: UC
515
535
  for uc in ucs_list:
516
- df = self.filter_item(self.col_uc, uc.value)
536
+ df = self.filter_item(self.map_analitico["uc"], uc.value)
517
537
  if df.empty:
518
538
  continue
519
539
 
520
- roteiro: str = self.get_first_value(df, self.col_roteiro)
540
+ roteiro: str = self.get_first_value(df, self.map_analitico["roteiro"])
521
541
  if roteiro is None:
522
542
  continue
523
543
 
@@ -563,35 +583,6 @@ def find_index_uc(values: ArrayList[ElementUC], uc: UC) -> int | None:
563
583
  return idx
564
584
 
565
585
 
566
- def include_roteiro(src_data: pd.DataFrame, *, col_uc = 'UC', tb: TableAnalitico) -> pd.DataFrame:
567
- """
568
- Adiciona uma coluna com o nome [roteiro] com os roteiros concatenados de cada UC livro local e rota
569
- """
570
-
571
- if not col_uc in src_data.columns.tolist():
572
- raise ValueError(f"Coluna não encontrada ... {col_uc}")
573
-
574
- data: pd.DataFrame = tb.get_parser().select_columns([tb.col_uc, tb.col_roteiro]).get_data().astype('str')
575
- ucs: ArrayList[str] = ArrayList(src_data[col_uc].astype('str').values.tolist())
576
- final_column: ArrayList[str] = ArrayList()
577
-
578
- uc:str
579
- for uc in ucs:
580
- current_df = data[data[tb.col_uc] == uc]
581
- if current_df is None:
582
- final_column.append('-')
583
- continue
584
- if current_df.empty:
585
- final_column.append('-')
586
- continue
587
-
588
- current_roteiro = current_df[tb.col_roteiro].values.tolist()[0]
589
- final_column.append(current_roteiro)
590
-
591
- src_data["roteiro"] = pd.Series(final_column)
592
- return src_data
593
-
594
-
595
586
  def save_map_roteiro(hash_map: HashMapDict[UC, Roteiro], opt_save: OptionsIOSheet, output: File) -> None:
596
587
  data = dict[str, str]()
597
588
  header: ArrayList[UC] = hash_map.header()
@@ -605,89 +596,15 @@ def save_map_roteiro(hash_map: HashMapDict[UC, Roteiro], opt_save: OptionsIOShee
605
596
  save.save(df, output)
606
597
 
607
598
 
608
- class IncludeRoteiro(object):
609
- """
610
- Incluir uma coluna com roteiro em um DataFrame que contenha um coluna com UCs.
611
- a nova coluna incluida tem o nome [roteiro] com livro, local e rota concatenados e
612
- separados por um '_'.
613
- """
614
-
615
- def __init__(self, files_data_base: ArrayList[File], *, output_dir: Directory) -> None:
616
- """
617
- output_dir: Diretório para exportar os dados
618
- files_data_base: Lista de arquivos EXCEL contendo UCs e roteiros.
619
- """
620
-
621
- self.files_data_base: ArrayList[File] = files_data_base
622
- self.output_dir: Directory = output_dir
623
- self.temp_dir = self.output_dir.concat('Temp', create=True)
624
- self.temp_files: ArrayList[File] = ArrayList()
625
- self._counter: int = 0
626
- self._temp_data: pd.DataFrame = None
627
- self.current_base: pd.DataFrame = None
628
-
629
- def _check_void_roteiro(self, df: pd.DataFrame) -> pd.DataFrame:
630
- df_void = df[df["roteiro"] == '-']
631
- return pd.DataFrame() if df_void is None else df_void
632
-
633
- def _check_ok_roteiro(self, df: pd.DataFrame) -> pd.DataFrame:
634
- df_ok = df[df["roteiro"] != '-']
635
- return pd.DataFrame() if df_ok is None else df_ok
636
-
637
- def join_all_files(self):
638
- """
639
- Junta os arquivos processados em um unico arquivo excel.
640
- """
641
- if self.temp_files.size() == 0:
642
- print(f'[!] Nenhum Dado para exportar')
643
- return
644
-
645
- final_file = self.output_dir.join_file('RoteirosFinal.xlsx')
646
- data_list = ArrayList()
647
-
648
- f: File
649
- for f in self.temp_files:
650
- data_list.append(pd.read_excel(f.absolute()))
651
-
652
- final_data: pd.DataFrame = pd.concat(data_list)
653
- print(f'Exportando ... {final_file.absolute()}')
654
- final_data.to_excel(final_file.absolute())
655
-
656
- def include(self, df: pd.DataFrame, *, col_uc: str = 'UC'):
657
- """
658
- Inclui uma coluna com roteiros em um DataFrame() que contenha uma coluna de UCs
659
- """
660
- self.current_base = df
661
- file_path: File
662
- total = self.files_data_base.size()
663
- for n, file_path in enumerate(self.files_data_base):
664
- print(f'Lendo base ... [{n+1} / {total}] {file_path.basename()}')
665
-
666
- if file_path.is_excel():
667
- opt_load = OptionsIOSheet(SheetType.EXCEL)
668
- elif file_path.is_csv():
669
- opt_load = OptionsIOSheet(SheetType.CSV)
670
- else:
671
- print(f'[PULANDO] ... {file_path.basename()}')
672
- continue
673
-
674
- loader = SheetLoader.create(opt_load, file_path)
675
- df_roteiros = include_roteiro(df, col_uc=col_uc, tb=TableAnalitico(loader.load()))
676
-
677
- df_ok = self._check_ok_roteiro(df_roteiros)
678
- if not df_ok.empty:
679
- self._counter += 1
680
- save_file = self.temp_dir.join_file(f'temp-{self._counter}.xlsx')
681
- df_ok.to_excel(save_file.absolute())
682
- self.temp_files.append(save_file)
683
-
684
- df_void = self._check_void_roteiro(df_roteiros)
685
- if df_void.empty:
686
- break
687
-
688
- self.current_base = df_void
689
- self.join_all_files()
599
+ #===================================================================#
600
+ # Organizar arquivos no disco
601
+ #===================================================================#
690
602
 
603
+ class OrganizableDocuments(ABC):
604
+
605
+ def __init__(self) -> None:
606
+ super().__init__()
607
+
691
608
 
692
609
  class DocumentsToTable(object):
693
610
 
@@ -735,8 +652,8 @@ class DocumentsToTable(object):
735
652
  ENUM_COLUMNS_TOI.TIPO_DOC: ArrayList(),
736
653
  }
737
654
 
738
- names: ArrayList[MapNameDocument] = MapNameDocument.create_list_map_names(self.src)
739
- name: MapNameDocument
655
+ names: ArrayList[NameDocument] = NameDocument.create_list_map_names(self.src)
656
+ name: NameDocument
740
657
  for name in names:
741
658
 
742
659
  local = "OUTRO"
@@ -803,8 +720,8 @@ class DocumentsToTable(object):
803
720
 
804
721
  def organize(self) -> None:
805
722
 
806
- names: ArrayList[MapNameDocument] = MapNameDocument.create_list_map_names(self.src)
807
- name: MapNameDocument
723
+ names: ArrayList[NameDocument] = NameDocument.create_list_map_names(self.src)
724
+ name: NameDocument
808
725
 
809
726
  for name in names:
810
727
  output_dir = self.src.concat(name.year).concat(name.mon).concat(name.day)
@@ -812,7 +729,23 @@ class DocumentsToTable(object):
812
729
  final_path = output_dir.join_file(name.path.basename())
813
730
 
814
731
  self._move(name.path.absolute(), final_path.absolute())
732
+
733
+
734
+ #===================================================================#
735
+ # Organizar arquivos do WhatsApp
736
+ #===================================================================#
737
+
738
+ class WhatsAppFiles(object):
739
+
740
+ def __init__(self, src: Directory) -> None:
741
+ self.src: Directory = src
815
742
 
743
+ def organize_by_types(self, output: Directory) -> None:
744
+ pass
745
+
746
+ def organize_by_date(self, output: Directory) -> None:
747
+ pass
748
+
816
749
 
817
750
  def main():
818
751
  pass
@@ -9,7 +9,7 @@ from table_stream.thread import (
9
9
 
10
10
  import pandas as pd
11
11
 
12
- from converter.lib_toi import DocumentsToTable, MapIdDocuments, MapNameDocument
12
+ from converter.lib_toi import DocumentsToTable, MapIdDocuments, NameDocument
13
13
 
14
14
 
15
15
  class ModelForms(object):
@@ -0,0 +1,40 @@
1
+ import io
2
+ import pandas as pd
3
+
4
+ def load_whats_csv(f: str) -> pd.DataFrame:
5
+
6
+ # Lê o arquivo direto definindo o LRM (\u200e) ou o '***' como separador
7
+ # O engine='python' é necessário para separadores com regex ou múltiplos caracteres
8
+ df = pd.read_csv(
9
+ f,
10
+ sep=r"\u200e|\*\*\*", # Aceita tanto \u200e quanto *** como delimitador
11
+ engine="python",
12
+ )
13
+
14
+ return df
15
+
16
+
17
+ def test():
18
+ from table_stream.sysfiles import File, Directory, UserFileSystem
19
+ from table_stream.sysfiles import JsonConvert, JsonData
20
+ from converter.lib_toi import DocumentsToTable
21
+
22
+ wp_name = "WhatsApp Image 2026-09-02 at 14.59.45"
23
+ name = 'IMG-20260406-WA0045.jpg'
24
+ list_name = name.split('-')
25
+
26
+ f = "/home/bruno/Documentos/CARTAS TOI/WP CARTAS TOI RURAL/Conversa do WhatsApp com CARTAS TOI RURAL.txt"
27
+ out = "/home/bruno/Documentos/CARTAS TOI/WP CARTAS TOI RURAL/final.xlsx"
28
+ data = load_whats_csv(f)
29
+ print(data.head())
30
+
31
+ data.to_excel(out)
32
+
33
+
34
+ def main() -> None:
35
+ from converter import run_ui
36
+ #run_ui()
37
+ test()
38
+
39
+ if __name__ == '__main__':
40
+ main()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyflutter_app"
3
- version = "0.1.7"
3
+ version = "0.1.8"
4
4
  description = "Bibliotecas Python para conversão de documentos, interface e processamento de tabelas."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -9,4 +9,8 @@ from .sheet import (
9
9
  csvEncoding, csvSeparator, OptionsIOSheet, SaveData,
10
10
  FactorySaveData, FactorySheetLoader, SheetType,
11
11
  )
12
+ from .sysfiles import (
13
+ File, Directory, ContentFiles, JsonConvert, JsonData,
14
+ EnumDocuments, ExtensionDocuments, UserFileSystem, PathElement
15
+ )
12
16
  from .state import ContextState, EventState
@@ -48,6 +48,10 @@ class ArrayList[T](list):
48
48
  return True
49
49
  return False
50
50
 
51
+ @classmethod
52
+ def from_string(cls, value: str, separator: str = " ") -> ArrayList[str]:
53
+ return cls(value.split(separator))
54
+
51
55
 
52
56
  #========================================================#
53
57
  # Base para HashMap().
@@ -8,9 +8,11 @@ Atualizações:
8
8
  from __future__ import annotations
9
9
  from typing import Any
10
10
  from enum import Enum
11
+ from abc import ABC, abstractmethod
11
12
  import os
12
13
  import json
13
14
  import platform
15
+ from table_stream.hash_map import ArrayList
14
16
 
15
17
  # Windows / Linux / ...
16
18
  KERNEL_TYPE = platform.system()
@@ -70,12 +72,42 @@ class EnumDocuments(Enum):
70
72
  ]
71
73
 
72
74
 
73
- class File(object):
74
- def __init__(self, filename: str):
75
- if os.path.isdir(filename):
76
- raise ValueError(f'{__class__.__name__} File() não pode ser um diretório.')
77
- self._abs_filename: str = os.path.abspath(filename)
75
+ class PathElement(ABC):
76
+
77
+ def __init__(self, path: str) -> None:
78
+ super().__init__()
79
+ self._path: str = os.path.abspath(path)
80
+
81
+ @abstractmethod
82
+ def basename(self) -> str:
83
+ pass
84
+
85
+ @abstractmethod
86
+ def name(self) -> str:
87
+ pass
88
+
89
+ @abstractmethod
90
+ def absolute(self) -> str:
91
+ pass
92
+
93
+ @abstractmethod
94
+ def is_file(self) -> bool:
95
+ pass
96
+
97
+ @abstractmethod
98
+ def is_dir(self) -> bool:
99
+ pass
100
+
78
101
 
102
+
103
+ class File(PathElement):
104
+
105
+ def __init__(self, path: str) -> None:
106
+ super().__init__(path)
107
+
108
+ if os.path.isdir(path):
109
+ raise ValueError(f'{__class__.__name__} File() não pode ser um diretório.')
110
+
79
111
  def __repr__(self):
80
112
  return f'<{__class__.__name__}()>: {self.absolute()}'
81
113
 
@@ -149,14 +181,14 @@ class File(object):
149
181
  def name(self):
150
182
  e = self.extension()
151
183
  if (e is None) or (e == ''):
152
- return os.path.basename(self._abs_filename)
153
- return os.path.basename(self._abs_filename).replace(e, '')
184
+ return os.path.basename(self._path)
185
+ return os.path.basename(self._path).replace(e, '')
154
186
 
155
187
  def name_absolute(self) -> str:
156
188
  e = self.extension()
157
189
  if (e is None) or (e == ''):
158
- return self._abs_filename
159
- return self._abs_filename.replace(e, '')
190
+ return self._path
191
+ return self._path.replace(e, '')
160
192
 
161
193
  def extension(self) -> str | None:
162
194
  try:
@@ -165,28 +197,38 @@ class File(object):
165
197
  return None
166
198
 
167
199
  def dirname(self) -> str:
168
- return os.path.dirname(self._abs_filename)
200
+ return os.path.dirname(self._path)
169
201
 
170
202
  def basename(self) -> str:
171
- return os.path.basename(self._abs_filename)
203
+ return os.path.basename(self._path)
172
204
 
173
205
  def exists(self) -> bool:
174
206
  return os.path.exists(self.absolute())
175
207
 
176
208
  def absolute(self) -> str:
177
- return self._abs_filename
209
+ return self._path
178
210
 
179
211
  def size(self):
180
- return os.path.getsize(self._abs_filename)
212
+ return os.path.getsize(self._path)
181
213
 
214
+ def is_file(self) -> bool:
215
+ return True
182
216
 
183
- class Directory(object):
217
+ def is_dir(self) -> bool:
218
+ return False
219
+
220
+
221
+
222
+ class Directory(PathElement):
184
223
 
185
- def __init__(self, dirpath: str):
186
- self.__abs_path: str = os.path.abspath(dirpath)
224
+ def __init__(self, path: str) -> None:
225
+ super().__init__(path)
226
+
227
+ if os.path.isfile(path):
228
+ raise ValueError(f'{__class__.__name__}() não pode ser um arquivo.')
187
229
 
188
230
  def absolute(self) -> str:
189
- return self.__abs_path
231
+ return self._path
190
232
 
191
233
  def __repr__(self):
192
234
  return f'{__class__.__name__}(): {self.absolute()}'
@@ -199,11 +241,11 @@ class Directory(object):
199
241
  def __hash__(self):
200
242
  return self.absolute().__hash__()
201
243
 
202
- def get_files(self) -> list[str]:
244
+ def get_files(self) -> ArrayList[str]:
203
245
  """
204
246
  Retorna a lista de diretórios e sub pastas.
205
247
  """
206
- path_list = []
248
+ path_list = ArrayList()
207
249
  # os.walk percorre a árvore de diretórios recursivamente
208
250
  for root, dirs, files in os.walk(self.absolute()):
209
251
  # Adiciona os diretórios encontrados
@@ -214,9 +256,10 @@ class Directory(object):
214
256
  path_list.append(os.path.join(root, name))
215
257
  return path_list
216
258
 
217
- def __content_recursive(self) -> list[File]:
218
- _paths: list[str] = self.get_files()
219
- values: list[File] = []
259
+ def __content_recursive(self) -> ArrayList[File]:
260
+ _paths: ArrayList[str] = self.get_files()
261
+ values: ArrayList[File] = ArrayList()
262
+
220
263
  for file_path in _paths:
221
264
  if os.path.isfile(file_path):
222
265
  values.append(
@@ -224,9 +267,10 @@ class Directory(object):
224
267
  )
225
268
  return values
226
269
 
227
- def __content_no_recursive(self) -> list[File]:
228
- content_files: list[str] = os.listdir(self.absolute())
229
- values: list[File] = []
270
+ def __content_no_recursive(self) -> ArrayList[File]:
271
+ content_files: ArrayList[str] = ArrayList(os.listdir(self.absolute()))
272
+ values: ArrayList[File] = ArrayList()
273
+
230
274
  for file in content_files:
231
275
  fp: str = os.path.join(self.absolute(), file)
232
276
  if os.path.isfile(fp):
@@ -235,18 +279,20 @@ class Directory(object):
235
279
  )
236
280
  return values
237
281
 
238
- def content_files(self, *, recursive: bool = True) -> list[File]:
282
+ def content_files(self, *, recursive: bool = True) -> ArrayList[File]:
239
283
  if recursive:
240
284
  return self.__content_recursive()
241
285
  return self.__content_no_recursive()
242
286
 
243
- def content_dirs(self, recursive: bool = True) -> list[Directory]:
287
+ def content_dirs(self, recursive: bool = True) -> ArrayList[Directory]:
244
288
  """
245
289
  retorna uma lista de diretórios
246
290
  """
247
- values: list[Directory] = []
291
+ values: ArrayList[Directory] = ArrayList()
292
+
248
293
  if recursive:
249
294
  _paths = self.get_files()
295
+
250
296
  for p in _paths:
251
297
  if os.path.isdir(p):
252
298
  values.append(
@@ -290,6 +336,16 @@ class Directory(object):
290
336
  os.path.join(self.absolute(), name)
291
337
  )
292
338
 
339
+ def name(self) -> str:
340
+ return self.basename()
341
+
342
+ def is_file(self) -> bool:
343
+ return False
344
+
345
+ def is_dir(self) -> bool:
346
+ return True
347
+
348
+
293
349
 
294
350
  class ContentFiles(object):
295
351
  """
@@ -302,23 +358,24 @@ class ContentFiles(object):
302
358
  self._input_dir: Directory = d
303
359
  self.__max_files: int = max_files
304
360
 
305
- def get_images(self) -> list[File]:
306
- return self.get_files(file_type=EnumDocuments.IMAGE)
361
+ def get_images(self, recursive: bool = True) -> ArrayList[File]:
362
+ return self.get_files(file_type=EnumDocuments.IMAGE, recursive=recursive)
307
363
 
308
- def get_pdfs(self) -> list[File]:
309
- return self.get_files(file_type=EnumDocuments.PDF)
364
+ def get_pdfs(self, recursive: bool = True) -> ArrayList[File]:
365
+ return self.get_files(file_type=EnumDocuments.PDF, recursive=recursive)
310
366
 
311
- def get_sheets(self) -> list[File]:
312
- return self.get_files(file_type=EnumDocuments.SHEET)
367
+ def get_sheets(self, recursive: bool = True) -> ArrayList[File]:
368
+ return self.get_files(file_type=EnumDocuments.SHEET, recursive=recursive)
313
369
 
314
- def get_files_with(self, *, infile: str, sort: bool = True) -> list[File]:
370
+ def get_files_with(self, *, infile: str, sort: bool = True) -> ArrayList[File]:
315
371
  """
316
372
  Retorna arquivos que contém a ocorrência (infile) no nome absoluto.
317
373
  """
318
- content_files: list[File] = []
374
+ content_files: ArrayList[File] = ArrayList()
319
375
  count: int = 0
320
376
  paths: list[str] = self._input_dir.get_files()
321
377
  file: str
378
+
322
379
  for file in paths:
323
380
  if not os.path.isfile(file):
324
381
  continue
@@ -398,7 +455,7 @@ class ContentFiles(object):
398
455
  file_type: EnumDocuments = EnumDocuments.ALL_DOCUMENTS,
399
456
  sort: bool = True,
400
457
  recursive: bool = True
401
- ) -> list[File]:
458
+ ) -> ArrayList[File]:
402
459
  """
403
460
  Retorna uma lista de File() conforme o tipo de arquivo
404
461
  especificado.
@@ -408,8 +465,8 @@ class ContentFiles(object):
408
465
 
409
466
  """
410
467
  if recursive:
411
- return self.__get_files_recursive(file_type=file_type, sort=sort)
412
- return self.__get_files_no_recursive(file_type=file_type, sort=sort)
468
+ return ArrayList(self.__get_files_recursive(file_type=file_type, sort=sort))
469
+ return ArrayList(self.__get_files_no_recursive(file_type=file_type, sort=sort))
413
470
 
414
471
 
415
472
  class JsonData(object):
@@ -35,7 +35,7 @@ name = "cffi"
35
35
  version = "2.1.1"
36
36
  source = { registry = "https://pypi.org/simple" }
37
37
  dependencies = [
38
- { name = "pycparser", marker = "implementation_name != 'PyPy' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
38
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
39
39
  ]
40
40
  sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
41
41
  wheels = [
@@ -169,7 +169,7 @@ name = "cryptography"
169
169
  version = "50.0.1"
170
170
  source = { registry = "https://pypi.org/simple" }
171
171
  dependencies = [
172
- { name = "cffi", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
172
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
173
173
  ]
174
174
  sdist = { url = "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz", hash = "sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", size = 880381, upload-time = "2026-08-25T19:45:45.499Z" }
175
175
  wheels = [
@@ -454,7 +454,7 @@ wheels = [
454
454
 
455
455
  [[package]]
456
456
  name = "pyflutter-app"
457
- version = "0.1.7"
457
+ version = "0.1.8"
458
458
  source = { editable = "." }
459
459
  dependencies = [
460
460
  { name = "openpyxl" },
@@ -586,8 +586,8 @@ name = "secretstorage"
586
586
  version = "3.5.0"
587
587
  source = { registry = "https://pypi.org/simple" }
588
588
  dependencies = [
589
- { name = "cryptography", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
590
- { name = "jeepney", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
589
+ { name = "cryptography" },
590
+ { name = "jeepney" },
591
591
  ]
592
592
  sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
593
593
  wheels = [
@@ -1,21 +0,0 @@
1
-
2
- def test():
3
- from table_stream.sysfiles import File, Directory, UserFileSystem
4
- from table_stream.sysfiles import JsonConvert, JsonData
5
- from converter.lib_toi import DocumentsToTable
6
-
7
- d = Directory("/home/bruno/Downloads/2026-08-25 TOIS (TARDE)/POLO 2/GM E NM/OK LANÇADO FALTA PLANILHAR")
8
- f = File("/home/bruno/Downloads/RFA/cartas-toi.xlsx")
9
-
10
- conv = DocumentsToTable(d)
11
- conv.save(f)
12
-
13
-
14
-
15
- def main() -> None:
16
- from converter import run_ui
17
- run_ui()
18
- #test()
19
-
20
- if __name__ == '__main__':
21
- main()
File without changes
File without changes