ccfx 1.0.6__tar.gz → 1.0.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccfx
3
- Version: 1.0.6
3
+ Version: 1.0.8
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-Expression: MIT
@@ -35,7 +35,7 @@ import math
35
35
  import requests
36
36
  from tqdm import tqdm
37
37
  import yt_dlp
38
- from typing import Optional
38
+ from typing import Optional, Any
39
39
  from datetime import datetime, timedelta
40
40
  from PIL import Image
41
41
 
@@ -72,7 +72,7 @@ def getExtension(filePath:str) -> str:
72
72
  return os.path.splitext(filePath)[1].lstrip('.')
73
73
 
74
74
 
75
- def getMp3Metadata(fn, imagePath=None):
75
+ def getMp3Metadata(fn: str, imagePath: Optional[str] = None) -> dict:
76
76
  '''
77
77
  This function takes a path to mp3 and returns a dictionary with
78
78
  the following keys:
@@ -127,7 +127,7 @@ def getMp3Metadata(fn, imagePath=None):
127
127
  return metadata
128
128
 
129
129
 
130
- def guessMimeType(imagePath):
130
+ def guessMimeType(imagePath: str) -> str:
131
131
  ext = os.path.splitext(imagePath.lower())[1]
132
132
  if ext in ['.jpg', '.jpeg']:
133
133
  return 'image/jpeg'
@@ -240,7 +240,7 @@ def parseYoutubeChannelVideos(channelUrl: str, maxItems: Optional[int] = None) -
240
240
  return [f"https://www.youtube.com/watch?v={e['id']}" for e in entries if e.get("id")]
241
241
 
242
242
 
243
- def runSWATPlus(txtinoutDir: str, finalDir: str, executablePath: str = "swatplus", v: bool = True):
243
+ def runSWATPlus(txtinoutDir: str, finalDir: str, executablePath: str = "swatplus", v: bool = True) -> None:
244
244
  os.chdir(txtinoutDir)
245
245
 
246
246
  if not v:
@@ -339,7 +339,7 @@ def formatTimedelta(delta: timedelta) -> str:
339
339
  return f"{time_fmt}"
340
340
 
341
341
 
342
- def setMp3Metadata(fn, metadata, imagePath=None):
342
+ def setMp3Metadata(fn: str, metadata: dict, imagePath: Optional[str] = None) -> bool:
343
343
  '''
344
344
  This function takes a path to an mp3 and a metadata dictionary,
345
345
  then writes that metadata to the file's ID3 tags.
@@ -434,7 +434,7 @@ def deleteFile(filePath:str, v:bool = False) -> bool:
434
434
 
435
435
  return deleted
436
436
 
437
- def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30):
437
+ def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30) -> None:
438
438
  '''
439
439
  Remove a specific color from an image.
440
440
  colour: RGB tuple, e.g., (255, 0, 0) for red
@@ -457,7 +457,7 @@ def removeImageColour(inPath:str, outPath:str, colour:tuple = (255, 255, 255), t
457
457
  img.putdata(new_data)
458
458
  img.save(outPath)
459
459
 
460
- def makeTransparent(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30):
460
+ def makeTransparent(inPath:str, outPath:str, colour:tuple = (255, 255, 255), tolerance:int = 30) -> None:
461
461
  '''
462
462
  Make some pixels in an image transparent.
463
463
  '''
@@ -519,7 +519,7 @@ def deletePath(path:str, v:bool = False) -> bool:
519
519
  deleted = False
520
520
 
521
521
 
522
- def downloadChunk(url, start, end, path):
522
+ def downloadChunk(url: str, start: int, end: int, path: str) -> None:
523
523
  headers = {'Range': f'bytes={start}-{end}'}
524
524
  response = requests.get(url, headers=headers, stream=True)
525
525
  with open(path, 'wb') as f:
@@ -528,7 +528,7 @@ def downloadChunk(url, start, end, path):
528
528
  f.write(chunk)
529
529
 
530
530
 
531
- def formatStringBlock(input_str, max_chars=70):
531
+ def formatStringBlock(input_str: str, max_chars: int = 70) -> str:
532
532
  '''
