soup-files 1.2__tar.gz → 1.2.2__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.
- {soup_files-1.2 → soup_files-1.2.2}/PKG-INFO +1 -1
- {soup_files-1.2 → soup_files-1.2.2}/pyproject.toml +2 -2
- soup_files-1.2.2/soup_files/__init__.py +16 -0
- soup_files-1.2.2/soup_files/__main__.py +14 -0
- soup_files-1.2.2/soup_files/files.py +473 -0
- soup_files-1.2.2/soup_files/progress.py +108 -0
- soup_files-1.2.2/soup_files/version.py +3 -0
- {soup_files-1.2 → soup_files-1.2.2}/soup_files.egg-info/PKG-INFO +1 -1
- soup_files-1.2.2/soup_files.egg-info/SOURCES.txt +11 -0
- soup_files-1.2.2/soup_files.egg-info/top_level.txt +1 -0
- soup_files-1.2/soup_files.egg-info/SOURCES.txt +0 -6
- soup_files-1.2/soup_files.egg-info/top_level.txt +0 -1
- {soup_files-1.2 → soup_files-1.2.2}/README.md +0 -0
- {soup_files-1.2 → soup_files-1.2.2}/setup.cfg +0 -0
- {soup_files-1.2 → soup_files-1.2.2}/soup_files.egg-info/dependency_links.txt +0 -0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "soup_files"
|
7
|
-
version = "1.2"
|
7
|
+
version = "1.2.2"
|
8
8
|
authors = [
|
9
9
|
{ name="Bruno Chaves"},
|
10
10
|
]
|
@@ -18,5 +18,5 @@ dependencies = [
|
|
18
18
|
|
19
19
|
[tool.setuptools]
|
20
20
|
packages = [
|
21
|
-
|
21
|
+
"soup_files"
|
22
22
|
]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
from .version import __version__
|
4
|
+
from .progress import ProgressBarAdapter, ProgressBarSimple, ABCProgressBar
|
5
|
+
from .files import (
|
6
|
+
File,
|
7
|
+
Directory,
|
8
|
+
JsonData,
|
9
|
+
JsonConvert,
|
10
|
+
InputFiles,
|
11
|
+
LibraryDocs,
|
12
|
+
UserFileSystem,
|
13
|
+
UserAppDir,
|
14
|
+
KERNEL_TYPE,
|
15
|
+
)
|
16
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
from soup_files import __version__
|
3
|
+
|
4
|
+
def test():
|
5
|
+
from soup_files import File, Directory, UserFileSystem, LibraryDocs, InputFiles
|
6
|
+
pass
|
7
|
+
|
8
|
+
|
9
|
+
def main():
|
10
|
+
print(f' soup_files - versão: {__version__}')
|
11
|
+
test()
|
12
|
+
|
13
|
+
if __name__ == '__main__':
|
14
|
+
main()
|
@@ -0,0 +1,473 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
#
|
3
|
+
"""
|
4
|
+
Esté módulo serve para manipulação de arquivos, diretórios e documentos
|
5
|
+
entre outros. Não depende de módulos externos apenas de builtins e stdlib.
|
6
|
+
"""
|
7
|
+
from __future__ import annotations
|
8
|
+
from typing import List, Dict
|
9
|
+
from enum import Enum
|
10
|
+
import os
|
11
|
+
import json
|
12
|
+
import platform
|
13
|
+
from pathlib import Path
|
14
|
+
from hashlib import md5
|
15
|
+
|
16
|
+
|
17
|
+
# Windows / Linux / ...
|
18
|
+
KERNEL_TYPE = platform.system()
|
19
|
+
|
20
|
+
|
21
|
+
class LibraryDocs(Enum):
|
22
|
+
|
23
|
+
IMAGE = ['.png', '.jpg', '.jpeg', '.svg']
|
24
|
+
PDF = ['.pdf']
|
25
|
+
DOCUMENTS = ['.png', '.jpg', '.jpeg', '.svg', '.pdf']
|
26
|
+
#
|
27
|
+
EXCEL = ['.xlsx']
|
28
|
+
CSV = ['.csv', '.txt']
|
29
|
+
#
|
30
|
+
SHEET = ['.csv', '.txt', '.xlsx', '.xls']
|
31
|
+
JSON = ['.json']
|
32
|
+
#
|
33
|
+
ALL_DOCUMENTS = [
|
34
|
+
'.png', '.jpg', '.jpeg', '.svg',
|
35
|
+
'.csv', '.txt', '.xlsx',
|
36
|
+
'.pdf',
|
37
|
+
'.json',
|
38
|
+
]
|
39
|
+
#
|
40
|
+
ALL = None
|
41
|
+
|
42
|
+
|
43
|
+
class File(object):
|
44
|
+
def __init__(self, filename: str):
|
45
|
+
if os.path.isdir(filename):
|
46
|
+
raise ValueError(f'{__class__.__name__} File() não pode ser um diretório.')
|
47
|
+
self.filename: str = os.path.abspath(filename)
|
48
|
+
self.__path: Path = Path(self.filename)
|
49
|
+
|
50
|
+
@property
|
51
|
+
def path(self) -> Path:
|
52
|
+
return self.__path
|
53
|
+
|
54
|
+
@path.setter
|
55
|
+
def path(self, new:Path):
|
56
|
+
if not isinstance(new, Path):
|
57
|
+
return
|
58
|
+
self.__path = new
|
59
|
+
|
60
|
+
def __eq__(self, value):
|
61
|
+
if not isinstance(value, File):
|
62
|
+
return NotImplemented
|
63
|
+
return self.absolute() == value.absolute()
|
64
|
+
|
65
|
+
def __hash__(self):
|
66
|
+
return self.absolute().__hash__()
|
67
|
+
|
68
|
+
def is_image(self) -> bool:
|
69
|
+
try:
|
70
|
+
return True if self.extension() in LibraryDocs.IMAGE.value else False
|
71
|
+
except:
|
72
|
+
return False
|
73
|
+
|
74
|
+
def is_pdf(self) -> bool:
|
75
|
+
try:
|
76
|
+
return True if self.extension() in LibraryDocs.PDF.value else False
|
77
|
+
except:
|
78
|
+
return False
|
79
|
+
|
80
|
+
def is_excel(self) -> bool:
|
81
|
+
try:
|
82
|
+
return True if self.extension() in LibraryDocs.EXCEL.value else False
|
83
|
+
except:
|
84
|
+
return False
|
85
|
+
|
86
|
+
def is_csv(self) -> bool:
|
87
|
+
try:
|
88
|
+
return True if self.extension() in LibraryDocs.CSV.value else False
|
89
|
+
except:
|
90
|
+
return False
|
91
|
+
|
92
|
+
def is_sheet(self) -> bool:
|
93
|
+
try:
|
94
|
+
return True if self.extension() in LibraryDocs.SHEET.value else False
|
95
|
+
except:
|
96
|
+
return False
|
97
|
+
|
98
|
+
def update_extension(self, e: str) -> File:
|
99
|
+
"""
|
100
|
+
Retorna uma instância de File() no mesmo diretório com a nova
|
101
|
+
extensão informada.
|
102
|
+
"""
|
103
|
+
current = self.extension()
|
104
|
+
full_path = self.absolute().replace(current, '')
|
105
|
+
return File(os.path.join(f'{full_path}{e}'))
|
106
|
+
|
107
|
+
def get_text(self) -> str | None:
|
108
|
+
try:
|
109
|
+
return self.__path.read_text()
|
110
|
+
except Exception as e:
|
111
|
+
print(e)
|
112
|
+
return None
|
113
|
+
|
114
|
+
def write_string(self, s:str):
|
115
|
+
self.__path.write_text(s)
|
116
|
+
|
117
|
+
def write_list(self, items: List[str]):
|
118
|
+
# Abrindo o arquivo em modo de escrita
|
119
|
+
with open(self.filename, "w", encoding="utf-8") as file:
|
120
|
+
for string in items:
|
121
|
+
file.write(string + "\n") # Adiciona uma quebra de linha após cada string
|
122
|
+
|
123
|
+
def name(self):
|
124
|
+
e = self.extension()
|
125
|
+
if (e is None) or (e == ''):
|
126
|
+
return os.path.basename(self.filename)
|
127
|
+
return os.path.basename(self.filename).replace(e, '')
|
128
|
+
|
129
|
+
def name_absolute(self) -> str:
|
130
|
+
e = self.extension()
|
131
|
+
if (e is None) or (e == ''):
|
132
|
+
return self.filename
|
133
|
+
return self.filename.replace(e, '')
|
134
|
+
|
135
|
+
def extension(self) -> str:
|
136
|
+
return self.__path.suffix
|
137
|
+
|
138
|
+
def dirname(self) -> str:
|
139
|
+
return os.path.dirname(self.filename)
|
140
|
+
|
141
|
+
def basename(self) -> str:
|
142
|
+
return os.path.basename(self.filename)
|
143
|
+
|
144
|
+
def exists(self) -> bool:
|
145
|
+
return self.__path.exists()
|
146
|
+
|
147
|
+
def absolute(self) -> str:
|
148
|
+
return self.filename
|
149
|
+
|
150
|
+
def size(self):
|
151
|
+
return os.path.getsize(self.filename)
|
152
|
+
|
153
|
+
def md5(self) -> str | None:
|
154
|
+
"""Retorna a hash md5 de um arquivo se ele existir no disco."""
|
155
|
+
if not self.path.exists():
|
156
|
+
return None
|
157
|
+
_hash_md5 = md5()
|
158
|
+
with open(self.absolute(), "rb") as f:
|
159
|
+
for _block in iter(lambda: f.read(4096), b""):
|
160
|
+
_hash_md5.update(_block)
|
161
|
+
return _hash_md5.hexdigest()
|
162
|
+
|
163
|
+
|
164
|
+
class Directory(object):
|
165
|
+
def __init__(self, dirpath:str):
|
166
|
+
self.dirpath: str = os.path.abspath(dirpath)
|
167
|
+
self.path: Path = Path(self.dirpath)
|
168
|
+
|
169
|
+
def __eq__(self, value):
|
170
|
+
if not isinstance(value, Directory):
|
171
|
+
return NotImplemented
|
172
|
+
return self.absolute() == value.absolute()
|
173
|
+
|
174
|
+
def __hash__(self):
|
175
|
+
return self.absolute().__hash__()
|
176
|
+
|
177
|
+
def iterpaths(self) -> List[Path]:
|
178
|
+
return self.path.rglob('*')
|
179
|
+
|
180
|
+
def __content_recursive(self) -> List[File]:
|
181
|
+
_paths = self.iterpaths()
|
182
|
+
values = []
|
183
|
+
for p in _paths:
|
184
|
+
if p.is_file():
|
185
|
+
values.append(
|
186
|
+
File(
|
187
|
+
os.path.abspath(p.absolute())
|
188
|
+
)
|
189
|
+
)
|
190
|
+
return values
|
191
|
+
|
192
|
+
def __content_no_recursive(self) -> List[File]:
|
193
|
+
content_files: List[str] = os.listdir(self.absolute())
|
194
|
+
values: List[File] = []
|
195
|
+
for file in content_files:
|
196
|
+
fp: str = os.path.join(self.absolute(), file)
|
197
|
+
if os.path.isfile(fp):
|
198
|
+
values.append(
|
199
|
+
File(os.path.abspath(fp))
|
200
|
+
)
|
201
|
+
return values
|
202
|
+
|
203
|
+
def content_files(self, *, recursive: bool = True) -> List[File]:
|
204
|
+
if recursive:
|
205
|
+
return self.__content_recursive()
|
206
|
+
return self.__content_no_recursive()
|
207
|
+
|
208
|
+
def content_dirs(self, recursive: bool = True) -> List[Directory]:
|
209
|
+
values: List[Directory] = []
|
210
|
+
if recursive:
|
211
|
+
_paths = self.iterpaths()
|
212
|
+
for p in _paths:
|
213
|
+
if p.is_dir():
|
214
|
+
values.append(
|
215
|
+
Directory(os.path.abspath(p.absolute()))
|
216
|
+
)
|
217
|
+
else:
|
218
|
+
_paths = os.listdir(self.absolute())
|
219
|
+
for d in _paths:
|
220
|
+
_dirpath = os.path.join(self.absolute(), d)
|
221
|
+
if os.path.isdir(_dirpath):
|
222
|
+
values.append(
|
223
|
+
Directory(os.path.abspath(_dirpath))
|
224
|
+
)
|
225
|
+
return values
|
226
|
+
|
227
|
+
def basename(self) -> str:
|
228
|
+
return os.path.basename(self.absolute())
|
229
|
+
|
230
|
+
def mkdir(self):
|
231
|
+
try:
|
232
|
+
os.makedirs(self.absolute())
|
233
|
+
except:
|
234
|
+
pass
|
235
|
+
|
236
|
+
def absolute(self) -> str:
|
237
|
+
return self.dirpath
|
238
|
+
|
239
|
+
def concat(self, d:str, create:bool=False) -> Directory:
|
240
|
+
if create == True:
|
241
|
+
try:
|
242
|
+
os.makedirs(os.path.join(self.absolute(), d))
|
243
|
+
except:
|
244
|
+
pass
|
245
|
+
return Directory(
|
246
|
+
os.path.join(self.absolute(), d)
|
247
|
+
)
|
248
|
+
|
249
|
+
def parent(self) -> Directory:
|
250
|
+
return Directory(
|
251
|
+
os.path.abspath(self.path.parent)
|
252
|
+
)
|
253
|
+
|
254
|
+
def join_file(self, name:str) -> File:
|
255
|
+
return File(
|
256
|
+
os.path.join(self.absolute(), name)
|
257
|
+
)
|
258
|
+
|
259
|
+
|
260
|
+
class InputFiles(object):
|
261
|
+
"""
|
262
|
+
Obeter uma lista de arquivos/documentos do diretório informado.
|
263
|
+
"""
|
264
|
+
def __init__(self, d: Directory, *, maxFiles: int = 5000):
|
265
|
+
if not isinstance(d, Directory):
|
266
|
+
raise ValueError(f'{__class__.__name__}\nUse: Directory(), não {type(d)}')
|
267
|
+
self.input_dir: Directory = d
|
268
|
+
self.maxFiles: int = maxFiles
|
269
|
+
|
270
|
+
def get_files_with(self, *, infile: str, sort: bool = True) -> List[File]:
|
271
|
+
"""
|
272
|
+
Retorna arquivos que contém a ocorrência (infile) no nome absoluto.
|
273
|
+
"""
|
274
|
+
content_files: List[File] = []
|
275
|
+
count: int = 0
|
276
|
+
paths = self.input_dir.iterpaths()
|
277
|
+
for file in paths:
|
278
|
+
if not file.is_file():
|
279
|
+
continue
|
280
|
+
if infile in os.path.abspath(file.absolute()):
|
281
|
+
content_files.append(
|
282
|
+
File(os.path.abspath(file.absolute()))
|
283
|
+
)
|
284
|
+
count += 1
|
285
|
+
if count >= self.maxFiles:
|
286
|
+
break
|
287
|
+
return content_files
|
288
|
+
|
289
|
+
def __get_files_recursive(self, *, file_type: LibraryDocs, sort: bool) -> List[File]:
|
290
|
+
#
|
291
|
+
_paths: List[Path] = self.input_dir.iterpaths()
|
292
|
+
_all_files = []
|
293
|
+
count: int = 0
|
294
|
+
if file_type == LibraryDocs.ALL:
|
295
|
+
# Todos os tipos de arquivos
|
296
|
+
for p in _paths:
|
297
|
+
if not p.is_file():
|
298
|
+
continue
|
299
|
+
_all_files.append(
|
300
|
+
File(os.path.abspath(p.absolute()))
|
301
|
+
)
|
302
|
+
count += 1
|
303
|
+
if count >= self.maxFiles:
|
304
|
+
break
|
305
|
+
else:
|
306
|
+
# Arquivos especificados em LibraryDocs
|
307
|
+
for p in _paths:
|
308
|
+
if not p.is_file():
|
309
|
+
continue
|
310
|
+
if (p.suffix is None) or (p.suffix == ''):
|
311
|
+
continue
|
312
|
+
if p.suffix in file_type.value:
|
313
|
+
_all_files.append(
|
314
|
+
File(os.path.abspath(p.absolute()))
|
315
|
+
)
|
316
|
+
count += 1
|
317
|
+
if count >= self.maxFiles:
|
318
|
+
break
|
319
|
+
if sort:
|
320
|
+
_all_files.sort(key=File.absolute)
|
321
|
+
return _all_files
|
322
|
+
|
323
|
+
def __get_files_no_recursive(self, *, file_type: LibraryDocs, sort: bool) -> List[File]:
|
324
|
+
_content_files: List[File] = self.input_dir.content_files(recursive=False)
|
325
|
+
|
326
|
+
_all_files: List[File] = []
|
327
|
+
count: int = 0
|
328
|
+
if file_type == LibraryDocs.ALL:
|
329
|
+
# Todos os tipos de arquivos
|
330
|
+
for file in _content_files:
|
331
|
+
_all_files.append(file)
|
332
|
+
count += 1
|
333
|
+
if count == self.maxFiles:
|
334
|
+
break
|
335
|
+
else:
|
336
|
+
# Arquivos especificados em LibraryDocs
|
337
|
+
for file in _content_files:
|
338
|
+
if file.extension() in file_type.value:
|
339
|
+
_all_files.append(file)
|
340
|
+
count += 1
|
341
|
+
if count == self.maxFiles:
|
342
|
+
break
|
343
|
+
if sort:
|
344
|
+
_all_files.sort(key=File.absolute)
|
345
|
+
return _all_files
|
346
|
+
|
347
|
+
def get_files(
|
348
|
+
self, *,
|
349
|
+
file_type: LibraryDocs = LibraryDocs.ALL_DOCUMENTS,
|
350
|
+
sort: bool = True,
|
351
|
+
recursive: bool = True
|
352
|
+
) -> List[File]:
|
353
|
+
"""
|
354
|
+
Retorna uma lista de File() de acordo com o tipo de arquivo
|
355
|
+
especificado.
|
356
|
+
- LibraryDocs.ALL_DOCUMENTS => Retorna todos os documentos do diretório.
|
357
|
+
- LibraryDocs.EXCEL => Retorna arquivos que são planilhas excel.
|
358
|
+
- ...
|
359
|
+
|
360
|
+
"""
|
361
|
+
if recursive:
|
362
|
+
return self.__get_files_recursive(file_type=file_type, sort=sort)
|
363
|
+
return self.__get_files_no_recursive(file_type=file_type, sort=sort)
|
364
|
+
|
365
|
+
|
366
|
+
class JsonData(object):
|
367
|
+
"""
|
368
|
+
Representação de um dado JSON apartir de uma string python.
|
369
|
+
"""
|
370
|
+
def __init__(self, string:str):
|
371
|
+
if not isinstance(string, str):
|
372
|
+
raise ValueError(f'{__class__.__name__} o JSON informado precisa ser do tipo string, não {type(string)}')
|
373
|
+
self.jsonString:str = string
|
374
|
+
|
375
|
+
def is_null(self) -> bool:
|
376
|
+
if (self.jsonString is None) or (self.jsonString == ''):
|
377
|
+
return True
|
378
|
+
return False
|
379
|
+
|
380
|
+
def to_string(self) -> str:
|
381
|
+
return self.jsonString
|
382
|
+
|
383
|
+
def to_dict(self) -> Dict[str, object]:
|
384
|
+
"""
|
385
|
+
Exportar/Converter o dado atual em um dicionário python.
|
386
|
+
"""
|
387
|
+
return json.loads(self.jsonString)
|
388
|
+
|
389
|
+
def to_file(self, f:File):
|
390
|
+
"""Exportar o dado atual para um arquivo .json"""
|
391
|
+
_data:str = json.loads(self.jsonString)
|
392
|
+
with open(f.absolute(), "w", encoding="utf-8") as file:
|
393
|
+
json.dump(_data, file, indent=4, ensure_ascii=False)
|
394
|
+
|
395
|
+
|
396
|
+
class JsonConvert(object):
|
397
|
+
"""
|
398
|
+
Conversão de um dado JSON em dados python
|
399
|
+
"""
|
400
|
+
def __init__(self, jsonData: JsonData):
|
401
|
+
self.jsonData: JsonData = jsonData
|
402
|
+
|
403
|
+
def to_json_data(self) -> JsonData:
|
404
|
+
return self.jsonData
|
405
|
+
|
406
|
+
@classmethod
|
407
|
+
def from_file(cls, file:File) -> JsonConvert:
|
408
|
+
"""
|
409
|
+
Gerar um dado JsonData apartir de um arquivo .json
|
410
|
+
"""
|
411
|
+
# Ler o arquivo e carregar o JSON em um dicionário Python
|
412
|
+
data = None
|
413
|
+
try:
|
414
|
+
with open(file.absolute(), "r", encoding="utf-8") as fp:
|
415
|
+
data:str = json.load(fp)
|
416
|
+
except Exception as e:
|
417
|
+
print(f'{__class__.__name__}\n{e}')
|
418
|
+
return cls(JsonData(''))
|
419
|
+
else:
|
420
|
+
#return JsonData(json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True))
|
421
|
+
return cls(JsonData(json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)))
|
422
|
+
|
423
|
+
@classmethod
|
424
|
+
def from_string_json(cls, data:str) -> JsonConvert:
|
425
|
+
"""
|
426
|
+
Gerar um dado JsonData apartir de uma string.
|
427
|
+
"""
|
428
|
+
json_string = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
|
429
|
+
return cls(JsonData(json_string))
|
430
|
+
|
431
|
+
@classmethod
|
432
|
+
def from_dict(cls, data:Dict[str, object]) -> JsonConvert:
|
433
|
+
"""
|
434
|
+
Converte um dicionário em objeto JSON/JsonData.
|
435
|
+
"""
|
436
|
+
if not isinstance(data, dict):
|
437
|
+
raise ValueError(f'{__class__.__name__} Informe um JSON em formato dict, não {type(data)}')
|
438
|
+
json_string = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
|
439
|
+
return cls(JsonData(json_string))
|
440
|
+
|
441
|
+
|
442
|
+
class UserFileSystem(object):
|
443
|
+
"""
|
444
|
+
Diretórios comuns para cache e configurações de usuário.
|
445
|
+
"""
|
446
|
+
def __init__(self, base_home: Directory = Directory(os.path.abspath(Path().home()))):
|
447
|
+
self.baseHome:Directory = base_home
|
448
|
+
self.userDownloads:Directory = self.baseHome.concat('Downloads', create=True)
|
449
|
+
self.userVarDir:Directory = self.baseHome.concat('var', create=True)
|
450
|
+
|
451
|
+
def config_dir(self) -> Directory:
|
452
|
+
return self.userVarDir.concat('config', create=True)
|
453
|
+
|
454
|
+
def cache_dir(self) -> Directory:
|
455
|
+
return self.userVarDir.concat('cache', create=True)
|
456
|
+
|
457
|
+
|
458
|
+
class UserAppDir(object):
|
459
|
+
"""
|
460
|
+
Diretório comun para cache e configurações do aplicativo.
|
461
|
+
"""
|
462
|
+
def __init__(self, appname:str, *, user_file_system: UserFileSystem = UserFileSystem()):
|
463
|
+
self.appname = appname
|
464
|
+
self.userFileSystem: UserFileSystem = user_file_system
|
465
|
+
self.workspaceDirApp: Directory = self.userFileSystem.userDownloads.concat(self.appname, create=True)
|
466
|
+
self.installDir: Directory = self.userFileSystem.userVarDir.concat('opt').concat(self.appname, create=True)
|
467
|
+
|
468
|
+
def cache_dir_app(self) -> Directory:
|
469
|
+
return self.userFileSystem.cache_dir().concat(self.appname, create=True)
|
470
|
+
|
471
|
+
def config_dir_app(self) -> Directory:
|
472
|
+
return self.userFileSystem.config_dir().concat(self.appname, create=True)
|
473
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
|
5
|
+
|
6
|
+
class ABCProgressBar(ABC):
|
7
|
+
"""
|
8
|
+
Barra de progresso Abstrata
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
super().__init__()
|
13
|
+
self._num_progress: float = 0
|
14
|
+
self.pbar_real: object = None
|
15
|
+
|
16
|
+
@property
|
17
|
+
def num_progress(self) -> float:
|
18
|
+
return self._num_progress
|
19
|
+
|
20
|
+
@num_progress.setter
|
21
|
+
def num_progress(self, new: float):
|
22
|
+
if isinstance(new, float):
|
23
|
+
self._num_progress = new
|
24
|
+
return
|
25
|
+
try:
|
26
|
+
_prog = float(new)
|
27
|
+
except Exception as e:
|
28
|
+
print(e)
|
29
|
+
else:
|
30
|
+
self._num_progress = _prog
|
31
|
+
|
32
|
+
@abstractmethod
|
33
|
+
def set_percent(self, percent: float):
|
34
|
+
"""Seta o progresso com float de porcentagem, ex: '42.8'"""
|
35
|
+
pass
|
36
|
+
|
37
|
+
@abstractmethod
|
38
|
+
def set_text(self, text: str):
|
39
|
+
"""Seta um texto indicando a situação atual"""
|
40
|
+
pass
|
41
|
+
|
42
|
+
def start(self):
|
43
|
+
"""Inicia a barra de progresso (pode ser vazio dependendo da implementação)"""
|
44
|
+
pass
|
45
|
+
|
46
|
+
def stop(self):
|
47
|
+
"""Para a barra de progresso (pode ser vazio dependendo da implementação)"""
|
48
|
+
pass
|
49
|
+
|
50
|
+
|
51
|
+
class ProgressBarSimple(ABCProgressBar):
|
52
|
+
"""Barra de progresso simples para mostrar no terminal."""
|
53
|
+
|
54
|
+
def __init__(self, simple_pbar=None):
|
55
|
+
super().__init__()
|
56
|
+
self.pbar_real = simple_pbar
|
57
|
+
self._text: str = 'Aguarde!'
|
58
|
+
self.num_progress: float = 0
|
59
|
+
|
60
|
+
def set_percent(self, percent: float):
|
61
|
+
if not isinstance(percent, float):
|
62
|
+
return
|
63
|
+
if len(f'{percent}') > 4:
|
64
|
+
percent = round(float(percent), 2)
|
65
|
+
self.num_progress = percent
|
66
|
+
#print(f'[{self.num_progress}%] {self._text}', end='\r')
|
67
|
+
|
68
|
+
def set_text(self, text: str):
|
69
|
+
self._text = text
|
70
|
+
print(f'[{self.num_progress}%] {self._text}', end='\r')
|
71
|
+
|
72
|
+
def start(self):
|
73
|
+
pass
|
74
|
+
|
75
|
+
def stop(self):
|
76
|
+
pass
|
77
|
+
|
78
|
+
|
79
|
+
class ProgressBarAdapter(object):
|
80
|
+
def __init__(self, progress_bar: ABCProgressBar = ProgressBarSimple()):
|
81
|
+
self.pbar_implement: ABCProgressBar = progress_bar
|
82
|
+
|
83
|
+
def get_current_percent(self) -> float:
|
84
|
+
return self.pbar_implement.num_progress
|
85
|
+
|
86
|
+
def update_text(self, text: str = "-"):
|
87
|
+
self.pbar_implement.set_text(text)
|
88
|
+
|
89
|
+
def update_percent(self, percent: float = 0):
|
90
|
+
if not isinstance(percent, float):
|
91
|
+
try:
|
92
|
+
percent = float(percent)
|
93
|
+
except Exception as e:
|
94
|
+
print(f'{__class__.__name__} {e}')
|
95
|
+
percent = 0
|
96
|
+
self.pbar_implement.set_percent(percent)
|
97
|
+
|
98
|
+
def update(self, percent: float, status: str = "-"):
|
99
|
+
self.update_percent(percent)
|
100
|
+
self.update_text(status)
|
101
|
+
#self.pbar_implement.set_text(status)
|
102
|
+
|
103
|
+
def start(self):
|
104
|
+
self.pbar_implement.start()
|
105
|
+
|
106
|
+
def stop(self):
|
107
|
+
self.pbar_implement.stop()
|
108
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
README.md
|
2
|
+
pyproject.toml
|
3
|
+
soup_files/__init__.py
|
4
|
+
soup_files/__main__.py
|
5
|
+
soup_files/files.py
|
6
|
+
soup_files/progress.py
|
7
|
+
soup_files/version.py
|
8
|
+
soup_files.egg-info/PKG-INFO
|
9
|
+
soup_files.egg-info/SOURCES.txt
|
10
|
+
soup_files.egg-info/dependency_links.txt
|
11
|
+
soup_files.egg-info/top_level.txt
|
@@ -0,0 +1 @@
|
|
1
|
+
soup_files
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|
File without changes
|
File without changes
|