julien-python-toolkit 0.2.1__tar.gz → 0.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.
Files changed (16) hide show
  1. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/PKG-INFO +11 -2
  2. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/google_services.py +139 -1
  3. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit.egg-info/PKG-INFO +11 -2
  4. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/setup.py +1 -1
  5. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/LICENSE.txt +0 -0
  6. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/README.md +0 -0
  7. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/__init__.py +0 -0
  8. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/email_logger.py +0 -0
  9. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/email_sender.py +0 -0
  10. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/file_utilities.py +0 -0
  11. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit/log_utilities.py +0 -0
  12. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit.egg-info/SOURCES.txt +0 -0
  13. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit.egg-info/dependency_links.txt +0 -0
  14. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit.egg-info/requires.txt +0 -0
  15. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/julien_python_toolkit.egg-info/top_level.txt +0 -0
  16. {julien_python_toolkit-0.2.1 → julien_python_toolkit-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: julien-python-toolkit
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Important code that I reuse through multiple projects. Please see license for allowed use.
5
5
  Home-page: https://github.com/JulienPython/JulienPythonToolkit-V001
6
6
  Author: Julien Python
@@ -16,6 +16,15 @@ Requires-Dist: google-auth==2.32.0
16
16
  Requires-Dist: google-auth-httplib2==0.2.0
17
17
  Requires-Dist: google-auth-oauthlib==1.2.1
18
18
  Requires-Dist: googleapis-common-protos==1.63.2
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: license
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
19
28
 
20
29
  # JulienPythonToolkit-V001
21
30
 
@@ -132,6 +132,25 @@ class GoogleServices():
132
132
  except Exception as error:
133
133
  raise Exception(f"Error initializing Google services: {error}")
134
134
 
135
+ @retry_if_network_error
136
+ def batch_update_spreadsheet(self, spreadsheet_id, body):
137
+
138
+ if self._sheet_service == None:
139
+ raise Exception("Spreadsheet service is not initialized. Please run the 'open' function.")
140
+
141
+ try:
142
+
143
+ response = self._sheet_service.spreadsheets().batchUpdate(
144
+ spreadsheetId=spreadsheet_id,
145
+ body=body
146
+ ).execute()
147
+
148
+ return response
149
+
150
+ except HttpError as error:
151
+ error_content = f"HttpError from {self.batch_update_spreadsheet.__name__}: {error.content}".encode('utf-8')
152
+ raise HttpError(resp=error.resp, content=error_content) from error
153
+
135
154
  @retry_if_network_error
136
155
  def write_csv_to_sheet(self, csv_data, spreadsheet_id, sheet_name):
137
156
 
@@ -173,6 +192,8 @@ class GoogleServices():
173
192
  @retry_if_network_error
174
193
  def write_data_to_sheet_batch_update(self, ranges, values, spreadsheet_id):
175
194
 
195
+ # TODO: Modify to use 'batch_update_spreadsheet' function to avoid code duplication.
196
+
176
197
  """
177
198
  Function to batch update values to google sheet.
178
199
 
@@ -253,6 +274,10 @@ class GoogleServices():
253
274
  @retry_if_network_error
254
275
  def get_sheet_names_from_sheet(self, spreadsheet_id):
255
276
 
277
+ logger.warning(f"Method '{self.get_sheet_names_from_sheet.__name__}' will be depreciated in a future version.")
278
+
279
+ # TODO: Use 'get_sheets_medatada_from_sheet' to get sheets metadata and then get sheets names, to avoid code duplication.
280
+
256
281
  if self._sheet_service is None:
257
282
  raise Exception("Spreadsheet service is not initialized. Please run the 'open' function.")
258
283
 
@@ -270,6 +295,24 @@ class GoogleServices():
270
295
  error_content = f"HttpError from {self.get_sheet_names_from_sheet.__name__}: {error.content}".encode('utf-8')
271
296
  raise HttpError(resp=error.resp, content=error_content) from error
272
297
 
298
+ @retry_if_network_error
299
+ def get_sheets_medatada_from_sheet(self, spreadsheet_id):
300
+
301
+ if self._sheet_service is None:
302
+ raise Exception("Spreadsheet service is not initialized. Please run the 'open' function.")
303
+
304
+ try:
305
+ # Call the Sheets API to get the spreadsheet
306
+ spreadsheet = self._sheet_service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute()
307
+
308
+ # Extract the sheet names
309
+ sheets_metadata = spreadsheet.get('sheets', [])
310
+ return sheets_metadata
311
+
312
+ except HttpError as error:
313
+ error_content = f"HttpError from {self.get_sheets_medatada_from_sheet.__name__}: {error.content}".encode('utf-8')
314
+ raise HttpError(resp=error.resp, content=error_content) from error
315
+
273
316
  def get_subfolders_in_folder(self, folder_id):
274
317
 
275
318
  if self._drive_service == None:
@@ -381,4 +424,99 @@ class GoogleServices():
381
424
  file = self._drive_service.files().create(body=file_metadata, media_body=media, fields='id').execute()
382
425
 
383
426
  except Exception as error:
384
- raise Exception(f"Error uploading file to folder: {error}")
427
+ raise Exception(f"Error uploading file to folder: {error}")
428
+
429
+ # Non network functions.
430
+
431
+ def duplicate_sheet(self, spreadsheet_id, source_sheet_id, insert_index, new_sheet_name):
432
+
433
+ if not isinstance(insert_index, int):
434
+ raise Exception(f"Insert index '{insert_index}' is not an integer.")
435
+
436
+ if not isinstance(new_sheet_name, str):
437
+ raise Exception(f"New sheet name '{new_sheet_name}' is not a string.")
438
+
439
+ if len(new_sheet_name) > 50:
440
+ raise Exception(f"New sheet name '{new_sheet_name}' has {len(new_sheet_name)} characters, but max. is 50 characters.")
441
+
442
+ request_body = {
443
+ "requests": [
444
+ {
445
+ "duplicateSheet": {
446
+ "sourceSheetId": source_sheet_id,
447
+ # Insert at the specified index
448
+ "insertSheetIndex": insert_index,
449
+ # New sheet name
450
+ "newSheetName": new_sheet_name
451
+ }
452
+ }
453
+ ]
454
+ }
455
+
456
+ return self.batch_update_spreadsheet(spreadsheet_id, request_body)
457
+
458
+ def delete_sheets_from_spreadsheet(self, spreadsheet_id, ids_of_sheets_to_delete):
459
+
460
+ if not isinstance(ids_of_sheets_to_delete, list):
461
+ raise Exception(f"Sheet ids '{ids_of_sheets_to_delete}' is not a list.")
462
+
463
+ if len(ids_of_sheets_to_delete) == 0:
464
+ raise Exception(f"Sheet ids list '{ids_of_sheets_to_delete}' is empty.")
465
+
466
+ requests = []
467
+ for sheet_id in ids_of_sheets_to_delete:
468
+ requests.append({
469
+ "deleteSheet": {
470
+ "sheetId": sheet_id
471
+ }
472
+ })
473
+
474
+ request_body = {
475
+ "requests": requests
476
+ }
477
+
478
+ return self.batch_update_spreadsheet(spreadsheet_id, request_body)
479
+
480
+ def reorder_all_sheets_in_spreadsheet(self, spreadsheet_id, sheet_ids_in_new_order):
481
+
482
+ # Step 0: Initial checks.
483
+ if not isinstance(sheet_ids_in_new_order, list):
484
+ raise Exception(f"Sheet names '{sheet_ids_in_new_order}' is not a list.")
485
+
486
+ if len(sheet_ids_in_new_order) == 0:
487
+ raise Exception(f"Sheet names list '{sheet_ids_in_new_order}' is empty.")
488
+
489
+ # Step 1: Check that each sheet id is an integer.
490
+ for sheet_id in sheet_ids_in_new_order:
491
+ if not isinstance(sheet_id, int):
492
+ raise Exception(f"Sheet id '{sheet_id}' is not an integer.")
493
+
494
+ # Step 2: Check that there are no duplicate sheet ids.
495
+ if len(sheet_ids_in_new_order) != len(set(sheet_ids_in_new_order)):
496
+ raise Exception(f"Sheet ids list '{sheet_ids_in_new_order}' contains duplicate sheet ids.")
497
+
498
+ # Step 3: Get the original sheet ids.
499
+ sheet_metadata = self.get_sheets_medatada_from_sheet(spreadsheet_id)
500
+ original_sheet_ids = [sheet['properties']['sheetId'] for sheet in sheet_metadata]
501
+
502
+ # Step 4: Check that the set of original sheet ids and new sheet ids are the same.
503
+ if set(original_sheet_ids) != set(sheet_ids_in_new_order):
504
+ raise Exception(f"Original sheet ids '{original_sheet_ids}' and new sheet ids '{sheet_ids_in_new_order}' are not the same.")
505
+
506
+ requests = []
507
+ for index, sheet_id in enumerate(sheet_ids_in_new_order):
508
+ requests.append({
509
+ "updateSheetProperties": {
510
+ "properties": {
511
+ "sheetId": sheet_id,
512
+ "index": index
513
+ },
514
+ "fields": "index"
515
+ }
516
+ })
517
+
518
+ request_body = {
519
+ "requests": requests
520
+ }
521
+
522
+ return self.batch_update_spreadsheet(spreadsheet_id, request_body)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: julien-python-toolkit
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Important code that I reuse through multiple projects. Please see license for allowed use.
5
5
  Home-page: https://github.com/JulienPython/JulienPythonToolkit-V001
6
6
  Author: Julien Python
@@ -16,6 +16,15 @@ Requires-Dist: google-auth==2.32.0
16
16
  Requires-Dist: google-auth-httplib2==0.2.0
17
17
  Requires-Dist: google-auth-oauthlib==1.2.1
18
18
  Requires-Dist: googleapis-common-protos==1.63.2
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: license
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
19
28
 
20
29
  # JulienPythonToolkit-V001
21
30
 
@@ -11,7 +11,7 @@ with open('requirements.txt') as f:
11
11
 
12
12
  setup(
13
13
  name='julien-python-toolkit',
14
- version='0.2.1',
14
+ version='0.2.2',
15
15
  packages=find_packages(),
16
16
  license='Custom Non-Commercial License', # Reference your custom license
17
17
  install_requires=required, # Use the list from requirements.txt