533
533
  This function takes a string and formats it into a block of text
534
534
  with a maximum number of characters per line.
@@ -559,7 +559,7 @@ def formatStringBlock(input_str, max_chars=70):
559
559
 
560
560
 
561
561
 
562
- def downloadFile(url, save_path, exists_action='resume', num_connections=5, v=False):
562
+ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_connections: int = 5, v: bool = False) -> None:
563
563
  if v:
564
564
  print(f"\ndownloading {url}")
565
565
  fname = getFileBaseName(url, extension=True)
@@ -649,7 +649,7 @@ def systemPlatform() -> str:
649
649
  '''
650
650
  return platform.system()
651
651
 
652
- def progressBar(count, total, message=""):
652
+ def progressBar(count: int, total: int, message: str = "") -> None:
653
653
  percent = int(count / total * 100)
654
654
  filled = int(percent / 2)
655
655
  bar = '█' * filled + '░' * (50 - filled)
@@ -669,7 +669,7 @@ def fileCount(path:str = "./", extension:str = ".*", v:bool = True) -> int:
669
669
  print(f'> there are {count} {extension if not extension ==".*" else ""} files in {path}')
670
670
  return count
671
671
 
672
- def resampleRaster(inFile:str, outFile:str, resolution:float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput:bool = True, v:bool = True) -> str:
672
+ def resampleRaster(inFile:str, outFile:str, resolution:float, dstSRS = None, resamplingMethod = 'bilinear', replaceOutput:bool = True, v:bool = True) -> Optional[str]:
673
673
  '''
674
674
  Resample a raster file
675
675
  inFile: input raster file
@@ -740,7 +740,7 @@ def watchFileCount(path:str="./", extension:str = ".*", interval:float = 0.2, du
740
740
  return None
741
741
 
742
742
 
743
- def pythonVariable(filename, option, variable=None):
743
+ def pythonVariable(filename: str, option: str, variable: Any = None) -> Any:
744
744
  '''
745
745
  option: save, load or open
746
746
 
@@ -774,7 +774,7 @@ def listFolders(path:str) -> list:
774
774
  else:
775
775
  return []
776
776
 
777
- def readFrom(filename, decode_codec = None, v=False):
777
+ def readFrom(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any:
778
778
  '''
779
779
  a function to read ascii files
780
780
  '''
@@ -793,16 +793,16 @@ def readFrom(filename, decode_codec = None, v=False):
793
793
 
794
794
 
795
795
  def pointsToGeodataframe(
796
- rowList,
797
- columnNames,
798
- latIndex,
799
- lonIndex,
800
- auth = "EPSG",
801
- code = "4326",
802
- outShape = "",
803
- format = "gpkg",
804
- v = False,
805
- includeLatLon = True ) -> geopandas.GeoDataFrame:
796
+ rowList: list,
797
+ columnNames: list,
798
+ latIndex: int,
799
+ lonIndex: int,
800
+ auth: str = "EPSG",
801
+ code: str = "4326",
802
+ outShape: str = "",
803
+ format: str = "gpkg",
804
+ v: bool = False,
805
+ includeLatLon: bool = True ) -> geopandas.GeoDataFrame:
806
806
  df = pandas.DataFrame(rowList, columns = columnNames)
807
807
  geometry = [
808
808
  Point(row[lonIndex], row[latIndex]) for row in rowList
@@ -824,10 +824,10 @@ def pointsToGeodataframe(
824
824
  return gdf
825
825
 
826
826
 
827
- def readFile(filename, decode_codec = None, v=False):
827
+ def readFile(filename: str, decode_codec: Optional[str] = None, v: bool = False) -> Any:
828
828
  return readFrom(filename, decode_codec, v)
829
829
 
830
- def writeTo(filename, file_text, encode_codec = None, v=False) -> bool:
830
+ def writeTo(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
831
831
  '''
832
832
  a function to write ascii files
833
833
  '''
@@ -846,13 +846,13 @@ def writeTo(filename, file_text, encode_codec = None, v=False) -> bool:
846
846
  if v: print("\t> wrote {0}".format(getFileBaseName(filename, extension=True)))
847
847
  return True
848
848
 
849
- def writeToFile(filename, file_text, encode_codec = None, v=False) -> bool:
849
+ def writeToFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
850
850
  return writeTo(filename, file_text, encode_codec, v)
851
851
 
852
- def writeFile(filename, file_text, encode_codec = None, v=False) -> bool:
852
+ def writeFile(filename: str, file_text: Any, encode_codec: Optional[str] = None, v: bool = False) -> bool:
853
853
  return writeTo(filename, file_text, encode_codec, v)
854
854
 
855
- def createPath(pathName, v = False):
855
+ def createPath(pathName: str, v: bool = False) -> str:
856
856
  '''
857
857
  this function creates a directory if it does not exist
858
858
  pathName: the path to create
@@ -871,7 +871,7 @@ def createPath(pathName, v = False):
871
871
  return pathName
872
872
 
873
873
 
874
- def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v = False) -> None:
874
+ def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, new_var_name: str, v: bool = False) -> None:
875
875
  """
876
876
  Renames a variable in a NetCDF file using CDO if it exists.
877
877
  If the variable does not exist, the file is copied without modification.
@@ -907,7 +907,7 @@ def renameNetCDFvariable(input_file: str, output_file: str, old_var_name: str, n
907
907
  print(f"Error: {e.stderr}")
908
908
 
909
909
 
910
- def compressTo7z(input_dir: str, output_file: str, compressionLevel: int = 4, excludeExt: list = None, v: bool = False) -> None:
910
+ def compressTo7z(input_dir: str, output_file: str, compressionLevel: int = 4, excludeExt: Optional[list] = None, v: bool = False) -> None:
911
911
  """
