manoj112025 commited on
Commit
361ab09
Β·
1 Parent(s): 63165e6

Added the files

Browse files
Files changed (4) hide show
  1. Dockerfile +25 -0
  2. README.md +17 -0
  3. requirements.txt +4 -0
  4. streamlit_app/app_streamlit.py +163 -0
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ ENV PYTHONUNBUFFERED=1 \
4
+ PIP_NO_CACHE_DIR=1 \
5
+ PORT=7860 \
6
+ BACKEND_URL=""
7
+
8
+ WORKDIR /app
9
+
10
+ # System deps
11
+ RUN apt-get update && apt-get install -y --no-install-recommends \
12
+ build-essential \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Python deps
16
+ COPY requirements.txt /app/requirements.txt
17
+ RUN pip install --no-cache-dir -r /app/requirements.txt
18
+
19
+ # App code
20
+ COPY streamlit_app /app/streamlit_app
21
+
22
+ EXPOSE 7860
23
+
24
+ # Start Streamlit on HF-provided $PORT
25
+ CMD ["bash", "-lc", "streamlit run streamlit_app/app_streamlit.py --server.port=${PORT} --server.address=0.0.0.0"]
README.md CHANGED
@@ -8,3 +8,20 @@ pinned: false
8
  ---
9
 
10
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
11
+
12
+
13
+ # ExtraaLearn – Frontend (Docker Space)
14
+
15
+ This Space hosts the **Streamlit UI** for the ExtraaLearn Lead Conversion demo.
16
+ It **calls the Backend API Space** for predictions.
17
+
18
+ ## πŸ”§ Environment Variable
19
+
20
+ Set in **Settings β†’ Repository secrets / variables**:
21
+
22
+ - `BACKEND_URL` β†’ your backend Space base URL (without trailing slash)
23
+ Example: `https://<username>-<backendspace>.hf.space`
24
+
25
+ ## ▢️ Run
26
+
27
+ This is a **Hugging Face Docker Space**. The app starts with:
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit==1.31.0
2
+ pandas>=1.5.0
3
+ numpy>=1.23.0
4
+ requests>=2.31.0
streamlit_app/app_streamlit.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import time
4
+ import pandas as pd
5
+ import streamlit as st
6
+ import requests
7
+
8
+ # ----------------------------
9
+ # Configuration
10
+ # ----------------------------
11
+ # Set this via Hugging Face Space secrets / variables or Docker env
12
+ BACKEND_URL = os.getenv("BACKEND_URL", "").rstrip("/")
13
+ if BACKEND_URL == "":
14
+ st.warning(
15
+ "⚠️ BACKEND_URL is not set. "
16
+ "Set an environment variable `BACKEND_URL` to your backend Space base URL, "
17
+ "e.g. https://<username>-<backendspace>.hf.space"
18
+ )
19
+
20
+ st.set_page_config(page_title="ExtraaLearn – Lead Conversion", page_icon="πŸ“ˆ", layout="wide")
21
+ st.title("πŸ“ˆ ExtraaLearn – Lead Conversion (Frontend)")
22
+
23
+ with st.expander("ℹ️ Instructions", expanded=False):
24
+ st.markdown(
25
+ """
26
+ - Set environment variable **`BACKEND_URL`** to your backend Space URL (without trailing slash).
27
+ - Use the **Single Prediction** tab for an individual lead.
28
+ - Use the **Batch Scoring** tab to upload a CSV and get predictions back.
29
+ """
30
+ )
31
+
32
+ # ----------------------------
33
+ # Utilities
34
+ # ----------------------------
35
+ def ping_backend():
36
+ if not BACKEND_URL:
37
+ return False, {"error": "BACKEND_URL not set"}
38
+ try:
39
+ r = requests.get(f"{BACKEND_URL}/", timeout=10)
40
+ return r.ok, r.json()
41
+ except Exception as e:
42
+ return False, {"error": str(e)}
43
+
44
+ def post_json(endpoint: str, payload):
45
+ url = f"{BACKEND_URL}{endpoint}"
46
+ r = requests.post(url, json=payload, timeout=60)
47
+ r.raise_for_status()
48
+ return r
49
+
50
+ # ----------------------------
51
+ # Health check
52
+ # ----------------------------
53
+ colA, colB = st.columns([1, 2])
54
+ with colA:
55
+ if st.button("πŸ” Check Backend Health"):
56
+ ok, info = ping_backend()
57
+ if ok:
58
+ st.success(f"Backend OK Β· model_loaded={info.get('model_loaded')}")
59
+ else:
60
+ st.error(f"Backend unreachable: {info}")
61
+
62
+ # ----------------------------
63
+ # Tabs
64
+ # ----------------------------
65
+ tab1, tab2 = st.tabs(["Single Prediction", "Batch Scoring"])
66
+
67
+ # ---- Tab 1: Single ----
68
+ with tab1:
69
+ st.subheader("Single Prediction")
70
+ c1, c2, c3 = st.columns(3)
71
+
72
+ with c1:
73
+ age = st.number_input("Age", min_value=10, max_value=100, value=30)
74
+ website_visits = st.number_input("Website Visits", min_value=0, max_value=500, value=3)
75
+ time_spent_on_website = st.number_input("Time Spent on Website (sec)", min_value=0, max_value=200000, value=300)
76
+ page_views_per_visit = st.number_input("Page Views per Visit", min_value=0.0, max_value=100.0, value=3.5, step=0.1)
77
+
78
+ with c2:
79
+ current_occupation = st.selectbox("Current Occupation", ["Professional", "Student", "Unemployed"])
80
+ first_interaction = st.selectbox("First Interaction", ["Website", "Mobile App"])
81
+ last_activity = st.selectbox("Last Activity", ["Email Activity", "Phone Activity", "Website Activity"])
82
+ profile_completed = st.selectbox(
83
+ "Profile Completed",
84
+ ["Low (0-50%)", "Medium (50-75%)", "High (75-100%)"]
85
+ )
86
+
87
+ with c3:
88
+ print_media_type1 = st.selectbox("Saw Newspaper Ad (print_media_type1)", ["No", "Yes"])
89
+ print_media_type2 = st.selectbox("Saw Magazine Ad (print_media_type2)", ["No", "Yes"])
90
+ digital_media = st.selectbox("Saw Digital Ad", ["No", "Yes"])
91
+ educational_channels = st.selectbox("Heard via Educational Channels", ["No", "Yes"])
92
+ referral = st.selectbox("Referred by Someone", ["No", "Yes"])
93
+
94
+ if st.button("πŸš€ Predict"):
95
+ if not BACKEND_URL:
96
+ st.error("Please set BACKEND_URL to your backend Space URL.")
97
+ else:
98
+ payload = {
99
+ "age": age,
100
+ "website_visits": website_visits,
101
+ "time_spent_on_website": time_spent_on_website,
102
+ "page_views_per_visit": page_views_per_visit,
103
+ "print_media_type1": print_media_type1,
104
+ "print_media_type2": print_media_type2,
105
+ "digital_media": digital_media,
106
+ "educational_channels": educational_channels,
107
+ "referral": referral,
108
+ "current_occupation": current_occupation,
109
+ "first_interaction": first_interaction,
110
+ "last_activity": last_activity,
111
+ "profile_completed": profile_completed
112
+ }
113
+ try:
114
+ start = time.time()
115
+ r = post_json("/predict", payload)
116
+ elapsed = time.time() - start
117
+ result = r.json()
118
+ st.success(f"βœ… Inference OK Β· {elapsed:.2f}s")
119
+ st.metric("Conversion Probability", f"{result.get('probability', 0.0):.3f}")
120
+ pred = result.get("prediction", 0)
121
+ st.write("Prediction:", "βœ… Will Convert" if pred == 1 else "❌ Unlikely to Convert")
122
+ st.code(json.dumps(payload, indent=2), language="json")
123
+ except requests.HTTPError as http_err:
124
+ st.error(f"HTTP error: {http_err} Β· {r.text}")
125
+ except Exception as e:
126
+ st.error(f"Request failed: {e}")
127
+
128
+ # ---- Tab 2: Batch ----
129
+ with tab2:
130
+ st.subheader("Batch Scoring (CSV)")
131
+ st.caption("The CSV must include the same columns used during model training.")
132
+
133
+ sample_schema = [
134
+ "age","website_visits","time_spent_on_website","page_views_per_visit",
135
+ "print_media_type1","print_media_type2","digital_media","educational_channels","referral",
136
+ "current_occupation","first_interaction","last_activity","profile_completed"
137
+ ]
138
+ st.text_area("Expected columns", ", ".join(sample_schema), height=70, disabled=True)
139
+
140
+ up = st.file_uploader("Upload CSV", type=["csv"])
141
+ if up is not None:
142
+ try:
143
+ df = pd.read_csv(up)
144
+ st.write("Preview:", df.head())
145
+ if st.button("πŸ“€ Send to Backend"):
146
+ if not BACKEND_URL:
147
+ st.error("Please set BACKEND_URL to your backend Space URL.")
148
+ else:
149
+ records = df.to_dict(orient="records")
150
+ start = time.time()
151
+ r = post_json("/predict-batch", {"records": records})
152
+ elapsed = time.time() - start
153
+ out = pd.DataFrame(r.json())
154
+ st.success(f"βœ… Batch inference OK Β· {elapsed:.2f}s")
155
+ st.dataframe(out.head(20), use_container_width=True)
156
+ st.download_button(
157
+ "⬇️ Download Results",
158
+ out.to_csv(index=False).encode("utf-8"),
159
+ file_name="predictions.csv",
160
+ mime="text/csv"
161
+ )
162
+ except Exception as e:
163
+ st.error(f"Failed to process CSV: {e}")