Update main_app.py
Browse files- main_app.py +185 -0
main_app.py
CHANGED
|
@@ -785,6 +785,191 @@ def _(create_yaml_tempfile, deployment_client, package_meta):
|
|
| 785 |
|
| 786 |
return pe_metadata, yaml_file_path
|
| 787 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 788 |
|
| 789 |
@app.cell
|
| 790 |
def _(
|
|
|
|
| 785 |
|
| 786 |
return pe_metadata, yaml_file_path
|
| 787 |
|
| 788 |
+
@app.cell(hide_code=True)
|
| 789 |
+
def _():
|
| 790 |
+
### Helper function for checking if a python library is in the standard software spec.
|
| 791 |
+
def analyze_software_spec(sw_spec_response, required_libraries, return_full_sw_package_list=False):
|
| 792 |
+
"""
|
| 793 |
+
Analyzes a software specification against a list of required libraries.
|
| 794 |
+
|
| 795 |
+
Args:
|
| 796 |
+
sw_spec_response (dict): The software specification response from the API
|
| 797 |
+
required_libraries (list): List of required libraries in format ["package_name", "package_name==version", etc.]
|
| 798 |
+
return_full_sw_package_list (bool): Whether to return the complete package list from sw_spec
|
| 799 |
+
|
| 800 |
+
Returns:
|
| 801 |
+
dict: A dictionary with analysis results containing:
|
| 802 |
+
- not_present: Dict of libraries not found in the software spec
|
| 803 |
+
- version_mismatch: Dict of libraries with version mismatches, with [sw_version, required_version] as values
|
| 804 |
+
- present: Dict of libraries that are present with matching versions
|
| 805 |
+
- sw_packages: (Optional) Complete dict of all packages in the software spec
|
| 806 |
+
"""
|
| 807 |
+
result = {
|
| 808 |
+
"present": {},
|
| 809 |
+
"not_present": {},
|
| 810 |
+
"version_mismatch": {}
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
+
# Extract all packages from the software specification
|
| 814 |
+
sw_packages = {}
|
| 815 |
+
|
| 816 |
+
try:
|
| 817 |
+
# Extract packages from included_packages in the software specification
|
| 818 |
+
included_packages = sw_spec_response["entity"]["software_specification"]["software_configuration"]["included_packages"]
|
| 819 |
+
|
| 820 |
+
# Create a dictionary of all packages in the software specification
|
| 821 |
+
for package in included_packages:
|
| 822 |
+
package_name = package["name"]
|
| 823 |
+
package_version = package["version"]
|
| 824 |
+
sw_packages[package_name] = package_version
|
| 825 |
+
except KeyError as e:
|
| 826 |
+
raise ValueError(f"Invalid software specification format: {e}")
|
| 827 |
+
|
| 828 |
+
# Parse required libraries
|
| 829 |
+
for lib in required_libraries:
|
| 830 |
+
if "==" in lib:
|
| 831 |
+
lib_name, lib_version = lib.split("==", 1)
|
| 832 |
+
lib_name = lib_name.strip()
|
| 833 |
+
lib_version = lib_version.strip()
|
| 834 |
+
else:
|
| 835 |
+
lib_name = lib.strip()
|
| 836 |
+
lib_version = None
|
| 837 |
+
|
| 838 |
+
# Check if library is present in software specification
|
| 839 |
+
if lib_name not in sw_packages:
|
| 840 |
+
result["not_present"][lib_name] = None
|
| 841 |
+
elif lib_version is not None and lib_version != sw_packages[lib_name]:
|
| 842 |
+
# Check version mismatch
|
| 843 |
+
result["version_mismatch"][lib_name] = [sw_packages[lib_name], lib_version]
|
| 844 |
+
else:
|
| 845 |
+
# Library is present with matching version (or no specific version required)
|
| 846 |
+
result["present"][lib_name] = sw_packages[lib_name]
|
| 847 |
+
|
| 848 |
+
if return_full_sw_package_list:
|
| 849 |
+
# Extract just the library names from required_libraries
|
| 850 |
+
req_libs_names = [lib.split("==")[0].strip() if "==" in lib else lib.strip() for lib in required_libraries]
|
| 851 |
+
|
| 852 |
+
def sort_key(pkg_name):
|
| 853 |
+
if pkg_name in result["not_present"]:
|
| 854 |
+
return (0, pkg_name) # Missing packages first
|
| 855 |
+
elif pkg_name in result["version_mismatch"]:
|
| 856 |
+
return (1, pkg_name) # Version mismatch second
|
| 857 |
+
elif pkg_name in req_libs_names:
|
| 858 |
+
return (2, pkg_name) # Required packages that match third
|
| 859 |
+
else:
|
| 860 |
+
return (3, pkg_name) # All other packages last
|
| 861 |
+
|
| 862 |
+
# Sort sw_packages using the custom sorting key
|
| 863 |
+
result["sw_packages"] = {k: sw_packages[k] for k in sorted(sw_packages.keys(), key=sort_key)}
|
| 864 |
+
|
| 865 |
+
# Add missing packages to the top of sw_packages
|
| 866 |
+
for pkg in result["not_present"]:
|
| 867 |
+
result["sw_packages"] = {pkg: None, **result["sw_packages"]}
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
return result
|
| 871 |
+
|
| 872 |
+
def visualize_software_spec(analysis_result, required_libraries=None):
|
| 873 |
+
"""
|
| 874 |
+
Visualizes the results of analyze_software_spec in a DataFrame with status indicators.
|
| 875 |
+
For use in Marimo notebooks.
|
| 876 |
+
|
| 877 |
+
Args:
|
| 878 |
+
analysis_result (dict): The result from analyze_software_spec function
|
| 879 |
+
required_libraries (list, optional): The original list of required libraries
|
| 880 |
+
used in analyze_software_spec
|
| 881 |
+
|
| 882 |
+
Returns:
|
| 883 |
+
pandas.DataFrame: A DataFrame showing the analysis results with status indicators
|
| 884 |
+
"""
|
| 885 |
+
import pandas as pd
|
| 886 |
+
|
| 887 |
+
# Parse required libraries to get the exact names for lookup
|
| 888 |
+
req_libs_parsed = {}
|
| 889 |
+
if required_libraries:
|
| 890 |
+
for lib in required_libraries:
|
| 891 |
+
if "==" in lib:
|
| 892 |
+
lib_name, lib_version = lib.split("==", 1)
|
| 893 |
+
lib_name = lib_name.strip()
|
| 894 |
+
lib_version = lib_version.strip()
|
| 895 |
+
req_libs_parsed[lib_name] = lib_version
|
| 896 |
+
else:
|
| 897 |
+
lib_name = lib.strip()
|
| 898 |
+
req_libs_parsed[lib_name] = None
|
| 899 |
+
|
| 900 |
+
# Determine if we have the full sw_packages list
|
| 901 |
+
has_full_list = "sw_packages" in analysis_result
|
| 902 |
+
|
| 903 |
+
# Create a DataFrame based on available data
|
| 904 |
+
if has_full_list:
|
| 905 |
+
# Use the full package list
|
| 906 |
+
packages = analysis_result["sw_packages"]
|
| 907 |
+
|
| 908 |
+
# Prepare data rows
|
| 909 |
+
rows = []
|
| 910 |
+
for package, version in packages.items():
|
| 911 |
+
if package in analysis_result.get("not_present", {}):
|
| 912 |
+
status = "❌ Missing"
|
| 913 |
+
priority = 0 # Top priority
|
| 914 |
+
elif package in analysis_result.get("version_mismatch", {}):
|
| 915 |
+
status = "⚠️ Version Mismatch"
|
| 916 |
+
priority = 1 # Second priority
|
| 917 |
+
elif package in req_libs_parsed:
|
| 918 |
+
status = "✅ Present"
|
| 919 |
+
priority = 2 # Third priority
|
| 920 |
+
else:
|
| 921 |
+
status = "Other"
|
| 922 |
+
priority = 3 # Lowest priority
|
| 923 |
+
|
| 924 |
+
rows.append({
|
| 925 |
+
"Package": package,
|
| 926 |
+
"Version": version if version is not None else "Not Present",
|
| 927 |
+
"Status": status,
|
| 928 |
+
"_priority": priority # Temporary field for sorting
|
| 929 |
+
})
|
| 930 |
+
|
| 931 |
+
df = pd.DataFrame(rows)
|
| 932 |
+
|
| 933 |
+
# Sort by priority and then package name
|
| 934 |
+
df = df.sort_values(by=["_priority", "Package"]).drop("_priority", axis=1).reset_index(drop=True)
|
| 935 |
+
|
| 936 |
+
else:
|
| 937 |
+
# Only use the packages mentioned in required_libraries
|
| 938 |
+
packages = set(list(analysis_result.get("not_present", {}).keys()) +
|
| 939 |
+
list(analysis_result.get("version_mismatch", {}).keys()) +
|
| 940 |
+
list(analysis_result.get("present", {}).keys()))
|
| 941 |
+
|
| 942 |
+
# Create dataframe rows
|
| 943 |
+
rows = []
|
| 944 |
+
for package in packages:
|
| 945 |
+
if package in analysis_result.get("not_present", {}):
|
| 946 |
+
version = "Not Present"
|
| 947 |
+
status = "❌ Missing"
|
| 948 |
+
priority = 0 # Top priority
|
| 949 |
+
elif package in analysis_result.get("version_mismatch", {}):
|
| 950 |
+
version = analysis_result["version_mismatch"][package][0] # sw_spec version
|
| 951 |
+
status = "⚠️ Version Mismatch"
|
| 952 |
+
priority = 1 # Second priority
|
| 953 |
+
else:
|
| 954 |
+
version = analysis_result["present"][package]
|
| 955 |
+
status = "✅ Present"
|
| 956 |
+
priority = 2 # Third priority
|
| 957 |
+
|
| 958 |
+
rows.append({
|
| 959 |
+
"Package": package,
|
| 960 |
+
"Version": version,
|
| 961 |
+
"Status": status,
|
| 962 |
+
"_priority": priority # Temporary field for sorting
|
| 963 |
+
})
|
| 964 |
+
|
| 965 |
+
df = pd.DataFrame(rows)
|
| 966 |
+
|
| 967 |
+
# Sort by priority and then package name
|
| 968 |
+
df = df.sort_values(by=["_priority", "Package"]).drop("_priority", axis=1).reset_index(drop=True)
|
| 969 |
+
|
| 970 |
+
return df
|
| 971 |
+
return analyze_software_spec, visualize_software_spec
|
| 972 |
+
|
| 973 |
|
| 974 |
@app.cell
|
| 975 |
def _(
|