912
912
  Compresses the contents of a directory to a .7z archive with maximum compression.
913
913
 
@@ -1210,8 +1210,8 @@ def ignoreWarnings(ignore:bool = True, v:bool = False) -> None:
1210
1210
  return None
1211
1211
 
1212
1212
 
1213
- def createGrid(topLeft: list = None, bottomRight: list = None, resolution: float = None,
1214
- inputShape: str = None, crs: str = "EPSG:4326", saveVector: str = None) -> geopandas.GeoDataFrame:
1213
+ def createGrid(topLeft: Optional[list] = None, bottomRight: Optional[list] = None, resolution: Optional[float] = None,
1214
+ inputShape: Optional[str] = None, crs: str = "EPSG:4326", saveVector: Optional[str] = None) -> geopandas.GeoDataFrame:
1215
1215
  '''
1216
1216
  This function creates a grid of polygons based on either a shapefile or corner coordinates
1217
1217
 
@@ -1341,7 +1341,7 @@ def netcdfVariableDimensions(ncFile: str, variable: str) -> dict:
1341
1341
 
1342
1342
  return bands_info
1343
1343
 
1344
- def netcdfExportTif(ncFile: str, variable: str, outputFile: Optional[str] = None, band: int = None, v:bool = True) -> gdal.Dataset:
1344
+ def netcdfExportTif(ncFile: str, variable: str, outputFile: Optional[str] = None, band: Optional[int] = None, v:bool = True) -> gdal.Dataset:
1345
1345
  '''
1346
1346
  Export a variable from a NetCDF file to a GeoTiff file
1347
1347
  ncFile: NetCDF file
