ccfx 1.0.5__tar.gz → 1.0.7__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.5
3
+ Version: 1.0.7
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
@@ -25,6 +25,7 @@ Requires-Dist: py7zr
25
25
  Requires-Dist: mutagen
26
26
  Requires-Dist: requests
27
27
  Requires-Dist: tqdm
28
+ Requires-Dist: pillow
28
29
  Dynamic: license-file
29
30
 
30
31
  # ccfx
@@ -37,6 +37,7 @@ from tqdm import tqdm
37
37
  import yt_dlp
38
38
  from typing import Optional
39
39
  from datetime import datetime, timedelta
40
+ from PIL import Image
40
41
 
41
42
  # functions
42
43
  def listFiles(path: str, ext: Optional[str] = None) -> list:
@@ -1593,6 +1594,33 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
1593
1594
  print()
1594
1595
 
1595
1596
 
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
+
1596
1624
  def listAllFiles(folder, extension="*"):
1597
1625
  list_of_files = []
1598
1626
  # Getting the current work directory (cwd)
@@ -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.5
3
+ Version: 1.0.7
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
@@ -25,6 +25,7 @@ Requires-Dist: py7zr
25
25
  Requires-Dist: mutagen
26
26
  Requires-Dist: requests
27
27
  Requires-Dist: tqdm
28
+ Requires-Dist: pillow
28
29
  Dynamic: license-file
29
30
 
30
31
  # ccfx
@@ -13,3 +13,4 @@ py7zr
13
13
  mutagen
14
14
  requests
15
15
  tqdm
16
+ pillow
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ccfx"
7
- version = "1.0.5"
7
+ version = "1.0.7"
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"
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "mutagen",
31
31
  "requests",
32
32
  "tqdm",
33
+ "pillow",
33
34
  ]
34
35
 
35
36
  [project.urls]
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