SmartHeal commited on
Commit
e68465b
ยท
verified ยท
1 Parent(s): 8475ebb

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +63 -26
src/streamlit_app.py CHANGED
@@ -1,11 +1,16 @@
 
1
  import streamlit as st
2
  import pandas as pd
 
3
 
4
  st.set_page_config(page_title="Simple Password Lookup", page_icon="๐Ÿ”Ž", layout="centered")
5
  st.title("๐Ÿ”Ž Simple Password Lookup")
6
- st.caption("Upload your Excel/CSV once โ†’ search by platform or username โ†’ reveal password.")
 
 
 
 
7
 
8
- # ---------------- Helpers ----------------
9
  ALIASES = {
10
  "name": ["name", "title", "site", "account", "platform", "service", "app"],
11
  "username": ["username", "user", "login", "email", "userid", "id"],
@@ -15,51 +20,83 @@ ALIASES = {
15
  }
16
  EXPECTED = ["name", "username", "url", "password", "note"]
17
 
18
-
19
  def standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
20
- # Normalize input headers to lowercase strings
21
  df.columns = [str(c).strip().lower() for c in df.columns]
22
  colmap = {}
23
- # map aliases
24
  for target, alias_list in ALIASES.items():
25
  for c in df.columns:
26
  if c in alias_list:
27
  colmap[target] = c
28
  break
29
- # ensure all expected exist
30
  for col in EXPECTED:
31
  if col not in colmap:
32
  df[col] = ""
33
  colmap[col] = col
34
  out = df[[colmap[c] for c in EXPECTED]].rename(columns={colmap[c]: c for c in colmap})
35
- # sanitize to strings
36
  for c in EXPECTED:
37
  out[c] = out[c].astype(str).fillna("")
38
- # drop fully empty rows
39
- mask = (out["name"].str.strip() != "") | (out["username"].str.strip() != "") | (out["url"].str.strip() != "") | (out["password"].str.strip() != "")
 
 
 
 
 
40
  return out[mask].reset_index(drop=True)
41
 
42
-
43
  def read_any(file) -> pd.DataFrame:
44
- name = (file.name or "").lower()
 
45
  if name.endswith(".csv"):
46
  return pd.read_csv(file)
47
  return pd.read_excel(file)
48
 
49
- # ---------------- Upload once ----------------
50
  if "creds" not in st.session_state:
51
- st.subheader("1) Upload your Excel/CSV")
 
 
 
 
 
 
 
 
 
 
 
52
  up = st.file_uploader("Choose file", type=["xlsx", "xls", "csv"], accept_multiple_files=False)
53
- if up is None:
54
  st.stop()
55
  try:
56
- df = read_any(up)
57
- df = standardize_columns(df)
58
  st.session_state.creds = df
59
- st.success(f"Loaded {len(df)} entries.")
 
 
 
 
 
60
  except Exception as e:
61
  st.error(f"Failed to read file: {e}")
62
  st.stop()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  # ---------------- Search ----------------
65
  st.subheader("2) Find your password")
@@ -75,25 +112,25 @@ if q.strip():
75
  )
76
  results = df[mask]
77
  if results.empty:
78
- st.warning("No matches. Try another keyword or check your file headers.")
79
  else:
80
  st.caption(f"Matches: {len(results)} (showing up to 50)")
81
  for idx, row in results.head(50).iterrows():
82
- title = row['name'] or row['username'] or row['url']
83
  with st.expander(f"{title} โ€” {row['username']} | {row['url']}"):
84
  show = st.checkbox("Show password", key=f"show_{idx}")
85
  st.text_input("Password", value=row["password"], type=("default" if show else "password"), key=f"pw_{idx}")
86
- if str(row.get("note", "")).strip():
87
- st.caption("Note: " + str(row["note"]))
88
  else:
89
  st.caption("Type a keyword above to search.")
90
 
91
- # ---------------- Tips ----------------
92
- with st.expander("Tips"):
93
  st.markdown(
94
  """
95
- - Required columns (case-insensitive, aliases ok): name, username, url, password. Optional: note.
96
- - Data is kept only **in memory** for this session. Re-upload if the app restarts.
97
- - For large files, use CSV for faster upload.
98
  """
99
  )
 
1
+ # simple_lookup_persistent.py
2
  import streamlit as st
3
  import pandas as pd
4
+ from pathlib import Path
5
 
6
  st.set_page_config(page_title="Simple Password Lookup", page_icon="๐Ÿ”Ž", layout="centered")
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 = {
15
  "name": ["name", "title", "site", "account", "platform", "service", "app"],
16
  "username": ["username", "user", "login", "email", "userid", "id"],
 
20
  }
21
  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 ----------------
56
  if "creds" not in st.session_state:
57
+ if PERSIST_FILE.exists():
58
+ try:
59
+ st.session_state.creds = standardize_columns(pd.read_excel(PERSIST_FILE))
60
+ st.info(f"Loaded saved credentials from: {PERSIST_FILE}")
61
+ except Exception as e:
62
+ st.error(f"Found {PERSIST_FILE} but failed to read it: {e}")
63
+ st.session_state.creds = None
64
+ else:
65
+ st.session_state.creds = None
66
+
67
+ if st.session_state.creds is None:
68
+ st.subheader("1) Upload your Excel/CSV (only once)")
69
  up = st.file_uploader("Choose file", type=["xlsx", "xls", "csv"], accept_multiple_files=False)
70
+ if not up:
71
  st.stop()
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)
78
+ st.success(f"Saved to {PERSIST_FILE}. You won't need to upload again.")
79
+ except Exception as e:
80
+ st.warning(f"Loaded for this session, but could not save for persistence: {e}")
81
  except Exception as e:
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:
89
+ try:
90
+ df_new = standardize_columns(read_any(new_up))
91
+ st.session_state.creds = df_new
92
+ try:
93
+ PERSIST_FILE.parent.mkdir(parents=True, exist_ok=True)
94
+ df_new.to_excel(PERSIST_FILE, index=False)
95
+ st.success(f"Replaced saved file at {PERSIST_FILE}.")
96
+ except Exception as e:
97
+ st.warning(f"Replaced in memory only, could not save: {e}")
98
+ except Exception as e:
99
+ st.error(f"Failed to read new file: {e}")
100
 
101
  # ---------------- Search ----------------
102
  st.subheader("2) Find your password")
 
112
  )
113
  results = df[mask]
114
  if results.empty:
115
+ st.warning("No matches found.")
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
 
128
+ # ---------------- Footer ----------------
129
+ with st.expander("Notes"):
130
  st.markdown(
131
  """
132
+ - Accepted columns (case-insensitive; aliases auto-mapped): **name**, **username**, **url**, **password**, *(optional)* **note**.
133
+ - Your first upload is saved to a persistent file so you **wonโ€™t have to re-upload again**.
134
+ - Use the โ€œReplace saved fileโ€ section above if you need to update your credentials later.
135
  """
136
  )