@@ -1401,8 +1401,8 @@ def netcdfSumMaps(ncFiles:list, variable:str, band:int = 1) -> numpy.ndarray:
1401
1401
  def tiffWriteArray(array: numpy.ndarray, outputFile: str,
1402
1402
  geoTransform: tuple = (0, 1, 0, 0, 0, -1),
1403
1403
  projection: str = 'EPSG:4326',
1404
- noData:float = None,
1405
- v:bool = False) -> gdal.Dataset:
1404
+ noData: Optional[float] = None,
1405
+ v: bool = False) -> gdal.Dataset:
1406
1406
  '''
1407
1407
  Write a numpy array to a GeoTIFF file
1408
1408
  array : numpy array to write
@@ -1448,7 +1448,7 @@ def copyFile(source:str, destination:str, v:bool = True) -> None:
1448
1448
  if v: print(f'> {source} copied to \t - {destination}')
1449
1449
 
1450
1450
 
1451
- def copyDirectory(source:str, destination:str, recursive = True, v:bool = True, filter = []) -> None:
1451
+ def copyDirectory(source:str, destination:str, recursive: bool = True, v:bool = True, filter: list = []) -> None:
1452
1452
  '''
1453
1453
  Copy a directory from source to destination
1454
1454
  source: source directory
@@ -1499,7 +1499,7 @@ def copyFolder(source:str, destination:str, v:bool = True) -> None:
1499
1499
  copyDirectory(source, destination, v=v)
1500
1500
 
1501
1501
 
1502
- def convertCoordinates(lon, lat, srcEPSG, dstCRS) -> tuple:
1502
+ def convertCoordinates(lon: float, lat: float, srcEPSG: str, dstCRS: str) -> tuple:
1503
1503
  """
1504
1504
  this function converts coordinates from one CRS to another
1505
1505
 
@@ -1516,7 +1516,7 @@ def convertCoordinates(lon, lat, srcEPSG, dstCRS) -> tuple:
1516
1516
  return (new_lon, new_lat)
1517
1517
 
1518
1518
 
1519
- def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> float:
1519
+ def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]:
1520
1520
  """
1521
1521
  Extract raster value at given coordinates.
1522
1522
 
@@ -1561,7 +1561,7 @@ def extractRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str =
1561
1561
  return float(value)
1562
1562
 
1563
1563
 
1564
- def getRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> float:
1564
+ def getRasterValue(rasterPath: str, lat: float, lon: float, coordProj: str = 'EPSG:4326') -> Optional[float]:
1565
1565
  '''
1566
1566
  this function is a wrapper for extractRasterValue
1567
1567
  '''
@@ -1588,13 +1588,40 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
1588
1588
  percentStr = f'{percent:03.1f}'
1589
1589
  filled = int(barLength * count / end)
1590
1590
  bar = '█' * filled + '░' * (barLength - filled)
1591
- print(f'\r{bar} {percentStr}% [{count}/{end}] | {message} ', end='', flush=True)
1591
+ print(f'\r{message} |{bar}| {percent}% [{count}/{end}]', end='', flush=True)
1592
1592
  if count == end:
1593
- print(f'\r{bar} {percentStr}% [{count}/{end}] ', end='', flush=True)
1593
+ print(f'\r{message} |{bar}| {percent}% [{count}/{end}] ', end='', flush=True)
1594
1594
  print()
1595
1595
 
1596
1596
 
