MilanM commited on
Commit
1817803
·
verified ·
1 Parent(s): becfb49

Update main_app.py

Browse files
Files changed (1) hide show
  1. main_app.py +426 -179
main_app.py CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
  import marimo
4
 
5
  __generated_with = "0.13.0"
@@ -44,6 +42,7 @@ def _(os):
44
  from typing import Any, Dict, List, Optional, Pattern, Set, Union, Tuple
45
  from ibm_watsonx_ai import APIClient, Credentials
46
  from pathlib import Path
 
47
  import pandas as pd
48
  import mimetypes
49
  import requests
@@ -51,11 +50,13 @@ def _(os):
51
  import polars
52
  import urllib3
53
  import tempfile
 
54
  import base64
55
  import uuid
56
- import ssl
57
  import time
58
  import json
 
 
59
  import ast
60
  import io
61
  import re
@@ -63,6 +64,9 @@ def _(os):
63
  # Set explicit temporary directory
64
  os.environ['TMPDIR'] = '/tmp/notebook_functions'
65
 
 
 
 
66
  # Make sure Python's tempfile module also uses this directory
67
  tempfile.tempdir = '/tmp/notebook_functions'
68
 
@@ -110,9 +114,11 @@ def _(os):
110
  Credentials,
111
  ast,
112
  get_iam_token,
 
113
  json,
114
  pd,
115
  setup_task_credentials,
 
116
  tempfile,
117
  uuid,
118
  )
