ccfx 0.1.0__py3-none-any.whl → 0.2.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 CHANGED
@@ -23,7 +23,9 @@ import pickle
23
23
  import time
24
24
  from shapely.geometry import box, Point
25
25
  import geopandas, pandas
26
- from osgeo import gdal
26
+ from osgeo import gdal, ogr, osr
27
+ import py7zr
28
+ import subprocess
27
29
 
28
30
 
29
31
 
@@ -132,9 +134,6 @@ def watchFileCount(path:str="./", extension:str = ".*", interval:float = 0.2, du
132
134
  return None
133
135
 
134
136
 
135
-
136
-
137
-
138
137
  def pythonVariable(filename, option, variable=None):
139
138
  '''
140
139
  option: save, load or open
@@ -233,6 +232,57 @@ def createPath(pathName, v = False):
233
232
  return pathName
234
233
 
235
234
 
235
+ def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v = False) -> None:
236
+ """
237
+ Renames a variable in a NetCDF file using CDO if it exists.
238
+ If the variable does not exist, the file is copied without modification.
239
+
240
+ :param input_file: Path to the input NetCDF file
241
+ :param output_file: Path to the output NetCDF file
242
+ :param old_var_name: Name of the variable to rename
243
+ :param new_var_name: New name for the variable
244
+ """
245
+ try:
246
+ # Check if the variable exists in the input file using `cdo showname`
247
+ result = subprocess.run(
248
+ ["cdo", "showname", input_file],
249
+ capture_output=True,
250
+ text=True,
251
+ check=True
252
+ )
253
+
254
+ # Check if the old variable name is in the output
255
+ if old_var_name in result.stdout:
256
+ # Rename the variable using `cdo chname`
257
+ subprocess.run(
258
+ ["cdo", f"chname,{old_var_name},{new_var_name}", input_file, output_file],
259
+ check=True
260
+ )
261
+ if v: print(f"Variable '{old_var_name}' renamed to '{new_var_name}' in '{output_file}'.")
262
+ else:
263
+ # Copy the file without renaming
264
+ shutil.move(input_file, output_file)
265
+ if v: print(f"Variable '{old_var_name}' not found; '{input_file}' moved to '{output_file}' without modification.")
266
+
267
+ except subprocess.CalledProcessError as e:
268
+ print(f"Error: {e.stderr}")
269
+
270
+ def compressTo7z(input_dir: str, output_file: str):
271
+ """
272
+ Compresses the contents of a directory to a .7z archive with maximum compression.
273
+
274
+ :param input_dir: Path to the directory to compress
275
+ :param output_file: Output .7z file path
276
+ """
277
+ # Create the .7z archive with LZMA2 compression
278
+ with py7zr.SevenZipFile(output_file, 'w', filters=[{'id': py7zr.FILTER_LZMA2, 'preset': 9}]) as archive:
279
+ # Add each item in the input directory, avoiding the top-level folder in the archive
280
+ for root, _, files in os.walk(input_dir):
281
+ for file in files:
282
+ file_path = os.path.join(root, file)
283
+ # Add file to the archive with a relative path to avoid including the 'tmp' folder itself
284
+ archive.write(file_path, arcname=os.path.relpath(file_path, start=input_dir))
285
+
236
286
 
237
287
  def moveDirectory(srcDir:str, destDir:str, v:bool = False) -> bool:
238
288
  '''
@@ -315,6 +365,99 @@ def clipRasterByExtent(inFile: str, outFile: str, bounds: tuple) -> str:
315
365
  ds = None
316
366
  return outFile
317
367
 
368
+ def clipVectorByExtent(inFile: str, outFile: str, bounds: tuple) -> str:
369
+ '''
370
+ Clips a vector using GeoPandas
371
+ inFile: input vector path
372
+ outFile: output path
373
+ bounds: tuple (minx, miny, maxx, maxy)
374
+ return: output path
375
+ '''
376
+ # Load the vector file as a GeoDataFrame
377
+ gdf = geopandas.read_file(inFile)
378
+ bbox = box(bounds[0], bounds[1], bounds[2], bounds[3])
379
+ clipped = gdf.clip(bbox)
380
+ clipped.to_file(outFile)
381
+
382
+ return outFile
383
+
384
+ def reprojectRaster(inFile: str, outFile: str, dstProjection: str, resamplingMethod: str = 'mode') -> str:
385
+ '''
386
+ Reprojects a raster to a new projection
387
+ inFile: input raster path
388
+ outFile: output raster path
389
+ dstProjection: target projection in "AUTH:CODE" format (e.g., "EPSG:3395")
390
+ resamplingMethod: resampling method to use (default is 'mode')
391
+ return: output path
392
+ '''
393
+ # Open the input raster
394
+ ds = gdal.Open(inFile)
395
+
396
+ # Define resampling method
397
+ resampling_methods = {
398
+ 'nearest': gdal.GRA_NearestNeighbour,
399
+ 'bilinear': gdal.GRA_Bilinear,
400
+ 'cubic': gdal.GRA_Cubic,
401
+ 'cubicspline': gdal.GRA_CubicSpline,
402
+ 'lanczos': gdal.GRA_Lanczos,
403
+ 'average': gdal.GRA_Average,
404
+ 'mode': gdal.GRA_Mode,
405
+ 'max': gdal.GRA_Max,
406
+ 'min': gdal.GRA_Min,
407
+ 'med': gdal.GRA_Med,
408
+ 'q1': gdal.GRA_Q1,
409
+ 'q3': gdal.GRA_Q3
410
+ }
411
+
412
+ resampling = resampling_methods.get(resamplingMethod, gdal.GRA_Mode)
413
+ gdal.Warp(outFile, ds, dstSRS=dstProjection, resampleAlg=resampling)
414
+ ds = None
415
+
416
+ return outFile
417
+
418
+ def rasterizeRaster(inFile: str, outFile: str, targetField: str, targetResolution: float) -> str:
419
+ '''
420
+ Rasterizes a vector layer to a raster file
421
+ inFile: input vector file path
422
+ outFile: output raster file path
423
+ targetField: the field in the vector layer to use as the raster value
424
+ targetResolution: resolution of the output raster (in units of the vector CRS)
425
+ return: output raster path
426
+ '''
427
+ # Open the vector file
428
+ vector_ds = ogr.Open(inFile)
429
+ layer = vector_ds.GetLayer()
430
+
431
+ # Get the extent of the vector layer
432
+ x_min, x_max, y_min, y_max = layer.GetExtent()
433
+
434
+ # Calculate the raster size based on target resolution
435
+ x_res = int((x_max - x_min) / targetResolution)
436
+ y_res = int((y_max - y_min) / targetResolution)
437
+
438
+ # Create the raster dataset
439
+ target_ds = gdal.GetDriverByName('GTiff').Create(outFile, x_res, y_res, 1, gdal.GDT_Int16)
440
+ target_ds.SetGeoTransform((x_min, targetResolution, 0, y_max, 0, -targetResolution))
441
+
442
+ # Set the projection from the vector layer
443
+ srs = layer.GetSpatialRef()
444
+ target_ds.SetProjection(srs.ExportToWkt())
445
+
446
+ # Set the no-data value to -999
447
+ band = target_ds.GetRasterBand(1)
448
+ band.SetNoDataValue(-999)
449
+
450
+ # Rasterize the vector layer
451
+ gdal.RasterizeLayer(target_ds, [1], layer, options=["ATTRIBUTE=" + targetField])
452
+
453
+ # Close the datasets
454
+ band = None
455
+ target_ds = None
456
+ vector_ds = None
457
+
458
+ return outFile
459
+
460
+
318
461
  def getVectorBounds(grid_gdf: geopandas.GeoDataFrame) -> tuple:
319
462
  '''
320
463
  This function gets the bounds of a GeoDataFrame
@@ -339,7 +482,6 @@ def getVectorBounds(grid_gdf: geopandas.GeoDataFrame) -> tuple:
339
482
 
340
483
  return minx, miny, maxx, maxy
341
484
 
342
-
343
485
  def ignoreWarnings(ignore:bool = True, v:bool = False) -> None:
344
486
  '''
345
487
  Ignore warnings
@@ -667,6 +809,27 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
667
809
  if count == end: print()
668
810
 
669
811
 
812
+ def listAllFiles(folder, extension="*"):
813
+ list_of_files = []
814
+ # Getting the current work directory (cwd)
815
+ thisdir = folder
816
+
817
+ # r=root, d=directories, f = files
818
+ for r, d, f in os.walk(thisdir):
819
+ for file in f:
820
+ if extension == "*":
821
+ list_of_files.append(os.path.join(r, file))
822
+ elif "." in extension:
823
+ if file.endswith(extension[1:]):
824
+ list_of_files.append(os.path.join(r, file))
825
+ # print(os.path.join(r, file))
826
+ else:
827
+ if file.endswith(extension):
828
+ list_of_files.append(os.path.join(r, file))
829
+ # print(os.path.join(r, file))
830
+
831
+ return list_of_files
832
+
670
833
 
671
834
  def createPointGeometry(coords: list, proj: str = "EPSG:4326") -> geopandas.GeoDataFrame:
672
835
  '''
ccfx/sqliteConnection.py CHANGED
@@ -33,19 +33,29 @@ class sqliteConnection:
33
33
 
34
34
  def updateValue(self, table_name, col_name, new_value, col_where1, val_1, v=False):
35
35
  """
36
- does not work yet!
36
+ Updates a single value in a specified table where the condition matches.
37
+
38
+ Args:
39
+ table_name (str): Name of the table to update
40
+ col_name (str): Name of the column to update
41
+ new_value: Value to set (can be None)
42
+ col_where1 (str): Column name for WHERE clause
43
+ val_1: Value to match in WHERE clause
44
+ v (bool): Verbose flag for logging
37
45
  """
38
- if not new_value is None:
39
- new_value = str(new_value)
40
- self.cursor.execute("UPDATE " + table_name + " SET " + col_name +
41
- " = '" + new_value + "' WHERE " + col_where1 + " = " + val_1 + ";")
42
- if new_value is None:
43
- self.cursor.execute("UPDATE " + table_name + " SET " + col_name +
44
- " = ? " + " WHERE " + col_where1 + " = ?", (new_value, val_1))
45
- # self.cursor.execute(sql_str)
46
- if v:
47
- self.report("\t -> updated {1} value in {0}".format(
48
- self.db_name.split("/")[-1].split("\\")[-1], table_name))
46
+ try:
47
+ # Use parameterized queries for ALL cases to prevent SQL injection
48
+ query = f"UPDATE {table_name} SET {col_name} = ? WHERE {col_where1} = ?"
49
+ self.cursor.execute(query, (new_value, val_1))
50
+
51
+ if v:
52
+ self.report(f"\t -> updated value in {self.db_name.split('/')[-1].split('\\')[-1]} table: {table_name}")
53
+
54
+ # Commit the transaction if needed (add self.conn.commit() if not in a transaction)
55
+
56
+ except Exception as e:
57
+ raise Exception(f"Error updating value: {str(e)}")
58
+
49
59
 
50
60
  def createTable(self, table_name, initial_field_name, data_type):
51
61
  '''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ccfx
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Your package description here
5
5
  Author-email: Celray James CHAWANDA <celray@chawanda.com>
6
6
  License: MIT
@@ -20,7 +20,8 @@ Requires-Dist: pandas
20
20
  Requires-Dist: xlsxwriter
21
21
  Requires-Dist: pyodbc
22
22
  Requires-Dist: sqlalchemy
23
- Requires-Dist: docx
23
+ Requires-Dist: python-docx
24
+ Requires-Dist: py7zr
24
25
 
25
26
  # ccfx
26
27
 
@@ -0,0 +1,11 @@
1
+ ccfx/__init__.py,sha256=VmBeF3oj6JTJ_793d4i8PvhyF8_FxaxA1L_FmHWqitc,142
2
+ ccfx/ccfx.py,sha256=lB6hTCywvbMpHH6lMgw0UJuoiU-7vA22yvttvKFG5Gc,28450
3
+ ccfx/excel.py,sha256=cQ4TQW49XqbMB3sSS0IOhO3-WArIolEBIrvOvhFyPtI,4757
4
+ ccfx/mssqlConnection.py,sha256=TwyZXhHHI7zy6BSfH1pszuHVJ5cmndRC5dVxvEtSTks,7904
5
+ ccfx/sqliteConnection.py,sha256=BsS3jzHSevXLDmtPIFPVuzKqB981su0VpYadTk4xFEQ,11231
6
+ ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
7
+ ccfx-0.2.0.dist-info/LICENSE,sha256=2-M3fBUS3FmrSIrqd3cZDmxXxojWVJtZY-SHSRE6RxM,1098
8
+ ccfx-0.2.0.dist-info/METADATA,sha256=2GD2kIkcVUo6FG4Th3IEAucmYmhZ4DvdbtGPTI328-c,5419
9
+ ccfx-0.2.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10
+ ccfx-0.2.0.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
11
+ ccfx-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- ccfx/__init__.py,sha256=VmBeF3oj6JTJ_793d4i8PvhyF8_FxaxA1L_FmHWqitc,142
2
- ccfx/ccfx.py,sha256=7vDYc8X1bQGHuF8ZWEH3cWkGDJGlZKrG5kcoeG9IZac,22110
3
- ccfx/excel.py,sha256=cQ4TQW49XqbMB3sSS0IOhO3-WArIolEBIrvOvhFyPtI,4757
4
- ccfx/mssqlConnection.py,sha256=TwyZXhHHI7zy6BSfH1pszuHVJ5cmndRC5dVxvEtSTks,7904
5
- ccfx/sqliteConnection.py,sha256=7xET_tV8uu4LEln4nsWzp3YR5HOma15aaILjrv2F5ZM,10866
6
- ccfx/word.py,sha256=AGa64jX5Zl5qotZh5L0QmrsjTnktIBhmj_ByRKZ88vw,3061
7
- ccfx-0.1.0.dist-info/LICENSE,sha256=2-M3fBUS3FmrSIrqd3cZDmxXxojWVJtZY-SHSRE6RxM,1098
8
- ccfx-0.1.0.dist-info/METADATA,sha256=hZSUkqy3F-ZWJBH8w6pPELTR_1rNSahFTA4G28PZfiw,5390
9
- ccfx-0.1.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
10
- ccfx-0.1.0.dist-info/top_level.txt,sha256=_cSvSA1WX2K8TgoV3iBJUdUZZqMKJbOPLNnKLYSLHaw,5
11
- ccfx-0.1.0.dist-info/RECORD,,
File without changes