1597
- def listAllFiles(folder, extension="*"):
1597
+ def dualProgress(primaryCount: int, primaryEnd: int,
1598
+ secondaryCount: int, secondaryEnd: int,
1599
+ barLength: int = 40,
1600
+ message: str = '') -> None:
1601
+ '''
1602
+ Draw two full progress bars every frame, overwriting previous frame.
1603
+ Bars are redrawn entirely each call.
1604
+ '''
1605
+
1606
+ primaryPercent = float(primaryCount / primaryEnd * 100) if primaryEnd > 0 else 100
1607
+ secondaryPercent = float(secondaryCount / secondaryEnd * 100) if secondaryEnd > 0 else 100
1608
+
1609
+ filledPrimary = int(barLength * primaryCount / primaryEnd) if primaryEnd > 0 else barLength
1610
+ filledSecondary = int((barLength - filledPrimary) * secondaryCount / secondaryEnd) if secondaryEnd > 0 else barLength
1611
+
1612
+ startSection = filledPrimary
1613
+ middleSection = filledSecondary
1614
+ endSection = barLength - startSection - middleSection
1615
+
1616
+ bar = '█' * startSection + '░' * middleSection + '-' * endSection
1617
+ formattedPrimaryPercent = f'{primaryPercent:03.1f}'
1618
+ formattedSecondaryPercent = f'{secondaryPercent:03.1f}'
1619
+ print(f'\r{bar} {formattedPrimaryPercent.rjust(6)}% | {formattedSecondaryPercent.rjust(6)}% | {message} ', end='', flush=True)
1620
+ if primaryCount == primaryEnd and secondaryCount == secondaryEnd:
1621
+ print(f'\r{bar} {formattedPrimaryPercent.rjust(6)}% | {formattedSecondaryPercent.rjust(6)}% ', end='', flush=True)
1622
+
1623
+
1624
+ def listAllFiles(folder: str, extension: str = "*") -> list:
1598
1625
  list_of_files = []
1599
1626
  # Getting the current work directory (cwd)
1600
1627
  thisdir = folder
@@ -1616,7 +1643,7 @@ def listAllFiles(folder, extension="*"):
1616
1643
  return list_of_files
1617
1644
 
1618
1645
 
1619
- def clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes = None, v = False) -> geopandas.GeoDataFrame:
1646
+ def clipFeatures(inputFeaturePath:str, boundaryFeature:str, outputFeature:str, keepOnlyTypes: Optional[list] = None, v: bool = False) -> geopandas.GeoDataFrame:
1620
1647
  '''
1621
1648
  keepOnlyTypes = ['MultiPolygon', 'Polygon', 'Point', etc]
1622
1649
 
@@ -1996,7 +2023,7 @@ def getTimeseriesStats(data:pandas.DataFrame, observed:Optional[str] = None, sim
1996
2023
 
1997
2024
  return stats
1998
2025
 
1999
- def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None):
2026
+ def readSWATPlusOutputs(filePath: str, column: Optional[str] = None, unit: Optional[int] = None, gis_id: Optional[int] = None, name: Optional[str] = None) -> Optional[pandas.DataFrame]:
2000
2027
  '''
2001
2028
  Read SWAT+ output files and return a pandas DataFrame with proper date handling
2002
2029
  and optional filtering capabilities.
@@ -17,7 +17,7 @@ import pandas
17
17
 
18
18
  # classes
19
19
  class sqliteConnection:
20
- def __init__(self, sqlite_database, connect = False):
20
+ def __init__(self, sqlite_database, connect = False) -> None:
21
21
  self.db_name = sqlite_database
22
22
  self.connection = None
23
23
  self.cursor = None
@@ -25,13 +25,13 @@ class sqliteConnection:
25
25
  if connect:
26
26
  self.connect()
27
27
 
28
- def connect(self, v=True):
28
+ def connect(self, v=True) -> None:
29
29
  self.connection = sqlite3.connect(self.db_name)
30
30
  self.cursor = self.connection.cursor()
31
31
  if v:
32
32
  self.report("\t-> connection to " + self.db_name + " established...")
33
33
 
