Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import random
|
4 |
+
from datetime import datetime, timedelta
|
5 |
+
|
6 |
+
st.set_page_config(layout="wide")
|
7 |
+
|
8 |
+
|
9 |
+
# Helper function to generate a random date within the last year
|
10 |
+
def random_date():
|
11 |
+
start_date = datetime.now() - timedelta(days=365)
|
12 |
+
random_days = random.randint(0, 365)
|
13 |
+
return (start_date + timedelta(days=random_days)).strftime("%Y-%m-%d")
|
14 |
+
|
15 |
+
# Function to load and cache the product catalog
|
16 |
+
@st.cache_data
|
17 |
+
def load_catalog():
|
18 |
+
# Generate approval attributes
|
19 |
+
cyber_approved = [random.choice([True, False]) for _ in range(50)]
|
20 |
+
accessibility_approved = [random.choice([True, False]) for _ in range(50)]
|
21 |
+
privacy_approved = [random.choice([True, False]) for _ in range(50)]
|
22 |
+
|
23 |
+
review_statuses = []
|
24 |
+
not_approved_reasons = []
|
25 |
+
for cyber, accessibility, privacy in zip(cyber_approved, accessibility_approved, privacy_approved):
|
26 |
+
if cyber and accessibility and privacy: # All approvals are True
|
27 |
+
review_statuses.append("Approved")
|
28 |
+
not_approved_reasons.append(None)
|
29 |
+
elif not cyber and not accessibility and not privacy: # All approvals are False
|
30 |
+
review_statuses.append("Not Approved")
|
31 |
+
not_approved_reasons.append(random.choice(["Security Concern", "Licensing Issue", "Privacy Issue", "Compliance Requirement"]))
|
32 |
+
else: # Mixed approvals
|
33 |
+
review_statuses.append("Under Review")
|
34 |
+
not_approved_reasons.append(None)
|
35 |
+
|
36 |
+
products = {
|
37 |
+
"Product Name": [
|
38 |
+
"Notepad++", "WinRAR", "7-Zip", "CCleaner", "TeamViewer",
|
39 |
+
"FileZilla", "PuTTY", "WinSCP", "Everything", "Greenshot",
|
40 |
+
"Visual Studio Code", "JetBrains IntelliJ IDEA", "Sublime Text", "Atom", "Eclipse",
|
41 |
+
"PyCharm", "NetBeans", "Xcode", "Android Studio", "GitLab",
|
42 |
+
"Norton Antivirus", "McAfee Total Protection", "Kaspersky Internet Security", "Bitdefender Antivirus Plus", "Avast Free Antivirus",
|
43 |
+
"Sophos Home", "Trend Micro Antivirus+", "ESET NOD32 Antivirus", "F-Secure SAFE", "Malwarebytes",
|
44 |
+
"Microsoft Office 365", "Google Workspace", "Slack", "Trello", "Asana",
|
45 |
+
"Zoom", "Evernote", "Notion", "Dropbox", "Adobe Acrobat Reader",
|
46 |
+
"Adobe Photoshop", "Adobe Illustrator", "Adobe Premiere Pro", "Final Cut Pro", "Sketch",
|
47 |
+
"Blender", "Autodesk Maya", "CorelDRAW", "GIMP", "Inkscape"
|
48 |
+
],
|
49 |
+
"Category": [
|
50 |
+
"Utility Tools", "Utility Tools", "Utility Tools", "Utility Tools", "Utility Tools",
|
51 |
+
"Utility Tools", "Utility Tools", "Utility Tools", "Utility Tools", "Utility Tools",
|
52 |
+
"Development Tools", "Development Tools", "Development Tools", "Development Tools", "Development Tools",
|
53 |
+
"Development Tools", "Development Tools", "Development Tools", "Development Tools", "Development Tools",
|
54 |
+
"Security Software", "Security Software", "Security Software", "Security Software", "Security Software",
|
55 |
+
"Security Software", "Security Software", "Security Software", "Security Software", "Security Software",
|
56 |
+
"Productivity Software", "Productivity Software", "Productivity Software", "Productivity Software", "Productivity Software",
|
57 |
+
"Productivity Software", "Productivity Software", "Productivity Software", "Productivity Software", "Productivity Software",
|
58 |
+
"Creative Software", "Creative Software", "Creative Software", "Creative Software", "Creative Software",
|
59 |
+
"Creative Software", "Creative Software", "Creative Software", "Creative Software", "Creative Software"
|
60 |
+
],
|
61 |
+
"Cyber Approved": cyber_approved,
|
62 |
+
"Accessibility Approved": accessibility_approved,
|
63 |
+
"Privacy Approved": privacy_approved,
|
64 |
+
"Review Date": [random_date() for _ in range(50)],
|
65 |
+
"Review Status": review_statuses,
|
66 |
+
"Not Approved Reason": not_approved_reasons
|
67 |
+
}
|
68 |
+
return pd.DataFrame(products)
|
69 |
+
|
70 |
+
# Function to filter the catalog based on multiple attributes with AND logic
|
71 |
+
@st.cache_data
|
72 |
+
def filter_catalog(catalog, search_query=None, selected_category=None, cyber_approved=None, accessibility_approved=None, privacy_approved=None,review_status=None):
|
73 |
+
filtered = catalog
|
74 |
+
if search_query:
|
75 |
+
filtered = filtered[filtered.apply(lambda row: search_query.lower() in str(row).lower(), axis=1)]
|
76 |
+
if selected_category and selected_category != 'All':
|
77 |
+
filtered = filtered[filtered["Category"] == selected_category]
|
78 |
+
if cyber_approved:
|
79 |
+
filtered = filtered[filtered["Cyber Approved"] == True]
|
80 |
+
if accessibility_approved:
|
81 |
+
filtered = filtered[filtered["Accessibility Approved"] == True]
|
82 |
+
if privacy_approved:
|
83 |
+
filtered = filtered[filtered["Privacy Approved"] == True]
|
84 |
+
if review_status and review_status != 'All':
|
85 |
+
filtered = filtered[filtered["Review Status"] == review_status]
|
86 |
+
return filtered
|
87 |
+
|
88 |
+
catalog = load_catalog()
|
89 |
+
|
90 |
+
|
91 |
+
st.markdown("""
|
92 |
+
<style>
|
93 |
+
.custom-header {
|
94 |
+
font-size: 24px;
|
95 |
+
font-weight: bold;
|
96 |
+
color: #4f8bf9;
|
97 |
+
margin-bottom: 10px;
|
98 |
+
}
|
99 |
+
.custom-text {
|
100 |
+
font-size: 16px;
|
101 |
+
margin-bottom: 20px;
|
102 |
+
}
|
103 |
+
.custom-button {
|
104 |
+
margin: 5px;
|
105 |
+
}
|
106 |
+
.stButton>button {
|
107 |
+
border: 2px solid #4f8bf9;
|
108 |
+
border-radius: 20px;
|
109 |
+
color: #4f8bf9;
|
110 |
+
}
|
111 |
+
.status-true {
|
112 |
+
color: green;
|
113 |
+
}
|
114 |
+
.stDataFrame {
|
115 |
+
font-size: 14px;
|
116 |
+
}
|
117 |
+
</style>
|
118 |
+
""", unsafe_allow_html=True)
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
|
123 |
+
|
124 |
+
|
125 |
+
|
126 |
+
# Streamlit app layout
|
127 |
+
st.markdown('<p class="custom-header">Enterprise Software Product Catalog</p>', unsafe_allow_html=True)
|
128 |
+
st.markdown('<p class="custom-text">This is the source of truth for app approval statuses within the enterprise.</p>', unsafe_allow_html=True)
|
129 |
+
|
130 |
+
|
131 |
+
|
132 |
+
# Sidebar for Advanced Search and Filtering
|
133 |
+
with st.sidebar:
|
134 |
+
st.markdown('<p class="custom-header">Advanced Search Options</p>', unsafe_allow_html=True)
|
135 |
+
search_query = st.text_input("Search by Any Attribute", key='search_query')
|
136 |
+
selected_category = st.selectbox("Select Category", ['All'] + list(catalog["Category"].unique()), key='search_category')
|
137 |
+
cyber_approved = st.checkbox("Cyber Approved", key='cyber_approved')
|
138 |
+
accessibility_approved = st.checkbox("Accessibility Approved", key='accessibility_approved')
|
139 |
+
privacy_approved = st.checkbox("Privacy Approved", key='privacy_approved')
|
140 |
+
review_status_options = ['All', 'Approved', 'Under Review', 'Not Approved']
|
141 |
+
review_status = st.selectbox("Select Review Status", options=review_status_options, key='review_status')
|
142 |
+
|
143 |
+
|
144 |
+
# Apply the enhanced filter based on user input
|
145 |
+
filtered_catalog = filter_catalog(catalog, search_query, selected_category, cyber_approved, accessibility_approved, privacy_approved, review_status)
|
146 |
+
|
147 |
+
|
148 |
+
|
149 |
+
# Display the filtered product catalog
|
150 |
+
st.markdown('<p class="custom-header">Product Catalog</p>', unsafe_allow_html=True)
|
151 |
+
st.dataframe(filtered_catalog.style.applymap(lambda x: "background-color: #ffffff"))
|
152 |
+
|
153 |
+
for index, row in filtered_catalog.iterrows():
|
154 |
+
with st.expander(f"{row['Product Name']}"):
|
155 |
+
st.markdown(f"""
|
156 |
+
<div>
|
157 |
+
<p><b>Category:</b> {row['Category']}</p>
|
158 |
+
<p><b>Cyber Approved:</b> <span class='{"status-true" if row["Cyber Approved"] else ""}'>{'Yes' if row['Cyber Approved'] else 'No'}</span></p>
|
159 |
+
<p><b>Accessibility Approved:</b> <span class='{"status-true" if row["Accessibility Approved"] else ""}'>{'Yes' if row['Accessibility Approved'] else 'No'}</span></p>
|
160 |
+
<p><b>Privacy Approved:</b> <span class='{"status-true" if row["Privacy Approved"] else ""}'>{'Yes' if row['Privacy Approved'] else 'No'}</span></p>
|
161 |
+
<p><b>Review Date:</b> {row['Review Date']}</p>
|
162 |
+
<p><b>Review Status:</b> {row['Review Status']}</p>
|
163 |
+
{'<p><b>Not Approved Reason:</b> '+row['Not Approved Reason']+'</p>' if row['Review Status'] == 'Not Approved' else ''}
|
164 |
+
</div>
|
165 |
+
""", unsafe_allow_html=True)
|
166 |
+
|