@@ -222,25 +228,32 @@ def _(
222
 
223
  if space_id:
224
  deployment_client = APIClient(credentials=wx_credentials, space_id=space_id)
 
225
  else:
226
  deployment_client = None
 
227
 
228
  if project_client is not None:
229
  task_credentials_details = setup_task_credentials(project_client)
230
  else:
231
- task_credentials_details = setup_task_credentials(deployment_client)
 
232
  else:
233
  wx_credentials = None
234
  project_client = None
235
  deployment_client = None
236
  task_credentials_details = None
 
 
237
 
 
 
 
238
  if project_client is not None or deployment_client is not None:
239
  client_callout_kind = "success"
240
  else:
241
  client_callout_kind = "neutral"
242
-
243
- return client_callout_kind, project_client, wx_credentials, deployment_client, task_credentials_details
244
 
245
 
246
  @app.cell
@@ -261,24 +274,7 @@ def _(client_callout_kind, client_instantiation_form, mo, template_variant):
261
 
262
 
263
  @app.cell
264
- def _(
265
- client_stack,
266
- deploy_fnc,
267
- deployment_definition,
268
- fm,
269
- function_editor,
270
- hw_selection_table,
271
- mo,
272
- package_analysis_stack,
273
- package_meta,
274
- purge_tabs,
275
- sc_m,
276
- schema_editors,
277
- selection_table,
278
- ss_asset_details,
279
- upload_func,
280
- yaml_template,
281
- ):
282
  client_section = mo.md(f'''
283
  ###**Instantiate your watsonx.ai client:**
284
 
@@ -295,7 +291,11 @@ def _(
295
  {client_stack}
296
 
297
  ''')
 
298
 
 
 
 
299
  sc_tabs = mo.ui.tabs(
300
  {
301
  "Schema Option Selection": sc_m,
@@ -304,7 +304,11 @@ def _(
304
  {schema_editors}"""),
305
  }
306
  )
 
 
307
 
 
 
308
  function_section = mo.md(f'''###**Create your function from the template:**
309
 
310
  1. Use the code editor window to create a function to deploy
@@ -343,7 +347,11 @@ def _(
343
  {fm}
344
 
345
  ''')
 
 
346
 
 
 
347
  upload_section = mo.md(f'''
348
  ###**Review and Upload your function**
349
 
@@ -364,7 +372,11 @@ def _(
364
  {upload_func}
365
 
366
  ''')
 
367
 
 
 
 
368
  deployment_section = mo.md(f'''
369
  ###**Deploy your function:**
370
 
@@ -397,7 +409,11 @@ def _(
397
  {deploy_fnc}
398
 
399
  ''')
 
 
400
 
 
 
401
  purging_section = mo.md(f'''
402
  ###**Helper Purge Functions:**
403
 
@@ -411,7 +427,18 @@ def _(
411
 
412
  ''')
413
 
 
 
414
 
 
 
 
 
 
 
 
 
 
415
  packages_section = mo.md(f'''
416
  ###**If needed - Create a custom software-spec with added python packages**
417
 
@@ -439,18 +466,12 @@ def _(
439
 
440
  ---
441
 
442
- Results:
443
-
444
- {ss_asset_details}
445
-
446
  ''')
447
- return (
448
- client_section,
449
- deployment_section,
450
- function_section,
451
- packages_section,
452
- upload_section,
453
- )
454
 
455
 
456
  @app.cell
@@ -499,9 +520,9 @@ def _(deployment_section, mo):
499
 
500
 
501
  @app.cell
502
- def _(mo, packages_section):
503
  ui_accordion_section_6 = mo.accordion(
504
- {"Section 6: **Helper Functions**": packages_section}
505
  )
506
  ui_accordion_section_6
507
  return
@@ -647,19 +668,8 @@ def _(tempfile):
647
  def create_yaml_tempfile(yaml_editor_value):
648
  """Creates temporary YAML file and returns its path"""
649
  temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
650
-
651
- # Access the actual YAML content within the dictionary structure
652
- if isinstance(yaml_editor_value, dict) and 'yml_editor' in yaml_editor_value:
653
- # Extract the YAML content string from the yml_editor key
654
- yaml_content = yaml_editor_value['yml_editor']
655
- else:
656
- # Use as is if it's already a string or has unexpected structure
657
- yaml_content = yaml_editor_value
658
-
659
- # Write the content to the file
660
  with open(temp_file.name, 'w') as f:
661
- f.write(str(yaml_content))
662
-
663
  return temp_file.name
664
  return (create_yaml_tempfile,)
665
 
@@ -760,10 +770,10 @@ def _(check_packages, mo, package_df):
760
 
761
 
762
  @app.cell
763
- def _(deployment_client):
764
- if deployment_client is not None:
765
  base_sw_spec_id = "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
766
- base_software_spec = deployment_client.software_specifications.get_details(base_sw_spec_id)
767
  else:
768
  base_sw_spec_id = None
769
  base_software_spec = None
@@ -771,12 +781,14 @@ def _(deployment_client):
771
 
772
 
773
  @app.cell
774
- def _(create_yaml_tempfile, deployment_client, package_meta):
775
- if package_meta.value is not None and deployment_client is not None:
 
 
776
  pe_metadata = {
777
- deployment_client.package_extensions.ConfigurationMetaNames.NAME: package_meta.value['package_name'],
778
- deployment_client.package_extensions.ConfigurationMetaNames.TYPE: package_meta.value['package_type'],
779
- deployment_client.software_specifications.ConfigurationMetaNames.DESCRIPTION:package_meta.value['package_description']
780
  }
781
  yaml_file_path = create_yaml_tempfile(package_meta.value['yml_editor'])
782
  else:
@@ -786,7 +798,23 @@ def _(create_yaml_tempfile, deployment_client, package_meta):
786
 
787
  return pe_metadata, yaml_file_path
788
 
789
- @app.cell(hide_code=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  def _():
791
  ### Helper function for checking if a python library is in the standard software spec.
792
  def analyze_software_spec(sw_spec_response, required_libraries, return_full_sw_package_list=False):
@@ -973,47 +1001,51 @@ def _():
973
 
974
 
975
  @app.cell
976
- def _(
977
- base_sw_spec_id,
978
- deployment_client,
979
- package_meta,
980
- pe_metadata,
981
- yaml_file_path,
982
- ):
983
- if yaml_file_path is not None:
984
- ### Stores the package extension
985
- pe_asset_details = deployment_client.package_extensions.store(
986
- meta_props=pe_metadata,
987
- file_path=yaml_file_path
988
- )
989
- package_id = pe_asset_details["metadata"]["asset_id"]
990
  ### Creates a custom software specification based on the standard python function spec_id - "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
 
 
 
991
  ss_metadata = {
992
- deployment_client.software_specifications.ConfigurationMetaNames.NAME: package_meta.value['software_spec_name'],
993
- deployment_client.software_specifications.ConfigurationMetaNames.DESCRIPTION: package_meta.value['software_spec_description'],
994
- deployment_client.software_specifications.ConfigurationMetaNames.BASE_SOFTWARE_SPECIFICATION: {'guid': base_sw_spec_id},
995
- deployment_client.software_specifications.ConfigurationMetaNames.PACKAGE_EXTENSIONS: [{'guid': package_id}]
996
  }
997
- ss_asset_details = deployment_client.software_specifications.store(meta_props=ss_metadata)
 
 
 
 
 
998
  else:
999
- pe_asset_details = None
1000
- package_id = None
1001
  ss_metadata = {}
1002
  ss_asset_details = None
 
1003
 
1004
  # ss_asset_details
1005
- return (ss_asset_details,)
1006
 
1007
 
1008
  @app.cell
1009
- def _(deployment_client, mo, pd):
1010
- if deployment_client:
1011
- supported_specs = deployment_client.software_specifications.list()[
1012
- deployment_client.software_specifications.list()['STATE'] == 'supported'
 
 
 
 
 
1013
  ]
1014
 
1015
- # Reset the index to start from 0
1016
- supported_specs = supported_specs.reset_index(drop=True)
 
 
 
 
1017
 
1018
  # Create a mapping dictionary for framework names based on software specifications
1019
  framework_mapping = {
@@ -1072,20 +1104,46 @@ def _(deployment_client, mo, pd):
1072
  )
1073
  else:
1074
  sel_df = pd.DataFrame(
1075
- data=[["ID", "Activate deployment_client."]],
1076
  columns=["ID", "VALUE"]
1077
  )
1078
 
1079
  selection_table = mo.ui.table(
1080
  sel_df,
1081
  selection="single", # Only allow selecting one row
1082
- label="You haven't activated the Deployment_Client",
1083
  initial_selection=[0]
1084
  )
1085
 
1086
  return (selection_table,)
1087
 
1088
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1089
  @app.cell
1090
  def _(mo):
1091
  input_schema_checkbox = mo.ui.checkbox(label="Add input schema (optional)")
@@ -1097,13 +1155,14 @@ def _(mo):
1097
  @app.cell
1098
  def _(
1099
  function_name,
 
1100
  input_schema_checkbox,
1101
  mo,
1102
  output_schema_checkbox,
1103
  selection_table,
1104
  template_variant,
1105
  ):
1106
- if selection_table.value['ID'].iloc[0]:
1107
  # Create the input fields
1108
  if function_name is not None:
1109
  fnc_nm = function_name
@@ -1118,7 +1177,7 @@ def _(
1118
  [mo.ui.text(placeholder="Metadata Tags..."), mo.ui.text(), mo.ui.text()],
1119
  label="Optional Metadata Tags"
1120
  )
1121
- software_spec = selection_table.value['ID'].iloc[0]
1122
 
1123
  description_input = mo.ui.text_area(
1124
  placeholder="Write a description for your function...)",
@@ -1231,9 +1290,10 @@ def _(json, mo, template_variant):
1231
  @app.cell
1232
  def _(
1233
  ast,
1234
- deployment_client,
1235
  description_input,
1236
  function_editor,
 
1237
  input_schema_checkbox,
1238
  input_schema_editor,
1239
  json,
@@ -1241,8 +1301,8 @@ def _(
1241
  os,
1242
  output_schema_checkbox,
1243
  output_schema_editor,
1244
- selection_table,
1245
  software_spec,
 
1246
  tags_editor,
1247
  uploaded_function_name,
1248
  ):
@@ -1250,11 +1310,11 @@ def _(
1250
 
1251
  function_meta = {}
1252
 
1253
- if selection_table.value['ID'].iloc[0] and deployment_client is not None:
1254
  # Start with the base required fields
1255
  function_meta = {
1256
- deployment_client.repository.FunctionMetaNames.NAME: f"{uploaded_function_name.value}" or "your_function_name",
1257
- deployment_client.repository.FunctionMetaNames.SOFTWARE_SPEC_ID: software_spec or "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
1258
  }
1259
 
1260
  # Add optional fields if they exist
@@ -1262,35 +1322,28 @@ def _(
1262
  # Filter out empty strings from the tags list
1263
  filtered_tags = [tag for tag in tags_editor.value if tag and tag.strip()]
1264
  if filtered_tags: # Only add if there are non-empty tags
1265
- function_meta[deployment_client.repository.FunctionMetaNames.TAGS] = filtered_tags
1266
 
1267
 
1268
  if description_input.value:
1269
- function_meta[deployment_client.repository.FunctionMetaNames.DESCRIPTION] = description_input.value
1270
 
1271
  # Add input schema if checkbox is checked
1272
  if input_schema_checkbox.value:
1273
  try:
1274
- function_meta[deployment_client.repository.FunctionMetaNames.INPUT_DATA_SCHEMAS] = json.loads(input_schema_editor.value)
1275
  except json.JSONDecodeError:
1276
  # If JSON parsing fails, try Python literal evaluation as fallback
1277
- function_meta[deployment_client.repository.FunctionMetaNames.INPUT_DATA_SCHEMAS] = ast.literal_eval(input_schema_editor.value)
1278
 
1279
  # Add output schema if checkbox is checked
1280
  if output_schema_checkbox.value:
1281
  try:
1282
- function_meta[deployment_client.repository.FunctionMetaNames.OUTPUT_DATA_SCHEMAS] = json.loads(output_schema_editor.value)
1283
  except json.JSONDecodeError:
1284
  # If JSON parsing fails, try Python literal evaluation as fallback
1285
- function_meta[deployment_client.repository.FunctionMetaNames.OUTPUT_DATA_SCHEMAS] = ast.literal_eval(output_schema_editor.value)
1286
 
1287
- # Add sample input if checkbox is checked
1288
- # if sample_input_checkbox.value:
1289
- # try:
1290
- # function_meta[deployment_client.repository.FunctionMetaNames.SAMPLE_SCORING_INPUT] = json.loads(sample_input_editor.value)
1291
- # except json.JSONDecodeError:
1292
- # # If JSON parsing fails, try Python literal evaluation as fallback
1293
- # function_meta[deployment_client.repository.FunctionMetaNames.SAMPLE_SCORING_INPUT] = ast.literal_eval(sample_input_editor.value)
1294
 
1295
  def upload_function(function_meta, use_function_object=False):
1296
  """
@@ -1310,7 +1363,7 @@ def _(
1310
  # This function is defined elsewhere in the notebook
1311
  func_name = uploaded_function_name.value or "your_function_name"
1312
  # Ensure function_meta has the correct function name
1313
- function_meta[deployment_client.repository.FunctionMetaNames.NAME] = func_name
1314
  # Save the file locally first
1315
  save_dir = "/tmp/notebook_functions"
1316
  os.makedirs(save_dir, exist_ok=True)
@@ -1320,8 +1373,6 @@ def _(
1320
 
1321
  if use_function_object:
1322
  # Import the function from the file
1323
- import sys
1324
- import importlib.util
1325
  # Add the directory to Python's path
1326
  sys.path.append(save_dir)
1327
  # Import the module
@@ -1336,19 +1387,11 @@ def _(
1336
 
1337
  # Upload the function object
1338
  mo.md(f"Uploading function object: {func_name}")
1339
- func_details = deployment_client.repository.store_function(function_object, function_meta)
1340
  else:
1341
  # Change to /tmp directory before calling IBM Watson SDK functions
1342
  os.chdir('/tmp/notebook_functions')
1343
 
1344
- # # Upload using the absolute file path
1345
- # abs_file_path = os.path.abspath(file_path)
1346
- # mo.md(f"Uploading function from file: {abs_file_path}")
1347
-
1348
- # # Using the absolute file path might help in some environments
1349
- # func_details = deployment_client.repository.store_function(abs_file_path, function_meta)
1350
- # Upload using the file path approach
1351
-
1352
  # Create a zip file of the Python module
1353
  import gzip
1354
  import shutil
@@ -1363,10 +1406,7 @@ def _(
1363
 
1364
  # Upload using the gzipped file path
1365
  mo.md(f"Uploading function from gzip: {gz_path}")
1366
- func_details = deployment_client.repository.store_function(gz_path, function_meta)
1367
-
1368
- # mo.md(f"Uploading function from file: {file_path}")
1369
- # func_details = deployment_client.repository.store_function(file_path, function_meta)
1370
 
1371
  set_upload_status(f"Latest Upload - id - {func_details['metadata']['id']}")
1372
  return func_details
@@ -1409,7 +1449,7 @@ def _(get_upload_status, mo, upload_button):
1409
 
1410
 
1411
  @app.cell
1412
- def _(deployment_client, mo, pd, upload_button, uuid):
1413
  def reorder_hardware_specifications(df):
1414
  """
1415
  Reorders a hardware specifications dataframe by type and size of environment
@@ -1454,9 +1494,9 @@ def _(deployment_client, mo, pd, upload_button, uuid):
1454
 
1455
  return result_df
1456
 
1457
- if deployment_client and upload_button.value:
1458
 
1459
- hardware_specs = deployment_client.hardware_specifications.list()
1460
  hardware_specs_df = reorder_hardware_specifications(hardware_specs)
1461
 
1462
  # Create a table with single-row selection
@@ -1477,14 +1517,14 @@ def _(deployment_client, mo, pd, upload_button, uuid):
1477
  deployment_name = mo.ui.text(value=f"deployed_func_{uuid_suffix}", label="Deployment Name:", placeholder="<Must be completely unique>")
1478
  else:
1479
  hw_df = pd.DataFrame(
1480
- data=[["ID", "Activate deployment_client."]],
1481
  columns=["ID", "VALUE"]
1482
  )
1483
 
1484
  hw_selection_table = mo.ui.table(
1485
  hw_df,
1486
  selection="single", # Only allow selecting one row
1487
- label="You haven't activated the Deployment_Client",
1488
  initial_selection=[0]
1489
  )
1490
 
@@ -1494,10 +1534,11 @@ def _(deployment_client, mo, pd, upload_button, uuid):
1494
  @app.cell
1495
  def _(
1496
  artifact_id,
1497
- deployment_client,
1498
  deployment_details,
1499
  deployment_name,
1500
  deployment_type,
 
1501
  hw_selection_table,
1502
  mo,
1503
  upload_button,
@@ -1519,27 +1560,27 @@ def _(
1519
 
1520
  if deployment_type.value == "Online (Function Endpoint)": # Changed from "Online (Function Endpoint)"
1521
  deployment_props = {
1522
- deployment_client.deployments.ConfigurationMetaNames.NAME: deployment_name.value,
1523
- deployment_client.deployments.ConfigurationMetaNames.ONLINE: {},
1524
- deployment_client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {"id": selected_hw_config},
1525
- deployment_client.deployments.ConfigurationMetaNames.SERVING_NAME: deployment_name.value,
1526
  }
1527
  else: # "Runnable Job" instead of "Batch (Runnable Jobs)"
1528
  deployment_props = {
1529
- deployment_client.deployments.ConfigurationMetaNames.NAME: deployment_name.value,
1530
- deployment_client.deployments.ConfigurationMetaNames.BATCH: {},
1531
- deployment_client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {"id": selected_hw_config},
1532
  # batch does not use serving names
1533
  }
1534
 
1535
  try:
1536
  print(deployment_props)
1537
  # First, get the asset details to confirm it exists
1538
- asset_details = deployment_client.repository.get_details(artifact_id)
1539
  print(f"Asset found: {asset_details['metadata']['name']} with ID: {asset_details['metadata']['id']}")
1540
 
1541
  # Create the deployment
1542
- deployed_function = deployment_client.deployments.create(artifact_id, deployment_props)
1543
  print(f"Creating deployment from Asset: {artifact_id} with deployment properties {str(deployment_props)}")
1544
  return deployed_function
1545
  except Exception as e:
@@ -1547,17 +1588,17 @@ def _(
1547
  return None
1548
 
1549
  def get_deployment_id(deployed_function):
1550
- deployment_id = deployment_client.deployments.get_uid(deployment_details)
1551
  return deployment_id
1552
 
1553
  def get_deployment_info(deployment_id):
1554
- deployment_info = deployment_client.deployments.get_details(deployment_id)
1555
  return deployment_info
1556
 
1557
  deployment_status = mo.state("No deployments yet")
1558
 
1559
- if hw_selection_table.value['ID'].iloc[0]:
1560
- selected_hw_config = hw_selection_table.value['ID'].iloc[0]
1561
 
1562
  deploy_button = mo.ui.button(
1563
  label="Deploy Function",
@@ -1566,7 +1607,7 @@ def _(
1566
  tooltip="Click to deploy function to watsonx.ai"
1567
  )
1568
 
1569
- if deployment_client and upload_button.value:
1570
  deployment_definition = mo.hstack([
1571
  deployment_type,
1572
  deployment_name
@@ -1593,13 +1634,17 @@ def _(deploy_button, deployment_definition, mo):
1593
  return (deploy_fnc,)
1594
 
1595
 
1596
- @app.cell(hide_code=True)
1597
- def _(deployment_client, mo, pd):
1598
  ### Functions to List , Get ID's as a list and Purge of Assets
1599
 
1600
  def get_deployment_list():
1601
- dep_df = deployment_client.deployments.list()
1602
  dep_df = pd.DataFrame(dep_df)
 
 
 
 
1603
  return dep_df
1604
 
1605
  def get_deployment_ids(df):
@@ -1609,7 +1654,7 @@ def _(deployment_client, mo, pd):
1609
  #----
1610
 
1611
  def get_data_assets_list():
1612
- data_a_df = deployment_client.data_assets.list()
1613
  data_a_df = pd.DataFrame(data_a_df)
1614
  return data_a_df
1615
 
@@ -1620,8 +1665,12 @@ def _(deployment_client, mo, pd):
1620
  #----
1621
 
1622
  def get_repository_list():
1623
- rep_list_df = deployment_client.repository.list()
1624
  rep_list_df = pd.DataFrame(rep_list_df)
 
 
 
 
1625
  return rep_list_df
1626
 
1627
  def get_repository_ids(df):
@@ -1630,63 +1679,130 @@ def _(deployment_client, mo, pd):
1630
 
1631
  #----
1632
 
1633
- def delete_with_progress(ids_list, delete_function, item_type="items"):
1634
- """
1635
- Generic wrapper that adds a progress bar to any deletion function
 
1636
 
1637
- Parameters:
1638
- ids_list: List of IDs to delete
1639
- delete_function: Function that deletes a single ID
1640
- item_type: String describing what's being deleted (for display)
1641
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1642
  with mo.status.progress_bar(
1643
  total=len(ids_list) or 1,
1644
  title=f"Purging {item_type}",
1645
  subtitle=f"Deleting {item_type}...",
1646
  completion_title="Purge Complete",
1647
- completion_subtitle=f"Successfully deleted {len(ids_list)} {item_type}"
 
1648
  ) as progress:
1649
  for item_id in ids_list:
1650
- delete_function(item_id)
1651
- progress.update(increment=1)
1652
- return f"Deleted {len(ids_list)} {item_type} successfully"
1653
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1654
  # Use with existing deletion functions
1655
  def delete_deployments(deployment_ids):
1656
  return delete_with_progress(
1657
  deployment_ids,
1658
- lambda id: deployment_client.deployments.delete(id),
1659
  "deployments"
1660
  )
1661
 
1662
  def delete_data_assets(data_asset_ids):
1663
  return delete_with_progress(
1664
  data_asset_ids,
1665
- lambda id: deployment_client.data_assets.delete(id),
1666
  "data assets"
1667
  )
1668
 
1669
  def delete_repository_items(repository_ids):
1670
  return delete_with_progress(
1671
  repository_ids,
1672
- lambda id: deployment_client.repository.delete(id),
1673
  "repository items"
1674
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1675
  return (
1676
  delete_data_assets,
1677
  delete_deployments,
 
1678
  delete_repository_items,
 
1679
  get_data_asset_ids,
1680
  get_data_assets_list,
1681
  get_deployment_ids,
1682
  get_deployment_list,
 
 
1683
  get_repository_ids,
1684
  get_repository_list,
 
 
1685
  )
1686
 
1687
 
1688
  @app.cell
1689
- def _(get_data_assets_tab, get_deployments_tab, get_repository_tab, mo):
 
 
 
 
 
 
 
1690
  if get_deployments_tab() is not None:
1691
  deployments_table = mo.ui.table(get_deployments_tab())
1692
  else:
@@ -1702,7 +1818,24 @@ def _(get_data_assets_tab, get_deployments_tab, get_repository_tab, mo):
1702
  else:
1703
  data_assets_table = mo.md("No Table Loaded")
1704
 
1705
- return data_assets_table, deployments_table, repository_table
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1706
 
1707
 
1708
  @app.cell
@@ -1729,7 +1862,6 @@ def _(
1729
  repository_table,
1730
  ):
1731
  repository_purge_stack = mo.hstack([get_repository_button, get_repository_id_list, purge_repository])
1732
-
1733
  repository_purge_stack_results = mo.vstack([repository_table, get_repository_id_list.value, purge_repository.value])
1734
 
1735
  repository_purge_tab = mo.vstack([repository_purge_stack, repository_purge_stack_results])
@@ -1752,9 +1884,47 @@ def _(
1752
 
1753
 
1754
  @app.cell
1755
- def _(data_assets_purge_tab, deployments_purge_tab, mo, repository_purge_tab):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1756
  purge_tabs = mo.ui.tabs(
1757
- {"Purge Deployments": deployments_purge_tab, "Purge Repository Assets": repository_purge_tab,"Purge Data Assets": data_assets_purge_tab }, lazy=False
 
 
 
 
 
 
 
1758
  )
1759
 
1760
  return (purge_tabs,)
@@ -1763,36 +1933,61 @@ def _(data_assets_purge_tab, deployments_purge_tab, mo, repository_purge_tab):
1763
  @app.cell
1764
  def _(mo):
1765
  get_deployments_tab, set_deployments_tab = mo.state(None)
 
 
 
 
 
1766
  get_repository_tab, set_repository_tab = mo.state(None)
 
 
 
 
 
1767
  get_data_assets_tab, set_data_assets_tab = mo.state(None)
1768
- return (
1769
- get_data_assets_tab,
1770
- get_deployments_tab,
1771
- get_repository_tab,
1772
- set_data_assets_tab,
1773
- set_deployments_tab,
1774
- set_repository_tab,
1775
- )
1776
 
1777
 
1778
- @app.cell(hide_code=True)
 
 
 
 
 
 
 
 
 
 
 
 
1779
  def _(
1780
  data_assets_table,
1781
  delete_data_assets,
1782
  delete_deployments,
 
1783
  delete_repository_items,
 
1784
  deployments_table,
1785
  get_data_asset_ids,
1786
  get_data_assets_list,
1787
  get_deployment_ids,
1788
  get_deployment_list,
 
 
1789
  get_repository_ids,
1790
  get_repository_list,
 
 
1791
  mo,
 
1792
  repository_table,
1793
  set_data_assets_tab,
1794
  set_deployments_tab,
 
1795
  set_repository_tab,
 
 
1796
  ):
1797
  ### Temporary Function Purge - Assets
1798
  get_data_assets_button = mo.ui.button(
@@ -1853,18 +2048,70 @@ def _(
1853
  on_click=lambda _: delete_repository_items(get_repository_id_list.value),
1854
  kind="danger",
1855
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1856
  return (
1857
  get_data_asset_id_list,
1858
  get_data_assets_button,
1859
  get_deployment_id_list,
1860
  get_deployments_button,
 
 
1861
  get_repository_button,
1862
  get_repository_id_list,
 
 
1863
  purge_data_assets,
1864
  purge_deployments,
 
1865
  purge_repository,
 
1866
  )
1867
 
1868
 
 
 
 
 
 
1869
  if __name__ == "__main__":
1870
  app.run()
 
 
 
1
  import marimo
2
 
3
  __generated_with = "0.13.0"
 
42
  from typing import Any, Dict, List, Optional, Pattern, Set, Union, Tuple
43
  from ibm_watsonx_ai import APIClient, Credentials
44
  from pathlib import Path
45
+ import importlib.util
46
  import pandas as pd
47
  import mimetypes
48
  import requests
 
50
  import polars
51
  import urllib3
52
  import tempfile
53
+ import importlib.util
54
  import base64
55
  import uuid
 
56
  import time
57
  import json
58
+ import sys
59
+ import ssl
60
  import ast
61
  import io
62
  import re
 
64
  # Set explicit temporary directory
65
  os.environ['TMPDIR'] = '/tmp/notebook_functions'
66
 
67
+ # Create the directory if it doesn't exist
68
+ os.makedirs('/tmp/notebook_functions', exist_ok=True)
69
+
70
  # Make sure Python's tempfile module also uses this directory
71
  tempfile.tempdir = '/tmp/notebook_functions'
72
 
 
114
  Credentials,
115
  ast,
116
  get_iam_token,
117
+ importlib,
118
  json,
119
  pd,
120
  setup_task_credentials,
121
+ sys,
122
  tempfile,
123
  uuid,
124
  )
 
228
 
229
  if space_id:
230
  deployment_client = APIClient(credentials=wx_credentials, space_id=space_id)
231
+ client = deployment_client
232
  else:
233
  deployment_client = None
234
+ client = None
235
 
236
  if project_client is not None:
237
  task_credentials_details = setup_task_credentials(project_client)
238
  else:
239
+ task_credentials_details = setup_task_credentials(client)
240
+
241
  else:
242
  wx_credentials = None
243
  project_client = None
244
  deployment_client = None
245
  task_credentials_details = None
246
+ client = None
247
+ return client, deployment_client, project_client
248
 
249
+
250
+ @app.cell
251
+ def _(deployment_client, project_client):
252
  if project_client is not None or deployment_client is not None:
253
  client_callout_kind = "success"
254
  else:
255
  client_callout_kind = "neutral"
256
+ return (client_callout_kind,)
 
257
 
258
 
259
  @app.cell
 
274
 
275
 
276
  @app.cell
277
+ def _(client_stack, mo):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  client_section = mo.md(f'''
279
  ###**Instantiate your watsonx.ai client:**
280
 
 
291
  {client_stack}
292
 
293
  ''')
294
+ return (client_section,)
295
 
296
+
297
+ @app.cell
298
+ def _(mo, sc_m, schema_editors):
299
  sc_tabs = mo.ui.tabs(
300
  {
301
  "Schema Option Selection": sc_m,
 
304
  {schema_editors}"""),
305
  }
306
  )
307
+ return (sc_tabs,)
308
+
309
 
310
+ @app.cell
311
+ def _(fm, function_editor, mo, sc_tabs):
312
  function_section = mo.md(f'''###**Create your function from the template:**
313
 
314
  1. Use the code editor window to create a function to deploy
 
347
  {fm}
348
 
349
  ''')
350
+ return (function_section,)
351
+
352
 
353
+ @app.cell
354
+ def _(mo, selection_table, upload_func):
355
  upload_section = mo.md(f'''
356
  ###**Review and Upload your function**
357
 
 
372
  {upload_func}
373
 
374
  ''')
375
+ return (upload_section,)
376
 
377
+
378
+ @app.cell
379
+ def _(deploy_fnc, deployment_definition, hw_selection_table, mo):
380
  deployment_section = mo.md(f'''
381
  ###**Deploy your function:**
382
 
 
409
  {deploy_fnc}
410
 
411
  ''')
412
+ return (deployment_section,)
413
+
414
 
415
+ @app.cell
416
+ def _(mo, purge_tabs):
417
  purging_section = mo.md(f'''
418
  ###**Helper Purge Functions:**
419
 
 
427
 
428
  ''')
429
 
430
+ return (purging_section,)
431
+
432
 
433
+ @app.cell
434
+ def _(
435
+ json,
436
+ mo,
437
+ package_analysis_stack,
438
+ package_meta,
439
+ ss_asset_response,
440
+ yaml_template,
441
+ ):
442
  packages_section = mo.md(f'''
443
  ###**If needed - Create a custom software-spec with added python packages**
444
 
 
466
 
467
  ---
468
 
469
+ Result:
470
+ ```json
471
+ {json.dumps(ss_asset_response, indent=2)}
472
+ ```
473
  ''')
474
+ return (packages_section,)
 
 
 
 
 
 
475
 
476
 
477
  @app.cell
 
520
 
521
 
522
  @app.cell
523
+ def _(mo, purging_section):
524
  ui_accordion_section_6 = mo.accordion(
525
+ {"Section 6: **Helper Functions**": purging_section}
526
  )
527
  ui_accordion_section_6
528
  return
 
668
  def create_yaml_tempfile(yaml_editor_value):
669
  """Creates temporary YAML file and returns its path"""
670
  temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
 
 
 
 
 
 
 
 
 
 
671
  with open(temp_file.name, 'w') as f:
672
+ f.write(str(yaml_editor_value))
 
673
  return temp_file.name
674
  return (create_yaml_tempfile,)
675
 
 
770
 
771
 
772
  @app.cell
773
+ def _(client):
774
+ if client is not None:
775
  base_sw_spec_id = "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
776
+ base_software_spec = client.software_specifications.get_details(base_sw_spec_id)
777
  else:
778
  base_sw_spec_id = None
779
  base_software_spec = None
 
781
 
782
 
783
  @app.cell
784
+ def _(client, create_yaml_tempfile, package_meta, uuid):
785
+ if package_meta.value is not None and client is not None:
786
+ pack_suffix = str(uuid.uuid4())[:4]
787
+ pack_name = package_meta.value['package_name']
788
  pe_metadata = {
789
+ client.package_extensions.ConfigurationMetaNames.NAME: f"{pack_name}_{pack_suffix}",
790
+ client.package_extensions.ConfigurationMetaNames.TYPE: package_meta.value['package_type'],
791
+ client.software_specifications.ConfigurationMetaNames.DESCRIPTION:package_meta.value['package_description']
792
  }
793
  yaml_file_path = create_yaml_tempfile(package_meta.value['yml_editor'])
794
  else:
 
798
 
799
  return pe_metadata, yaml_file_path
800
 
801
+
802
+ @app.cell
803
+ def _(client, pe_metadata, yaml_file_path):
804
+ if yaml_file_path is not None:
805
+ ### Stores the package extension
806
+ pe_asset_details = client.package_extensions.store(
807
+ meta_props=pe_metadata,
808
+ file_path=yaml_file_path
809
+ )
810
+ package_id = pe_asset_details["metadata"]["asset_id"]
811
+ else:
812
+ pe_asset_details = None
813
+ package_id = None
814
+ return (package_id,)
815
+
816
+
817
+ @app.cell
818
  def _():
819
  ### Helper function for checking if a python library is in the standard software spec.
820
  def analyze_software_spec(sw_spec_response, required_libraries, return_full_sw_package_list=False):
 
1001
 
1002
 
1003
  @app.cell
1004
+ def _(base_sw_spec_id, client, package_id, package_meta, uuid):
1005
+ if package_id is not None:
 
 
 
 
 
 
 
 
 
 
 
 
1006
  ### Creates a custom software specification based on the standard python function spec_id - "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
1007
+ ss_suffix = str(uuid.uuid4())[:4]
1008
+ ss_name = package_meta.value['software_spec_name']
1009
+
1010
  ss_metadata = {
1011
+ client.software_specifications.ConfigurationMetaNames.NAME: f"{ss_name}_{ss_suffix}",
1012
+ client.software_specifications.ConfigurationMetaNames.DESCRIPTION: package_meta.value['software_spec_description'],
1013
+ client.software_specifications.ConfigurationMetaNames.BASE_SOFTWARE_SPECIFICATION: {'guid': base_sw_spec_id},
1014
+ client.software_specifications.ConfigurationMetaNames.PACKAGE_EXTENSIONS: [{'guid': package_id}]
1015
  }
1016
+ ss_asset_details = client.software_specifications.store(meta_props=ss_metadata)
1017
+
1018
+ if ss_asset_details is not None and "metadata" in ss_asset_details:
1019
+ ss_asset_response = ss_asset_details["metadata"]
1020
+ else:
1021
+ ss_asset_response = {"error": "Unsuccessful Upload"}
1022
  else:
 
 
1023
  ss_metadata = {}
1024
  ss_asset_details = None
1025
+ ss_asset_response = None
1026
 
1027
  # ss_asset_details
1028
+ return (ss_asset_response,)
1029
 
1030
 
1031
  @app.cell
1032
+ def _(client, mo, pd):
1033
+ if client:
1034
+ # First, get all specs data once
1035
+ specs_df = client.software_specifications.list()
1036
+
1037
+ # Filter the specs into two groups
1038
+ base_specs = specs_df[
1039
+ (specs_df['STATE'] == 'supported') &
1040
+ (specs_df['NAME'].isin(['runtime-24.1-py3.11', 'runtime-24.1-py3.11-cuda']))
1041
  ]
1042
 
1043
+ derived_specs = specs_df[
1044
+ (specs_df['TYPE'] == 'derived')
1045
+ ]
1046
+
1047
+ # Concatenate with base specs first, then derived specs
1048
+ supported_specs = pd.concat([base_specs, derived_specs]).reset_index(drop=True)
1049
 
1050
  # Create a mapping dictionary for framework names based on software specifications
1051
  framework_mapping = {
 
1104
  )
1105
  else:
1106
  sel_df = pd.DataFrame(
1107
+ data=[["ID", "Activate client."]],
1108
  columns=["ID", "VALUE"]
1109
  )
1110
 
1111
  selection_table = mo.ui.table(
1112
  sel_df,
1113
  selection="single", # Only allow selecting one row
1114
+ label="You haven't activated the client",
1115
  initial_selection=[0]
1116
  )
1117
 
1118
  return (selection_table,)
1119
 
1120
 
1121
+ @app.cell
1122
+ def _(mo):
1123
+ get_selected_sw_spec, set_selected_sw_spec = mo.state(None)
1124
+ return get_selected_sw_spec, set_selected_sw_spec
1125
+
1126
+
1127
+ @app.cell
1128
+ def _(selection_table, set_selected_sw_spec):
1129
+ if selection_table.value is not None:
1130
+ set_selected_sw_spec(selection_table.value['ID'].iloc[0])
1131
+ return
1132
+
1133
+
1134
+ @app.cell
1135
+ def _(mo):
1136
+ get_selected_hw_spec, set_selected_hw_spec = mo.state(None)
1137
+ return get_selected_hw_spec, set_selected_hw_spec
1138
+
1139
+
1140
+ @app.cell
1141
+ def _(hw_selection_table, set_selected_hw_spec):
1142
+ if hw_selection_table.value is not None:
1143
+ set_selected_hw_spec(hw_selection_table.value['ID'].iloc[0])
1144
+ return
1145
+
1146
+
1147
  @app.cell
1148
  def _(mo):
1149
  input_schema_checkbox = mo.ui.checkbox(label="Add input schema (optional)")
 
1155
  @app.cell
1156
  def _(
1157
  function_name,
1158
+ get_selected_sw_spec,
1159
  input_schema_checkbox,
1160
  mo,
1161
  output_schema_checkbox,
1162
  selection_table,
1163
  template_variant,
1164
  ):
1165
+ if selection_table.value is not None:
1166
  # Create the input fields
1167
  if function_name is not None:
1168
  fnc_nm = function_name
 
1177
  [mo.ui.text(placeholder="Metadata Tags..."), mo.ui.text(), mo.ui.text()],
1178
  label="Optional Metadata Tags"
1179
  )
1180
+ software_spec = get_selected_sw_spec()
1181
 
1182
  description_input = mo.ui.text_area(
1183
  placeholder="Write a description for your function...)",
 
1290
  @app.cell
1291
  def _(
1292
  ast,
1293
+ client,
1294
  description_input,
1295
  function_editor,
1296
+ importlib,
1297
  input_schema_checkbox,
1298
  input_schema_editor,
1299
  json,
 
1301
  os,
1302
  output_schema_checkbox,
1303
  output_schema_editor,
 
1304
  software_spec,
1305
+ sys,
1306
  tags_editor,
1307
  uploaded_function_name,
1308
  ):
 
1310
 
1311
  function_meta = {}
1312
 
1313
+ if software_spec and client is not None:
1314
  # Start with the base required fields
1315
  function_meta = {
1316
+ client.repository.FunctionMetaNames.NAME: f"{uploaded_function_name.value}" or "your_function_name",
1317
+ client.repository.FunctionMetaNames.SOFTWARE_SPEC_ID: software_spec or "45f12dfe-aa78-5b8d-9f38-0ee223c47309"
1318
  }
1319
 
1320
  # Add optional fields if they exist
 
1322
  # Filter out empty strings from the tags list
1323
  filtered_tags = [tag for tag in tags_editor.value if tag and tag.strip()]
1324
  if filtered_tags: # Only add if there are non-empty tags
1325
+ function_meta[client.repository.FunctionMetaNames.TAGS] = filtered_tags
1326
 
1327
 
1328
  if description_input.value:
1329
+ function_meta[client.repository.FunctionMetaNames.DESCRIPTION] = description_input.value
1330
 
1331
  # Add input schema if checkbox is checked
1332
  if input_schema_checkbox.value:
1333
  try:
1334
+ function_meta[client.repository.FunctionMetaNames.INPUT_DATA_SCHEMAS] = json.loads(input_schema_editor.value)
1335
  except json.JSONDecodeError:
1336
  # If JSON parsing fails, try Python literal evaluation as fallback
1337
+ function_meta[client.repository.FunctionMetaNames.INPUT_DATA_SCHEMAS] = ast.literal_eval(input_schema_editor.value)
1338
 
1339
  # Add output schema if checkbox is checked
1340
  if output_schema_checkbox.value:
1341
  try:
1342
+ function_meta[client.repository.FunctionMetaNames.OUTPUT_DATA_SCHEMAS] = json.loads(output_schema_editor.value)
1343
  except json.JSONDecodeError:
1344
  # If JSON parsing fails, try Python literal evaluation as fallback
1345
+ function_meta[client.repository.FunctionMetaNames.OUTPUT_DATA_SCHEMAS] = ast.literal_eval(output_schema_editor.value)
1346
 
 
 
 
 
 
 
 
1347
 
1348
  def upload_function(function_meta, use_function_object=False):
1349
  """
 
1363
  # This function is defined elsewhere in the notebook
1364
  func_name = uploaded_function_name.value or "your_function_name"
1365
  # Ensure function_meta has the correct function name
1366
+ function_meta[client.repository.FunctionMetaNames.NAME] = func_name
1367
  # Save the file locally first
1368
  save_dir = "/tmp/notebook_functions"
1369
  os.makedirs(save_dir, exist_ok=True)
 
1373
 
1374
  if use_function_object:
1375
  # Import the function from the file
 
 
1376
  # Add the directory to Python's path
1377
  sys.path.append(save_dir)
1378
  # Import the module
 
1387
 
1388
  # Upload the function object
1389
  mo.md(f"Uploading function object: {func_name}")
1390
+ func_details = client.repository.store_function(function_object, function_meta)
1391
  else:
1392
  # Change to /tmp directory before calling IBM Watson SDK functions
1393
  os.chdir('/tmp/notebook_functions')
1394
 
 
 
 
 
 
 
 
 
1395
  # Create a zip file of the Python module
1396
  import gzip
1397
  import shutil
 
1406
 
1407
  # Upload using the gzipped file path
1408
  mo.md(f"Uploading function from gzip: {gz_path}")
1409
+ func_details = client.repository.store_function(gz_path, function_meta)
 
 
 
1410
 
1411
  set_upload_status(f"Latest Upload - id - {func_details['metadata']['id']}")
1412
  return func_details
 
1449
 
1450
 
1451
  @app.cell
1452
+ def _(client, mo, pd, upload_button, uuid):
1453
  def reorder_hardware_specifications(df):
1454
  """
1455
  Reorders a hardware specifications dataframe by type and size of environment
 
1494
 
1495
  return result_df
1496
 
1497
+ if client and upload_button.value:
1498
 
1499
+ hardware_specs = client.hardware_specifications.list()
1500
  hardware_specs_df = reorder_hardware_specifications(hardware_specs)
1501
 
1502
  # Create a table with single-row selection
 
1517
  deployment_name = mo.ui.text(value=f"deployed_func_{uuid_suffix}", label="Deployment Name:", placeholder="<Must be completely unique>")
1518
  else:
1519
  hw_df = pd.DataFrame(
1520
+ data=[["ID", "Activate client."]],
1521
  columns=["ID", "VALUE"]
1522
  )
1523
 
1524
  hw_selection_table = mo.ui.table(
1525
  hw_df,
1526
  selection="single", # Only allow selecting one row
1527
+ label="You haven't activated the client",
1528
  initial_selection=[0]
1529
  )
1530
 
 
1534
  @app.cell
1535
  def _(
1536
  artifact_id,
1537
+ client,
1538
  deployment_details,
1539
  deployment_name,
1540
  deployment_type,
1541
+ get_selected_hw_spec,
1542
  hw_selection_table,
1543
  mo,
1544
  upload_button,
 
1560
 
1561
  if deployment_type.value == "Online (Function Endpoint)": # Changed from "Online (Function Endpoint)"
1562
  deployment_props = {
1563
+ client.deployments.ConfigurationMetaNames.NAME: deployment_name.value,
1564
+ client.deployments.ConfigurationMetaNames.ONLINE: {},
1565
+ client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {"id": selected_hw_config},
1566
+ client.deployments.ConfigurationMetaNames.SERVING_NAME: deployment_name.value,
1567
  }
1568
  else: # "Runnable Job" instead of "Batch (Runnable Jobs)"
1569
  deployment_props = {
1570
+ client.deployments.ConfigurationMetaNames.NAME: deployment_name.value,
1571
+ client.deployments.ConfigurationMetaNames.BATCH: {},
1572
+ client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {"id": selected_hw_config},
1573
  # batch does not use serving names
1574
  }
1575
 
1576
  try:
1577
  print(deployment_props)
1578
  # First, get the asset details to confirm it exists
1579
+ asset_details = client.repository.get_details(artifact_id)
1580
  print(f"Asset found: {asset_details['metadata']['name']} with ID: {asset_details['metadata']['id']}")
1581
 
1582
  # Create the deployment
1583
+ deployed_function = client.deployments.create(artifact_id, deployment_props)
1584
  print(f"Creating deployment from Asset: {artifact_id} with deployment properties {str(deployment_props)}")
1585
  return deployed_function
1586
  except Exception as e:
 
1588
  return None
1589
 
1590
  def get_deployment_id(deployed_function):
1591
+ deployment_id = client.deployments.get_uid(deployment_details)
1592
  return deployment_id
1593
 
1594
  def get_deployment_info(deployment_id):
1595
+ deployment_info = client.deployments.get_details(deployment_id)
1596
  return deployment_info
1597
 
1598
  deployment_status = mo.state("No deployments yet")
1599
 
1600
+ if hw_selection_table.value is not None:
1601
+ selected_hw_config = get_selected_hw_spec()
1602
 
1603
  deploy_button = mo.ui.button(
1604
  label="Deploy Function",
 
1607
  tooltip="Click to deploy function to watsonx.ai"
1608
  )
1609
 
1610
+ if client and upload_button.value:
1611
  deployment_definition = mo.hstack([
1612
  deployment_type,
1613
  deployment_name
 
1634
  return (deploy_fnc,)
1635
 
1636
 
1637
+ @app.cell
1638
+ def _(client, mo, pd, sys):
1639
  ### Functions to List , Get ID's as a list and Purge of Assets
1640
 
1641
  def get_deployment_list():
1642
+ dep_df = client.deployments.list()
1643
  dep_df = pd.DataFrame(dep_df)
1644
+
1645
+ columns_to_drop = [col for col in dep_df.columns if 'STATE' in col or 'REPLACEMENT' in col]
1646
+ if columns_to_drop:
1647
+ dep_df = dep_df.drop(columns=columns_to_drop)
1648
  return dep_df
1649
 
1650
  def get_deployment_ids(df):
 
1654
  #----
1655
 
1656
  def get_data_assets_list():
1657
+ data_a_df = client.data_assets.list()
1658
  data_a_df = pd.DataFrame(data_a_df)
1659
  return data_a_df
1660
 
 
1665
  #----
1666
 
1667
  def get_repository_list():
1668
+ rep_list_df = client.repository.list()
1669
  rep_list_df = pd.DataFrame(rep_list_df)
1670
+
1671
+ columns_to_drop = [col for col in ['SPEC_STATE', 'SPEC_REPLACEMENT'] if col in rep_list_df.columns]
1672
+ if columns_to_drop:
1673
+ rep_list_df = rep_list_df.drop(columns=columns_to_drop)
1674
  return rep_list_df
1675
 
1676
  def get_repository_ids(df):
 
1679
 
1680
  #----
1681
 
1682
+ def get_pkg_ext_list():
1683
+ pkg_ext_list_df = client.package_extensions.list()
1684
+ pkg_ext_list_df = pd.DataFrame(pkg_ext_list_df)
1685
+ return pkg_ext_list_df
1686
 
1687
+ def get_pkg_ext_ids(df):
1688
+ pkg_ext_id_list = df['ASSET_ID'].tolist()
1689
+ return pkg_ext_id_list
1690
+
1691
+ #----
1692
+
1693
+ def get_sws_list():
1694
+ sws_list_df = client.software_specifications.list()
1695
+ # Filter to only include derived types
1696
+ derived_sws_list_df = sws_list_df[
1697
+ (sws_list_df['TYPE'] == 'derived')
1698
+ ]
1699
+ # Reset the index and prepare final dataframe
1700
+ sws_list_df = pd.DataFrame(derived_sws_list_df).reset_index(drop=True)
1701
+ # Drop STATE and REPLACEMENT columns if they exist
1702
+ columns_to_drop = [col for col in ['STATE', 'REPLACEMENT'] if col in sws_list_df.columns]
1703
+ if columns_to_drop:
1704
+ sws_list_df = sws_list_df.drop(columns=columns_to_drop)
1705
+ return sws_list_df
1706
+
1707
+ def get_sws_ids(df):
1708
+ sws_id_list = df['ID'].tolist()
1709
+ return sws_id_list
1710
+
1711
+ #----
1712
+
1713
+ def delete_with_progress(ids_list, delete_function, item_type="items", display_errors=True):
1714
+ errors = []
1715
+
1716
  with mo.status.progress_bar(
1717
  total=len(ids_list) or 1,
1718
  title=f"Purging {item_type}",
1719
  subtitle=f"Deleting {item_type}...",
1720
  completion_title="Purge Complete",
1721
+ completion_subtitle=f"Successfully deleted {len(ids_list) - len(errors)} {item_type}",
1722
+ remove_on_exit=True
1723
  ) as progress:
1724
  for item_id in ids_list:
1725
+ try:
1726
+ delete_function(item_id)
1727
+ except Exception as e:
1728
+ error_msg = f"Error deleting {item_type} with ID {item_id}: {str(e)}"
1729
+ if display_errors:
1730
+ print(error_msg)
1731
+ errors.append((item_id, str(e)))
1732
+ finally:
1733
+ progress.update(increment=1)
1734
+
1735
+ if errors and display_errors:
1736
+ with mo.redirect_stderr():
1737
+ sys.stderr.write("\nErrors encountered during deletion:\n")
1738
+ for item_id, error in errors:
1739
+ sys.stderr.write(f" - ID {item_id}: {error}\n")
1740
+
1741
+ return f"Deleted {len(ids_list) - len(errors)} {item_type} successfully"
1742
+
1743
  # Use with existing deletion functions
1744
  def delete_deployments(deployment_ids):
1745
  return delete_with_progress(
1746
  deployment_ids,
1747
+ lambda id: client.deployments.delete(id),
1748
  "deployments"
1749
  )
1750
 
1751
  def delete_data_assets(data_asset_ids):
1752
  return delete_with_progress(
1753
  data_asset_ids,
1754
+ lambda id: client.data_assets.delete(id),
1755
  "data assets"
1756
  )
1757
 
1758
  def delete_repository_items(repository_ids):
1759
  return delete_with_progress(
1760
  repository_ids,
1761
+ lambda id: client.repository.delete(id),
1762
  "repository items"
1763
  )
1764
+
1765
+ def delete_pkg_ext_items(pkg_ids):
1766
+ return delete_with_progress(
1767
+ pkg_ids,
1768
+ lambda id: client.package_extensions.delete(id),
1769
+ "package extensions"
1770
+ )
1771
+
1772
+ def delete_sws_items(sws_ids):
1773
+ return delete_with_progress(
1774
+ sws_ids,
1775
+ lambda id: client.software_specifications.delete(id),
1776
+ "software specifications"
1777
+ )
1778
  return (
1779
  delete_data_assets,
1780
  delete_deployments,
1781
+ delete_pkg_ext_items,
1782
  delete_repository_items,
1783
+ delete_sws_items,
1784
  get_data_asset_ids,
1785
  get_data_assets_list,
1786
  get_deployment_ids,
1787
  get_deployment_list,
1788
+ get_pkg_ext_ids,
1789
+ get_pkg_ext_list,
1790
  get_repository_ids,
1791
  get_repository_list,
1792
+ get_sws_ids,
1793
+ get_sws_list,
1794
  )
1795
 
1796
 
1797
  @app.cell
1798
+ def _(
1799
+ get_data_assets_tab,
1800
+ get_deployments_tab,
1801
+ get_pkg_ext_tab,
1802
+ get_repository_tab,
1803
+ get_sws_tab,
1804
+ mo,
1805
+ ):
1806
  if get_deployments_tab() is not None:
1807
  deployments_table = mo.ui.table(get_deployments_tab())
1808
  else:
 
1818
  else:
1819
  data_assets_table = mo.md("No Table Loaded")
1820
 
1821
+ if get_sws_tab() is not None:
1822
+ sws_table = mo.ui.table(get_sws_tab())
1823
+ else:
1824
+ sws_table = mo.md("No Table Loaded")
1825
+
1826
+ if get_pkg_ext_tab() is not None:
1827
+ pkg_ext_table = mo.ui.table(get_pkg_ext_tab())
1828
+ else:
1829
+ pkg_ext_table = mo.md("No Table Loaded")
1830
+
1831
+
1832
+ return (
1833
+ data_assets_table,
1834
+ deployments_table,
1835
+ pkg_ext_table,
1836
+ repository_table,
1837
+ sws_table,
1838
+ )
1839
 
1840
 
1841
  @app.cell
 
1862
  repository_table,
1863
  ):
1864
  repository_purge_stack = mo.hstack([get_repository_button, get_repository_id_list, purge_repository])
 
1865
  repository_purge_stack_results = mo.vstack([repository_table, get_repository_id_list.value, purge_repository.value])
1866
 
1867
  repository_purge_tab = mo.vstack([repository_purge_stack, repository_purge_stack_results])
 
1884
 
1885
 
1886
  @app.cell
1887
+ def _(get_sws_button, get_sws_id_list, mo, purge_sws, sws_table):
1888
+ sws_purge_stack = mo.hstack([get_sws_button, get_sws_id_list, purge_sws])
1889
+ sws_purge_stack_results = mo.vstack([sws_table, get_sws_id_list.value, purge_sws.value])
1890
+
1891
+ sws_purge_stack_tab = mo.vstack([sws_purge_stack, sws_purge_stack_results])
1892
+ return (sws_purge_stack_tab,)
1893
+
1894
+
1895
+ @app.cell
1896
+ def _(
1897
+ get_pkg_ext_button,
1898
+ get_pkg_ext_id_list,
1899
+ mo,
1900
+ pkg_ext_table,
1901
+ purge_pkg_ext,
1902
+ ):
1903
+ pkg_ext_purge_stack = mo.hstack([get_pkg_ext_button, get_pkg_ext_id_list, purge_pkg_ext])
1904
+ pkg_ext_purge_stack_results = mo.vstack([pkg_ext_table, get_pkg_ext_id_list.value, purge_pkg_ext.value])
1905
+
1906
+ pkg_ext_purge_tab = mo.vstack([pkg_ext_purge_stack, pkg_ext_purge_stack_results])
1907
+ return (pkg_ext_purge_tab,)
1908
+
1909
+
1910
+ @app.cell
1911
+ def _(
1912
+ data_assets_purge_tab,
1913
+ deployments_purge_tab,
1914
+ mo,
1915
+ pkg_ext_purge_tab,
1916
+ repository_purge_tab,
1917
+ sws_purge_stack_tab,
1918
+ ):
1919
  purge_tabs = mo.ui.tabs(
1920
+ {
1921
+ "Purge Deployments": deployments_purge_tab,
1922
+ "Purge Repository Assets": repository_purge_tab,
1923
+ "Purge Data Assets": data_assets_purge_tab,
1924
+ "Purge Software Specifications": sws_purge_stack_tab,
1925
+ "Purge Package Extensions": pkg_ext_purge_tab,
1926
+ }
1927
+ , lazy=False
1928
  )
1929
 
1930
  return (purge_tabs,)
 
1933
  @app.cell
1934
  def _(mo):
1935
  get_deployments_tab, set_deployments_tab = mo.state(None)
1936
+ return get_deployments_tab, set_deployments_tab
1937
+
1938
+
1939
+ @app.cell
1940
+ def _(mo):
1941
  get_repository_tab, set_repository_tab = mo.state(None)
1942
+ return get_repository_tab, set_repository_tab
1943
+
1944
+
1945
+ @app.cell
1946
+ def _(mo):
1947
  get_data_assets_tab, set_data_assets_tab = mo.state(None)
1948
+ return get_data_assets_tab, set_data_assets_tab
 
 
 
 
 
 
 
1949
 
1950
 
1951
+ @app.cell
1952
+ def _(mo):
1953
+ get_pkg_ext_tab, set_pkg_ext_tab = mo.state(None)
1954
+ return get_pkg_ext_tab, set_pkg_ext_tab
1955
+
1956
+
1957
+ @app.cell
1958
+ def _(mo):
1959
+ get_sws_tab, set_sws_tab = mo.state(None)
1960
+ return get_sws_tab, set_sws_tab
1961
+
1962
+
1963
+ @app.cell
1964
  def _(
1965
  data_assets_table,
1966
  delete_data_assets,
1967
  delete_deployments,
1968
+ delete_pkg_ext_items,
1969
  delete_repository_items,
1970
+ delete_sws_items,
1971
  deployments_table,
1972
  get_data_asset_ids,
1973
  get_data_assets_list,
1974
  get_deployment_ids,
1975
  get_deployment_list,
1976
+ get_pkg_ext_ids,
1977
+ get_pkg_ext_list,
1978
  get_repository_ids,
1979
  get_repository_list,
1980
+ get_sws_ids,
1981
+ get_sws_list,
1982
  mo,
1983
+ pkg_ext_table,
1984
  repository_table,
1985
  set_data_assets_tab,
1986
  set_deployments_tab,
1987
+ set_pkg_ext_tab,
1988
  set_repository_tab,
1989
+ set_sws_tab,
1990
+ sws_table,
1991
  ):
1992
  ### Temporary Function Purge - Assets
1993
  get_data_assets_button = mo.ui.button(
 
2048
  on_click=lambda _: delete_repository_items(get_repository_id_list.value),
2049
  kind="danger",
2050
  )
2051
+
2052
+ ### Software Spec Purge
2053
+ get_sws_button = mo.ui.button(
2054
+ label="Get Software Spec Dataframe",
2055
+ on_click=lambda _: get_sws_list(),
2056
+ on_change=lambda value: set_sws_tab(value),
2057
+ kind="neutral",
2058
+ )
2059
+
2060
+ get_sws_id_list = mo.ui.button(
2061
+ label="Turn Dataframe into List of IDs",
2062
+ on_click=lambda _: get_sws_ids(sws_table.value),
2063
+ kind="neutral",
2064
+ )
2065
+
2066
+ purge_sws = mo.ui.button(
2067
+ label="Purge Software Specifications",
2068
+ on_click=lambda _: delete_sws_items(get_sws_id_list.value),
2069
+ kind="danger",
2070
+ )
2071
+
2072
+
2073
+ ### Package Extensions Purge
2074
+ get_pkg_ext_button = mo.ui.button(
2075
+ label="Get Package Extensions Dataframe",
2076
+ on_click=lambda _: get_pkg_ext_list(),
2077
+ on_change=lambda value: set_pkg_ext_tab(value),
2078
+ kind="neutral",
2079
+ )
2080
+
2081
+ get_pkg_ext_id_list = mo.ui.button(
2082
+ label="Turn Dataframe into List of IDs",
2083
+ on_click=lambda _: get_pkg_ext_ids(pkg_ext_table.value),
2084
+ kind="neutral",
2085
+ )
2086
+
2087
+ purge_pkg_ext = mo.ui.button(
2088
+ label="Purge Package Extensions",
2089
+ on_click=lambda _: delete_pkg_ext_items(get_pkg_ext_id_list.value),
2090
+ kind="danger",
2091
+ )
2092
  return (
2093
  get_data_asset_id_list,
2094
  get_data_assets_button,
2095
  get_deployment_id_list,
2096
  get_deployments_button,
2097
+ get_pkg_ext_button,
2098
+ get_pkg_ext_id_list,
2099
  get_repository_button,
2100
  get_repository_id_list,
2101
+ get_sws_button,
2102
+ get_sws_id_list,
2103
  purge_data_assets,
2104
  purge_deployments,
2105
+ purge_pkg_ext,
2106
  purge_repository,
2107
+ purge_sws,
2108
  )
2109
 
2110
 
2111
+ @app.cell
2112
+ def _():
2113
+ return
2114
+
2115
+
2116
  if __name__ == "__main__":
2117
  app.run()