34
- def updateValue(self, table_name, col_name, new_value, col_where1, val_1, v=False):
34
+ def updateValue(self, table_name, col_name, new_value, col_where1, val_1, v=False) -> None:
35
35
  """
36
36
  Updates a single value in a specified table where the condition matches.
37
37
 
@@ -56,7 +56,7 @@ class sqliteConnection:
56
56
  raise Exception(f"Error updating value: {str(e)}")
57
57
 
58
58
 
59
- def createTable(self, table_name, initial_field_name, data_type):
59
+ def createTable(self, table_name, initial_field_name, data_type) -> None:
60
60
  '''
61
61
  can be text, real, etc
62
62
  '''
@@ -67,7 +67,100 @@ class sqliteConnection:
67
67
  except:
68
68
  self.report("\t! table exists")
69
69
 
70
- def renameTable(self, old_table_name, new_table_name, v=False):
70
+ def newTable(self, tableName, columnsDict, columnOrder=None, notNull=None, foreignKeys=None) -> None:
71
+ """
72
+ Create a new table based on dictionary of columns and types.
73
+
74
+ Args:
75
+ tableName (str): Name of the table to create
76
+ columnsDict (dict): Dictionary with column names as keys and data types as values
77
+ options:
78
+ INTEGER, REAL, TEXT, BLOB, etc.
79
+ Example: {'id': 'INTEGER PRIMARY KEY', 'name': 'TEXT', 'age': 'INTEGER'}
80
+
81
+ columnOrder (list, optional): List specifying the order of columns
82
+ Example: ['name', 'age', 'id']
83
+
84
+ notNull (list, optional): List of column names that should have NOT NULL constraint
85
+ Example: ['name', 'age']
86
+
87
+ foreignKeys (dict, optional): Dictionary defining foreign key constraints
88
+ Format Option 1: {currentTableField: {foreignTable: foreignField}, 'onDelete': 'CASCADE'}
89
+ Format Option 2: {currentTableField: {foreignTable: foreignField, 'onDelete': 'CASCADE'}}
90
+
91
+ onDelete options: CASCADE, SET NULL, RESTRICT, NO ACTION, SET DEFAULT
92
+ Examples:
93
+ {'user_id': {'users': 'id'}, 'onDelete': 'CASCADE'}
94
+ {'user_id': {'users': 'id', 'onDelete': 'SET NULL'}}
95
+
96
+ """
97
+ try:
98
+ # Determine column order
99
+ if columnOrder:
100
+ # Use specified order, but include any columns not in the order list at the end
101
+ orderedColumns = []
102
+ for col in columnOrder:
103
+ if col in columnsDict:
104
+ orderedColumns.append(col)
105
+ # Add any remaining columns not in the order list
106
+ for col in columnsDict:
107
+ if col not in orderedColumns:
108
+ orderedColumns.append(col)
109
+ else:
110
+ orderedColumns = list(columnsDict.keys())
111
+
112
+ # Build column definitions
113
+ columnDefinitions = []
114
+ for col in orderedColumns:
115
+ definition = f"{col} {columnsDict[col]}"
116
+
117
+ # Add NOT NULL constraint if specified
118
+ if notNull and col in notNull:
119
+ definition += " NOT NULL"
120
+
121
+ columnDefinitions.append(definition)
122
+
123
+ # Handle foreign key constraints
124
+ if foreignKeys:
125
+ onDeleteClause = ""
126
+ if 'onDelete' in foreignKeys:
127
+ onDeleteClause = f" ON DELETE {foreignKeys['onDelete']}"
128
+
129
+ for currentField, foreignRef in foreignKeys.items():
130
+ if currentField != 'onDelete': # Skip the onDelete key
131
+ if isinstance(foreignRef, dict):
132
+ # Check if onDelete is specified at field level
133
+ if 'onDelete' in foreignRef:
134
+ fieldOnDelete = f" ON DELETE {foreignRef['onDelete']}"
135
+ # Get the actual foreign table and field (skip onDelete key)
136
+ foreignTable = None
137
+ foreignField = None
138
+ for key, value in foreignRef.items():
139
+ if key != 'onDelete':
140
+ foreignTable = key
141
+ foreignField = value
142
+ break
143
+ else:
144
+ # Use global onDelete clause and assume dict format {table: field}
145
+ fieldOnDelete = onDeleteClause
146
+ foreignTable = list(foreignRef.keys())[0]
147
+ foreignField = foreignRef[foreignTable]
148
+
149
+ if foreignTable and foreignField:
150
+ foreignKeyDef = f"FOREIGN KEY ({currentField}) REFERENCES {foreignTable}({foreignField}){fieldOnDelete}"
151
+ columnDefinitions.append(foreignKeyDef)
152
+
153
+ # Create the SQL statement
154
+ columnsSQL = ', '.join(columnDefinitions)
155
+ sql = f"CREATE TABLE {tableName} ({columnsSQL})"
156
+
157
+ self.cursor.execute(sql)
158
+ self.report(f"\t-> created table {tableName} in {self.db_name}")
159
+
160
+ except Exception as e:
161
+ self.report(f"\t! error creating table {tableName}: {str(e)}")
162
+
163
+ def renameTable(self, old_table_name, new_table_name, v=False) -> None:
71
164
  """
