Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,133 +1,140 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
import pandas as pd
|
| 3 |
-
import
|
| 4 |
-
|
| 5 |
import os
|
|
|
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
if
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
data.append(new_entry)
|
| 40 |
-
|
| 41 |
-
with open(SAVED_LISTS_FILE, "w") as f:
|
| 42 |
-
json.dump(data, f, indent=2)
|
| 43 |
-
|
| 44 |
-
# Load lists
|
| 45 |
-
def load_saved_lists():
|
| 46 |
-
if os.path.exists(SAVED_LISTS_FILE):
|
| 47 |
-
with open(SAVED_LISTS_FILE, "r") as f:
|
| 48 |
-
return json.load(f)
|
| 49 |
-
return []
|
| 50 |
-
|
| 51 |
-
# Delete list
|
| 52 |
-
def delete_list(name):
|
| 53 |
-
data = load_saved_lists()
|
| 54 |
-
data = [d for d in data if d["list_name"] != name]
|
| 55 |
-
with open(SAVED_LISTS_FILE, "w") as f:
|
| 56 |
-
json.dump(data, f, indent=2)
|
| 57 |
-
|
| 58 |
-
# UI: Contact Filter Tab
|
| 59 |
-
def contact_filter_ui():
|
| 60 |
-
st.markdown("## π Skype Contacts: Saved Lists")
|
| 61 |
-
|
| 62 |
-
df = load_contacts()
|
| 63 |
-
country_col = "profile.locations[0].country"
|
| 64 |
-
search = st.text_input("π Search by Display Name")
|
| 65 |
-
if search:
|
| 66 |
-
df = df[df["display_name"].str.contains(search, case=False, na=False)]
|
| 67 |
-
|
| 68 |
-
# Logic choice
|
| 69 |
-
logic = st.radio("Tag Match Type", ["OR", "AND"], horizontal=True)
|
| 70 |
-
tag_selections = []
|
| 71 |
-
st.markdown("### π Filter by Tags")
|
| 72 |
-
cols = st.columns(4)
|
| 73 |
for i, tag in enumerate(TAGS):
|
| 74 |
-
if cols[i %
|
| 75 |
-
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
# Show tick boxes
|
| 86 |
-
st.markdown("### β
Tick Contacts to Save in a List")
|
| 87 |
-
show_df = df[["display_name", country_col]].copy()
|
| 88 |
-
show_df["checkbox"] = False
|
| 89 |
-
edited_df = st.data_editor(show_df, num_rows="dynamic", use_container_width=True)
|
| 90 |
-
|
| 91 |
-
# Save list
|
| 92 |
-
ticked = edited_df[edited_df["checkbox"] == True]
|
| 93 |
-
list_name = st.text_input("πΎ Name this list")
|
| 94 |
-
if st.button("Save List"):
|
| 95 |
-
if list_name and not ticked.empty:
|
| 96 |
-
save_new_list(list_name, ticked["display_name"].tolist(), ", ".join(tag_selections))
|
| 97 |
-
st.success(f"List '{list_name}' saved.")
|
| 98 |
else:
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import pandas as pd
|
| 2 |
+
import streamlit as st
|
| 3 |
+
import re
|
| 4 |
import os
|
| 5 |
+
import json
|
| 6 |
|
| 7 |
+
st.set_page_config(page_title="Skype Tool + Charterer Matrix", layout="wide")
|
| 8 |
+
|
| 9 |
+
CONTACT_FILE = "contacts.csv"
|
| 10 |
+
MATRIX_FILE = "channel_matrix.xlsx"
|
| 11 |
+
SAVE_FILE = "saved_lists.json"
|
| 12 |
+
|
| 13 |
+
tabs = st.tabs(["Contact Filter", "Channel Matrix"])
|
| 14 |
+
|
| 15 |
+
# Load or initialize saved lists
|
| 16 |
+
if os.path.exists(SAVE_FILE):
|
| 17 |
+
with open(SAVE_FILE, "r") as f:
|
| 18 |
+
saved_data = json.load(f)
|
| 19 |
+
else:
|
| 20 |
+
saved_data = {}
|
| 21 |
+
|
| 22 |
+
# TAB 1: Contact Filter Tool
|
| 23 |
+
with tabs[0]:
|
| 24 |
+
st.title("π Skype Contact Filter Tool")
|
| 25 |
+
|
| 26 |
+
TAGS = ["+mini", "+hdy", "+smx", "+pmx", "+cape", "+med", "+atl", "+rsea",
|
| 27 |
+
"+safr", "+pg", "+wci", "+eci", "+seas", "+feast", "+nopac", "+aus", "+aust"]
|
| 28 |
+
|
| 29 |
+
df = pd.read_csv(CONTACT_FILE)
|
| 30 |
+
df["display_name"] = df["display_name"].astype(str).str.lower()
|
| 31 |
+
if "country" not in df.columns:
|
| 32 |
+
df["country"] = "N/A"
|
| 33 |
+
|
| 34 |
+
filter_mode = st.radio("Filter Mode", ["AND", "OR"], horizontal=True)
|
| 35 |
+
|
| 36 |
+
st.markdown("### Select Tags to Filter")
|
| 37 |
+
selected_tags = []
|
| 38 |
+
cols = st.columns(6)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
for i, tag in enumerate(TAGS):
|
| 40 |
+
if cols[i % 6].checkbox(tag):
|
| 41 |
+
selected_tags.append(tag)
|
| 42 |
|
| 43 |
+
ticked_contacts = []
|
| 44 |
+
|
| 45 |
+
if selected_tags:
|
| 46 |
+
filtered_df = df.copy()
|
| 47 |
+
if filter_mode == "AND":
|
| 48 |
+
for tag in selected_tags:
|
| 49 |
+
filtered_df = filtered_df[filtered_df["display_name"].str.contains(re.escape(tag))]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
else:
|
| 51 |
+
pattern = "|".join(re.escape(tag) for tag in selected_tags)
|
| 52 |
+
filtered_df = filtered_df[filtered_df["display_name"].str.contains(pattern)]
|
| 53 |
+
|
| 54 |
+
st.success(f"Found {len(filtered_df)} matching contacts.")
|
| 55 |
+
|
| 56 |
+
with st.expander("β
Tick off contacts as you check them", expanded=False):
|
| 57 |
+
for i, row in filtered_df.iterrows():
|
| 58 |
+
ticked = st.checkbox(f"**{row['display_name'].title()}** _( {row['country']} )_", key=f"contact_{i}")
|
| 59 |
+
if ticked:
|
| 60 |
+
ticked_contacts.append({"display_name": row["display_name"], "country": row["country"]})
|
| 61 |
+
|
| 62 |
+
# Save preset block
|
| 63 |
+
if ticked_contacts:
|
| 64 |
+
st.markdown("### πΎ Save this list as a preset")
|
| 65 |
+
preset_name = st.text_input("Enter a name for this contact list")
|
| 66 |
+
if st.button("Save List"):
|
| 67 |
+
if preset_name:
|
| 68 |
+
saved_data[preset_name] = {
|
| 69 |
+
"tags": selected_tags,
|
| 70 |
+
"contacts": ticked_contacts
|
| 71 |
+
}
|
| 72 |
+
with open(SAVE_FILE, "w") as f:
|
| 73 |
+
json.dump(saved_data, f, indent=2)
|
| 74 |
+
st.success(f"List '{preset_name}' saved successfully!")
|
| 75 |
+
else:
|
| 76 |
+
st.warning("Please enter a name before saving.")
|
| 77 |
+
|
| 78 |
+
# Download button
|
| 79 |
+
csv_data = pd.DataFrame(ticked_contacts).to_csv(index=False)
|
| 80 |
+
st.download_button("π₯ Download Ticked Contacts as CSV", data=csv_data, file_name="ticked_contacts.csv", mime="text/csv")
|
| 81 |
+
else:
|
| 82 |
+
st.info("Select at least one tag to filter the contacts.")
|
| 83 |
+
|
| 84 |
+
# Load and manage saved lists
|
| 85 |
+
st.markdown("---")
|
| 86 |
+
st.markdown("### π Load or Manage a Saved List")
|
| 87 |
+
if saved_data:
|
| 88 |
+
selected_preset = st.selectbox("Select a saved contact list", list(saved_data.keys()))
|
| 89 |
+
if selected_preset:
|
| 90 |
+
st.markdown(f"**Tags:** `{', '.join(saved_data[selected_preset]['tags'])}`")
|
| 91 |
+
st.markdown("**Contacts in this list:**")
|
| 92 |
+
for c in saved_data[selected_preset]["contacts"]:
|
| 93 |
+
st.markdown(f"- **{c['display_name'].title()}** _( {c['country']} )_")
|
| 94 |
+
|
| 95 |
+
st.markdown("#### ποΈ Delete or Rename List")
|
| 96 |
+
col1, col2 = st.columns(2)
|
| 97 |
+
with col1:
|
| 98 |
+
if st.button("β Delete This List"):
|
| 99 |
+
del saved_data[selected_preset]
|
| 100 |
+
with open(SAVE_FILE, "w") as f:
|
| 101 |
+
json.dump(saved_data, f, indent=2)
|
| 102 |
+
st.success(f"Deleted list '{selected_preset}'. Please reload the page.")
|
| 103 |
+
with col2:
|
| 104 |
+
new_name = st.text_input("Rename List As", value=selected_preset)
|
| 105 |
+
if st.button("βοΈ Rename"):
|
| 106 |
+
if new_name and new_name != selected_preset:
|
| 107 |
+
saved_data[new_name] = saved_data.pop(selected_preset)
|
| 108 |
+
with open(SAVE_FILE, "w") as f:
|
| 109 |
+
json.dump(saved_data, f, indent=2)
|
| 110 |
+
st.success(f"Renamed to '{new_name}'. Please reload the page.")
|
| 111 |
+
else:
|
| 112 |
+
st.info("No saved lists available yet.")
|
| 113 |
+
|
| 114 |
+
# TAB 2: Charterer-Operator Matrix
|
| 115 |
+
with tabs[1]:
|
| 116 |
+
st.title("π Channel Matrix Checker")
|
| 117 |
+
|
| 118 |
+
try:
|
| 119 |
+
matrix_df = pd.read_excel(MATRIX_FILE)
|
| 120 |
+
matrix_df = matrix_df.rename(columns={matrix_df.columns[0]: "Operator"})
|
| 121 |
+
|
| 122 |
+
charterers = list(matrix_df.columns[1:])
|
| 123 |
+
selected_charterer = st.selectbox("Select a Charterer", charterers)
|
| 124 |
+
|
| 125 |
+
if selected_charterer:
|
| 126 |
+
yes_ops = matrix_df[matrix_df[selected_charterer].astype(str).str.upper() == "YES"]["Operator"]
|
| 127 |
+
no_ops = matrix_df[matrix_df[selected_charterer].astype(str).str.upper() == "NO"]["Operator"]
|
| 128 |
+
|
| 129 |
+
col1, col2 = st.columns(2)
|
| 130 |
+
with col1:
|
| 131 |
+
st.success(f"β
Operators who work with us for {selected_charterer}'s cargoes (CRH TRADING):")
|
| 132 |
+
for name in yes_ops:
|
| 133 |
+
st.markdown(f"- **{name}**")
|
| 134 |
+
with col2:
|
| 135 |
+
st.error(f"β Operators who won't work with us for {selected_charterer}'s cargoes (CRH TRADING):")
|
| 136 |
+
for name in no_ops:
|
| 137 |
+
st.markdown(f"- {name}")
|
| 138 |
+
except Exception as e:
|
| 139 |
+
st.warning("Could not load matrix file. Please ensure 'channel_matrix.xlsx' exists.")
|
| 140 |
+
st.text(f"Error: {e}")
|