SmartHeal commited on
Commit
d884caf
Β·
verified Β·
1 Parent(s): e68465b

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +30 -19
src/streamlit_app.py CHANGED
@@ -8,7 +8,7 @@ st.title("πŸ”Ž Simple Password Lookup")
8
  st.caption("Upload your Excel/CSV once β†’ it will be saved for future sessions β†’ search to reveal passwords.")
9
 
10
  # ---------------- Config ----------------
11
- # On Hugging Face Spaces, /data is persistent; locally we fall back to ./creds.xlsx
12
  PERSIST_FILE = Path("/data/creds.xlsx") if Path("/data").exists() else Path("creds.xlsx")
13
 
14
  ALIASES = {
@@ -22,34 +22,38 @@ EXPECTED = ["name", "username", "url", "password", "note"]
22
 
23
  def standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
24
  """Map common header aliases -> EXPECTED, create missing columns, clean strings."""
 
25
  df.columns = [str(c).strip().lower() for c in df.columns]
26
  colmap = {}
 
27
  for target, alias_list in ALIASES.items():
28
  for c in df.columns:
29
  if c in alias_list:
30
  colmap[target] = c
31
  break
 
32
  for col in EXPECTED:
33
  if col not in colmap:
34
  df[col] = ""
35
  colmap[col] = col
 
36
  out = df[[colmap[c] for c in EXPECTED]].rename(columns={colmap[c]: c for c in colmap})
 
37
  for c in EXPECTED:
38
- out[c] = out[c].astype(str).fillna("")
39
- # keep rows with at least one useful field
40
- mask = (
41
- out["name"].str.strip() != ""
42
- | out["username"].str.strip() != ""
43
- | out["url"].str.strip() != ""
44
- | out["password"].str.strip() != ""
45
- )
46
- return out[mask].reset_index(drop=True)
47
 
48
  def read_any(file) -> pd.DataFrame:
49
- """Read CSV or Excel from an uploaded file-like."""
50
  name = (getattr(file, "name", "") or "").lower()
51
  if name.endswith(".csv"):
52
  return pd.read_csv(file)
 
53
  return pd.read_excel(file)
54
 
55
  # ---------------- Load or Upload once ----------------
@@ -72,6 +76,7 @@ if st.session_state.creds is None:
72
  try:
73
  df = standardize_columns(read_any(up))
74
  st.session_state.creds = df
 
75
  try:
76
  PERSIST_FILE.parent.mkdir(parents=True, exist_ok=True)
77
  df.to_excel(PERSIST_FILE, index=False)
@@ -82,7 +87,7 @@ if st.session_state.creds is None:
82
  st.error(f"Failed to read file: {e}")
83
  st.stop()
84
  else:
85
- # Optional: allow replacing the saved file if needed
86
  with st.expander("Replace saved file (optional)"):
87
  new_up = st.file_uploader("Upload new Excel/CSV to replace saved file", type=["xlsx", "xls", "csv"], key="replacer")
88
  if new_up is not None:
@@ -105,10 +110,11 @@ q = st.text_input("Search by platform/site/username", placeholder="e.g., netflix
105
  if q.strip():
106
  Q = q.lower().strip()
107
  df = st.session_state.creds
 
108
  mask = (
109
- df["name"].str.lower().str.contains(Q)
110
- | df["username"].str.lower().str.contains(Q)
111
- | df["url"].str.lower().str.contains(Q)
112
  )
113
  results = df[mask]
114
  if results.empty:
@@ -116,12 +122,17 @@ if q.strip():
116
  else:
117
  st.caption(f"Matches: {len(results)} (showing up to 50)")
118
  for idx, row in results.head(50).iterrows():
119
- title = row["name"] or row["username"] or row["url"]
120
  with st.expander(f"{title} β€” {row['username']} | {row['url']}"):
121
  show = st.checkbox("Show password", key=f"show_{idx}")
122
- st.text_input("Password", value=row["password"], type=("default" if show else "password"), key=f"pw_{idx}")
123
- if row.get("note", "").strip():
124
- st.caption("Note: " + row["note"])
 
 
 
 
 
125
  else:
126
  st.caption("Type a keyword above to search.")
127
 
 
8
  st.caption("Upload your Excel/CSV once β†’ it will be saved for future sessions β†’ search to reveal passwords.")
9
 
10
  # ---------------- Config ----------------
11
+ # Hugging Face Spaces mounts /data as persistent storage; locally fall back to ./creds.xlsx
12
  PERSIST_FILE = Path("/data/creds.xlsx") if Path("/data").exists() else Path("creds.xlsx")
13
 
14
  ALIASES = {
 
22
 
23
  def standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
24
  """Map common header aliases -> EXPECTED, create missing columns, clean strings."""
25
+ # Normalize headers
26
  df.columns = [str(c).strip().lower() for c in df.columns]
27
  colmap = {}
28
+ # Map aliases
29
  for target, alias_list in ALIASES.items():
30
  for c in df.columns:
31
  if c in alias_list:
32
  colmap[target] = c
33
  break
34
+ # Ensure all expected columns exist
35
  for col in EXPECTED:
36
  if col not in colmap:
37
  df[col] = ""
38
  colmap[col] = col
39
+
40
  out = df[[colmap[c] for c in EXPECTED]].rename(columns={colmap[c]: c for c in colmap})
41
+ # Clean to strings
42
  for c in EXPECTED:
43
+ out[c] = out[c].astype(str).fillna("").replace("nan", "")
44
+
45
+ # βœ… Keep rows that have at least one non-empty (after strip) among key fields
46
+ key_cols = ["name", "username", "url", "password"]
47
+ mask = out[key_cols].applymap(lambda x: str(x).strip() != "").any(axis=1)
48
+ out = out[mask].reset_index(drop=True)
49
+ return out
 
 
50
 
51
  def read_any(file) -> pd.DataFrame:
52
+ """Read CSV or Excel from an uploaded file-like object."""
53
  name = (getattr(file, "name", "") or "").lower()
54
  if name.endswith(".csv"):
55
  return pd.read_csv(file)
56
+ # Default to Excel
57
  return pd.read_excel(file)
58
 
59
  # ---------------- Load or Upload once ----------------
 
76
  try:
77
  df = standardize_columns(read_any(up))
78
  st.session_state.creds = df
79
+ # Try to persist for future sessions
80
  try:
81
  PERSIST_FILE.parent.mkdir(parents=True, exist_ok=True)
82
  df.to_excel(PERSIST_FILE, index=False)
 
87
  st.error(f"Failed to read file: {e}")
88
  st.stop()
89
  else:
90
+ # Optional: allow replacing the saved file
91
  with st.expander("Replace saved file (optional)"):
92
  new_up = st.file_uploader("Upload new Excel/CSV to replace saved file", type=["xlsx", "xls", "csv"], key="replacer")
93
  if new_up is not None:
 
110
  if q.strip():
111
  Q = q.lower().strip()
112
  df = st.session_state.creds
113
+ # Robust contains with na=False to avoid NaN issues
114
  mask = (
115
+ df["name"].str.lower().str.contains(Q, na=False)
116
+ | df["username"].str.lower().str.contains(Q, na=False)
117
+ | df["url"].str.lower().str.contains(Q, na=False)
118
  )
119
  results = df[mask]
120
  if results.empty:
 
122
  else:
123
  st.caption(f"Matches: {len(results)} (showing up to 50)")
124
  for idx, row in results.head(50).iterrows():
125
+ title = (row["name"] or row["username"] or row["url"]).strip()
126
  with st.expander(f"{title} β€” {row['username']} | {row['url']}"):
127
  show = st.checkbox("Show password", key=f"show_{idx}")
128
+ st.text_input(
129
+ "Password",
130
+ value=row["password"],
131
+ type=("default" if show else "password"),
132
+ key=f"pw_{idx}",
133
+ )
134
+ if str(row.get("note", "")).strip():
135
+ st.caption("Note: " + str(row["note"]))
136
  else:
137
  st.caption("Type a keyword above to search.")
138