72
165
  this function gives a new name to an existing table and saves the changes
73
166
  """
@@ -77,7 +170,7 @@ class sqliteConnection:
77
170
  self.report("\t-> renamed " + old_table_name + " to " + new_table_name)
78
171
  self.commitChanges()
79
172
 
80
- def tableExists(self, table_name):
173
+ def tableExists(self, table_name) -> bool:
81
174
  self.cursor.execute("SELECT count(name) FROM sqlite_master WHERE type='table' AND name='{table_name}'".format(
82
175
  table_name=table_name))
83
176
  if self.cursor.fetchone()[0] == 1:
@@ -85,7 +178,7 @@ class sqliteConnection:
85
178
  else:
86
179
  return False
87
180
 
88
- def deleteRows(self, table_to_clean, col_where=None, col_where_value=None, v=False):
181
+ def deleteRows(self, table_to_clean, col_where=None, col_where_value=None, v=False) -> None:
89
182
  """
90
183
 
91
184
  """
@@ -103,17 +196,17 @@ class sqliteConnection:
103
196
  if v:
104
197
  self.report("\t-> removed all rows from " + table_to_clean)
105
198
 
106
- def deleteTable(self, table_name):
199
+ def deleteTable(self, table_name) -> None:
107
200
  """
108
201
  this function deletes the specified table
109
202
  """
110
203
  self.cursor.execute('''DROP TABLE ''' + table_name)
111
204
  self.report("\t-> deleted table " + table_name + " from " + self.db_name)
112
-
113
- def dropTable(self, table_name):
205
+
206
+ def dropTable(self, table_name) -> None:
114
207
  self.deleteTable(table_name)
115
208
 
116
- def undoChanges(self):
209
+ def undoChanges(self) -> None:
117
210
  """
118
211
  This function reverts the database to status before last commit
119
212
  """
@@ -121,7 +214,7 @@ class sqliteConnection:
121
214
  self.connection.rollback()
122
215
  self.commitChanges()
123
216
 
124
- def readTableAsDict(self, table_name, key_column = 'id'):
217
+ def readTableAsDict(self, table_name, key_column: str = 'id') -> dict:
125
218
  # Execute a SQL query to fetch all rows from your table
126
219
  self.cursor = self.connection.execute(f"SELECT * FROM {table_name}")
127
220
 
@@ -134,7 +227,7 @@ class sqliteConnection:
134
227
 
135
228
  return data
136
229
 
137
- def getColumnsWithTypes(self, table_name):
230
+ def getColumnsWithTypes(self, table_name) -> dict:
138
231
  c = self.cursor
139
232
 
140
233
  # Prepare and execute a PRAGMA table_info statement
@@ -145,7 +238,7 @@ class sqliteConnection:
145
238
 
146
239
  return columns_with_types
147
240
 
148
- def insertDictPartial(self, table_name, data_dict):
241
+ def insertDictPartial(self, table_name, data_dict) -> None:
149
242
  c = self.cursor
150
243
 
151
244
  # Get the column names from the table
@@ -168,7 +261,7 @@ class sqliteConnection:
168
261
  self.commitChanges()
169
262
 
170
263
 
171
- def report(self, string, printing=False):
264
+ def report(self, string, printing=False) -> None:
172
265
  if printing:
173
266
  print(f"\t> {string}")
174
267
  else:
@@ -176,7 +269,7 @@ class sqliteConnection:
176
269
  sys.stdout.flush()
177
270
 
178
271
 
179
- def createTableFromDict(self, table_name, columns_with_types):
272
+ def createTableFromDict(self, table_name, columns_with_types) -> None:
180
273
 
181
274
  # Prepare a CREATE TABLE statement
182
275
  fields = ', '.join(f'{column} {data_type}' for column, data_type in columns_with_types.items())
@@ -187,7 +280,7 @@ class sqliteConnection:
187
280
  self.commitChanges()
188
281
 
189
282
 
190
- def insertDict(self, table_name, data):
283
+ def insertDict(self, table_name, data) -> None:
191
284
 
192
285
  # Prepare an INSERT INTO statement for each dictionary
193
286
  for id, row in data.items():
@@ -204,7 +297,7 @@ class sqliteConnection:
204
297
 
205
298
 
206
299
 
207
- def readTableColumns(self, table_name, column_list="all"):
300
+ def readTableColumns(self, table_name, column_list="all") -> list:
208
301
  """
