ccfx 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
- ccfx/ccfx.py +103 -1
- {ccfx-0.5.0.dist-info → ccfx-0.7.0.dist-info}/METADATA +3 -1
- {ccfx-0.5.0.dist-info → ccfx-0.7.0.dist-info}/RECORD +6 -6
- {ccfx-0.5.0.dist-info → ccfx-0.7.0.dist-info}/WHEEL +0 -0
- {ccfx-0.5.0.dist-info → ccfx-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {ccfx-0.5.0.dist-info → ccfx-0.7.0.dist-info}/top_level.txt +0 -0
ccfx/ccfx.py
CHANGED
@@ -29,7 +29,10 @@ import subprocess
|
|
29
29
|
import multiprocessing
|
30
30
|
from mutagen.mp3 import MP3
|
31
31
|
from mutagen.id3 import ID3, TPE1, TALB, TIT2, TRCK, TDRC, TCON, APIC, COMM, USLT, TPE2, TCOM, TPE3, TPE4, TCOP, TENC, TSRC, TBPM
|
32
|
-
|
32
|
+
from concurrent.futures import ThreadPoolExecutor
|
33
|
+
import math
|
34
|
+
import requests
|
35
|
+
from tqdm import tqdm
|
33
36
|
|
34
37
|
# functions
|
35
38
|
def listFiles(path: str, ext: str = None) -> list:
|
@@ -247,6 +250,82 @@ def deletePath(path:str, v:bool = False) -> bool:
|
|
247
250
|
deleted = False
|
248
251
|
|
249
252
|
|
253
|
+
def downloadChunk(url, start, end, path):
|
254
|
+
headers = {'Range': f'bytes={start}-{end}'}
|
255
|
+
response = requests.get(url, headers=headers, stream=True)
|
256
|
+
with open(path, 'wb') as f:
|
257
|
+
for chunk in response.iter_content(chunk_size=8192):
|
258
|
+
if chunk:
|
259
|
+
f.write(chunk)
|
260
|
+
|
261
|
+
def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=True):
|
262
|
+
if v:
|
263
|
+
print(f"\ndownloading {url}")
|
264
|
+
fname = getFileBaseName(url, extension=True)
|
265
|
+
save_dir = os.path.dirname(save_path)
|
266
|
+
save_fname = "{0}/{1}".format(save_dir, fname)
|
267
|
+
|
268
|
+
if not os.path.isdir(save_dir):
|
269
|
+
os.makedirs(save_dir)
|
270
|
+
|
271
|
+
# Handle existing file
|
272
|
+
if os.path.exists(save_fname):
|
273
|
+
if exists_action == 'skip':
|
274
|
+
if v:
|
275
|
+
print(f"File exists, skipping: {save_fname}")
|
276
|
+
return
|
277
|
+
elif exists_action == 'overwrite':
|
278
|
+
os.remove(save_fname)
|
279
|
+
# 'resume' is handled below
|
280
|
+
|
281
|
+
# Get file size
|
282
|
+
response = requests.head(url)
|
283
|
+
file_size = int(response.headers.get('content-length', 0))
|
284
|
+
|
285
|
+
# Resume download if file exists and exists_action is 'resume'
|
286
|
+
initial_pos = 0
|
287
|
+
if exists_action == 'resume' and os.path.exists(save_fname):
|
288
|
+
initial_pos = os.path.getsize(save_fname)
|
289
|
+
if initial_pos >= file_size:
|
290
|
+
if v:
|
291
|
+
print(f"File already completed: {save_fname}")
|
292
|
+
return
|
293
|
+
|
294
|
+
# Calculate chunk sizes
|
295
|
+
chunk_size = math.ceil((file_size - initial_pos) / num_connections)
|
296
|
+
chunks = []
|
297
|
+
for i in range(num_connections):
|
298
|
+
start = initial_pos + (i * chunk_size)
|
299
|
+
end = min(start + chunk_size - 1, file_size - 1)
|
300
|
+
chunks.append((start, end))
|
301
|
+
|
302
|
+
# Download chunks in parallel
|
303
|
+
temp_files = [f"{save_fname}.part{i}" for i in range(num_connections)]
|
304
|
+
with ThreadPoolExecutor(max_workers=num_connections) as executor:
|
305
|
+
futures = []
|
306
|
+
for i, (start, end) in enumerate(chunks):
|
307
|
+
futures.append(
|
308
|
+
executor.submit(downloadChunk, url, start, end, temp_files[i])
|
309
|
+
)
|
310
|
+
|
311
|
+
# Wait for all downloads to complete with progress bar
|
312
|
+
with tqdm(total=file_size-initial_pos, initial=initial_pos, unit='B',
|
313
|
+
unit_scale=True, desc=fname) as pbar:
|
314
|
+
completed = initial_pos
|
315
|
+
while completed < file_size:
|
316
|
+
current = sum(os.path.getsize(f) for f in temp_files if os.path.exists(f))
|
317
|
+
pbar.update(current - completed)
|
318
|
+
completed = current
|
319
|
+
|
320
|
+
# Merge chunks
|
321
|
+
with open(save_fname, 'ab' if initial_pos > 0 else 'wb') as outfile:
|
322
|
+
for temp_file in temp_files:
|
323
|
+
if os.path.exists(temp_file):
|
324
|
+
with open(temp_file, 'rb') as infile:
|
325
|
+
outfile.write(infile.read())
|
326
|
+
os.remove(temp_file)
|
327
|
+
|
328
|
+
|
250
329
|
def mergeRasterTiles(tileList:list, outFile:str) -> str:
|
251
330
|
'''
|
252
331
|
Merge raster tiles into one raster file
|
@@ -1114,6 +1193,29 @@ def listAllFiles(folder, extension="*"):
|
|
1114
1193
|
return list_of_files
|
1115
1194
|
|
1116
1195
|
|
1196
|
+
def clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes = None, v = False) -> geopandas.GeoDataFrame:
|
1197
|
+
'''
|
1198
|
+
keepOnlyTypes = ['MultiPolygon', 'Polygon', 'Point', etc]
|
1199
|
+
|
1200
|
+
'''
|
1201
|
+
mask_gdf = geopandas.read_file(boundaryFeature)
|
1202
|
+
input_gdf = geopandas.read_file(inputFeaturePath)
|
1203
|
+
|
1204
|
+
outDir = os.path.dirname(outputFeature)
|
1205
|
+
createPath(f"{outDir}/")
|
1206
|
+
out_gdf = input_gdf.clip(mask_gdf.to_crs(input_gdf.crs))
|
1207
|
+
|
1208
|
+
if not keepOnlyTypes is None:
|
1209
|
+
out_gdf = out_gdf[out_gdf.geometry.apply(lambda x : x.type in keepOnlyTypes)]
|
1210
|
+
|
1211
|
+
out_gdf.to_file(outputFeature)
|
1212
|
+
|
1213
|
+
if v:
|
1214
|
+
print("\t - clipped feature to " + outputFeature)
|
1215
|
+
return out_gdf
|
1216
|
+
|
1217
|
+
|
1218
|
+
|
1117
1219
|
def createPointGeometry(coords: list, proj: str = "EPSG:4326") -> geopandas.GeoDataFrame:
|
1118
1220
|
'''
|
1119
1221
|
Convert list of coordinate tuples to GeoDataFrame
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ccfx
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: This package simplifies regular common actions for quick prototyping in a user friendly way
|
5
5
|
Author-email: Celray James CHAWANDA <celray@chawanda.com>
|
6
6
|
License: MIT
|
@@ -23,6 +23,8 @@ Requires-Dist: sqlalchemy
|
|
23
23
|
Requires-Dist: python-docx
|
24
24
|
Requires-Dist: py7zr
|
25
25
|
Requires-Dist: mutagen
|
26
|
+
Requires-Dist: requests
|
27
|
+
Requires-Dist: tqdm
|
26
28
|
Dynamic: license-file
|
27
29
|
|
28
30
|
# ccfx
|
@@ -1,11 +1,11 @@
|
|
1
1
|
ccfx/__init__.py,sha256=VmBeF3oj6JTJ_793d4i8PvhyF8_FxaxA1L_FmHWqitc,142
|
2
|
-
ccfx/ccfx.py,sha256=
|
2
|
+
ccfx/ccfx.py,sha256=4BD22BQovfkcAFZRkoHaA1quvXlDBFc1GiH_Fffz6Q8,55571
|
3
3
|
ccfx/excel.py,sha256=cQ4TQW49XqbMB3sSS0IOhO3-WArIolEBIrvOvhFyPtI,4757
|
4
4
|
ccfx/mssqlConnection.py,sha256=TwyZXhHHI7zy6BSfH1pszuHVJ5cmndRC5dVxvEtSTks,7904
|
5
5
|
ccfx/sqliteConnection.py,sha256=jEJ94D5ySt84N7AeDpa27Rclt1NaKhkX6nYzidwApIg,11104
|
6
6
|
ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
|
7
|
-
ccfx-0.
|
8
|
-
ccfx-0.
|
9
|
-
ccfx-0.
|
10
|
-
ccfx-0.
|
11
|
-
ccfx-0.
|
7
|
+
ccfx-0.7.0.dist-info/licenses/LICENSE,sha256=2-M3fBUS3FmrSIrqd3cZDmxXxojWVJtZY-SHSRE6RxM,1098
|
8
|
+
ccfx-0.7.0.dist-info/METADATA,sha256=_rPWPy0bHKGdUv5y2Ovt_XGpIbRv7Xa33oJJDfWDv-o,5425
|
9
|
+
ccfx-0.7.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
10
|
+
ccfx-0.7.0.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
|
11
|
+
ccfx-0.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|