209
302
  this function takes a list to be a string separated by commmas and
210
303
  a table and puts the columns in the table into a variable
@@ -225,7 +318,7 @@ class sqliteConnection:
225
318
  self.report("\t-> read selected table columns from " + table_name)
226
319
  return list_of_tuples
227
320
 
228
- def insertField(self, table_name, field_name, data_type, to_new_line=False, messages=True):
321
+ def insertField(self, table_name, field_name, data_type, to_new_line=False, messages=True) -> None:
229
322
  """
230
323
  This will insert a new field into your sqlite database
231
324
 
@@ -244,7 +337,7 @@ class sqliteConnection:
244
337
  "\r\t-> inserted into table {0} field {1} ".format(table_name, field_name))
245
338
  sys.stdout.flush()
246
339
 
247
- def insertRow(self, table_name, ordered_content_list = [], dictionary_obj = {}, messages=False):
340
+ def insertRow(self, table_name, ordered_content_list = [], dictionary_obj = {}, messages=False) -> None:
248
341
  """
249
342
  ordered_list such as ['ha','he','hi']
250
343
  list should have data as strings
@@ -263,7 +356,7 @@ class sqliteConnection:
263
356
  if messages:
264
357
  self.report("\t-> inserted row into " + table_name)
265
358
 
266
- def insertRows(self, table_name, list_of_tuples, messages=False):
359
+ def insertRows(self, table_name, list_of_tuples, messages=False) -> None:
267
360
  """
268
361
  list_of_tuples such as [('ha','he','hi')'
269
362
  ('ha','he','hi')]
@@ -274,7 +367,7 @@ class sqliteConnection:
274
367
  if messages:
275
368
  self.report("\t-> inserted rows into " + table_name)
276
369
 
277
- def dumpCSV(self, table_name, file_name, index=False, v=False):
370
+ def dumpCSV(self, table_name, file_name, index=False, v=False) -> None:
278
371
  '''
279
372
  save table to csv
280
373
  '''
@@ -300,7 +393,11 @@ class sqliteConnection:
300
393
  self.report(
301
394
  "\t-> saved {0} changes to ".format(number_of_changes) + self.db_name)
302
395
 
303
- def closeConnection(self, commit=True):
396
+ def commit(self, v=False) -> None:
397
+ '''proxy to commitChanges'''
398
+ self.commitChanges(v)
399
+
400
+ def closeConnection(self, commit=True) -> None:
304
401
  '''
305
402
  disconnects from the database
306
403
  '''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccfx
3
- Version: 1.0.6
3
+ Version: 1.0.8
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-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ccfx"
7
- version = "1.0.6"
7
+ version = "1.0.8"
8
8
  description = "This package simplifies regular common actions for quick prototyping in a user friendly way"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes