Aravindhan11's picture
Update main.py
b4b7b5a verified
import os
import requests
import pandas as pd
import time
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("RAPIDAPI_KEY")
API_HOST = os.getenv("RAPIDAPI_HOST")
HEADERS = {
"x-rapidapi-host": API_HOST if API_HOST else "",
"x-rapidapi-key": API_KEY if API_KEY else ""
}
# Only build BASE_URL if API_HOST is present
BASE_URL = f"https://{API_HOST}/v1/app-store-api/reviews" if API_HOST else None
# Example apps with known Apple App IDs
APPLE_APP_IDS = {
"Photo Editor & Candy Camera & Grid & ScrapBook": 364709193,
"Coloring book moana": 123456789,
"U Launcher Lite – FREE Live Cool Themes, Hide Apps": 987654321,
# add more as needed
}
def fetch_reviews(app_id, page=1, country="us", lang="en", retries=3):
"""
Fetch reviews from the API. If BASE_URL or API credentials are missing,
safely return None and print an explanatory message.
"""
if BASE_URL is None or not API_KEY or not API_HOST:
print("⚠️ Skipping network call: RAPIDAPI_HOST or RAPIDAPI_KEY is not set in environment.")
return None
params = {
"id": app_id,
"sort": "mostRecent",
"page": page,
"country": country, # fixed typo from 'contry' earlier
"lang": lang
}
for attempt in range(1, retries+1):
try:
resp = requests.get(BASE_URL, headers=HEADERS, params=params, timeout=10)
resp.raise_for_status()
return resp.json()
except requests.exceptions.HTTPError as e:
status = resp.status_code if 'resp' in locals() else 'unknown'
print(f"HTTPError (status {status}) on attempt {attempt} for app_id {app_id}: {e}")
if status == 429: # rate limit
wait = 2 ** attempt
print(f"Rate limited. Waiting {wait}s before retry...")
time.sleep(wait)
elif status == 403:
print("Forbidden. Skipping this app.")
return None
else:
time.sleep(1)
except Exception as e:
print(f"Error fetching app_id {app_id}:", e)
time.sleep(1)
return None
def merge_google_apple(google_df):
apple_data = []
for app in google_df['App']:
app_id = APPLE_APP_IDS.get(app)
if not app_id:
print(f"No Apple App ID for {app}. Skipping.")
apple_data.append({"App": app, "AppleRating": None, "AppleReviews": 0})
continue
reviews = fetch_reviews(app_id)
if reviews:
# If API returns list of reviews
try:
ratings = [r.get("score", 0) for r in reviews]
avg_rating = sum(ratings)/len(ratings) if ratings else None
apple_data.append({
"App": app,
"AppleRating": avg_rating,
"AppleReviews": len(reviews)
})
except Exception:
# If API returns different structure, gracefully fallback
apple_data.append({"App": app, "AppleRating": None, "AppleReviews": 0})
else:
apple_data.append({"App": app, "AppleRating": None, "AppleReviews": 0})
apple_df = pd.DataFrame(apple_data)
combined = pd.merge(google_df, apple_df, on="App", how="outer")
os.makedirs("data/cleaned_data", exist_ok=True)
combined.to_csv("data/cleaned_data/combined_apps.csv", index=False)
print("✅ Combined data saved to data/cleaned_data/combined_apps.csv")
return combined
def main():
# Example Google Play data
google_data = {"App": list(APPLE_APP_IDS.keys())}
google_df = pd.DataFrame(google_data)
print("Merging with Apple App Store data...")
combined_df = merge_google_apple(google_df)
print(combined_df)
if __name__ == "__main__":
# If you run script directly, call main once (same behaviour as before)
main()
# ------------------------------------------------------------------
# Gradio UI wrapper (terminal-like single-file app).
# ------------------------------------------------------------------
import gradio as gr
import html
# Helper to render chat history as terminal-like HTML
def render_terminal_html(history):
"""
history: list of (role, message) tuples. role in ("user","assistant","system")
"""
lines = []
for role, msg in history:
safe = html.escape(str(msg)).replace("\n", "<br>")
if role == "user":
lines.append(f'<div class="line user">&gt; {safe}</div>')
elif role == "assistant":
lines.append(f'<div class="line assistant">{safe}</div>')
else:
lines.append(f'<div class="line system">{safe}</div>')
return "<div class='terminal'>\n" + "\n".join(lines) + "\n</div>"
# Load sample output (if exists)
def load_last_sentences(n=10):
path = "data/cleaned_data/combined_apps.csv"
if os.path.exists(path):
try:
df = pd.read_csv(path)
rows = []
for i, row in df.head(n).iterrows():
rows.append(f"{i+1}. App='{row.get('App', '')}' | AppleRating={row.get('AppleRating', '')} | AppleReviews={row.get('AppleReviews', '')}")
return "<pre class='sentences'>" + "\n".join(rows) + "</pre>"
except Exception as e:
return f"<pre class='sentences'>Error reading CSV: {e}</pre>"
else:
return "<pre class='sentences'>No combined CSV found. Run the merge (type 'run' or '/run').</pre>"
# The function that handles user messages in the chat UI
def handle_message(message, history):
if history is None:
history = []
history.append(("user", message))
normalized = message.strip().lower()
assistant_reply = ""
right_html = load_last_sentences()
if normalized in ["run", "/run", "merge", "/merge"]:
history.append(("system", "Executing main() — merging data. This may take a moment (uses your configured RAPIDAPI keys)."))
try:
# call the existing main() - it's defined above and will use your API keys/env
main()
assistant_reply = "✅ main() finished. Combined CSV saved to data/cleaned_data/combined_apps.csv"
right_html = load_last_sentences()
except Exception as e:
assistant_reply = f"❌ main() raised an exception: {e}"
right_html = f"<pre class='sentences'>Exception during run:\n{html.escape(str(e))}</pre>"
elif normalized in ["help", "/help", "commands"]:
assistant_reply = ("Available commands:\n"
"- run or /run : execute the merge (calls main())\n"
"- help : show this message\n"
"- any other text will be echoed back in terminal style.")
else:
assistant_reply = f"Echo: {message}"
history.append(("assistant", assistant_reply))
chat_html = render_terminal_html(history)
return chat_html, history, right_html
# Small reset function
def reset_chat():
history = [("system", "Terminal chat started. Type 'run' to execute data merge.")]
return render_terminal_html(history), history, load_last_sentences()
# CSS to inject (we won't pass it to gr.Blocks directly to avoid compatibility issues)
TERMINAL_CSS = """
<style>
.terminal {
background: #0b0f13;
color: #c8f7c5;
font-family: "Courier New", Courier, monospace;
padding: 12px;
height: 520px;
overflow: auto;
border-radius: 6px;
border: 1px solid #111;
}
.terminal .line { padding: 4px 0; white-space: pre-wrap; word-break: break-word; }
.terminal .user { color: #9be5ff; }
.terminal .assistant { color: #c8f7c5; }
.terminal .system { color: #9ea7b0; font-style: italic; }
.sentences {
background: #021013;
color: #bfe6c6;
font-family: "Courier New", monospace;
padding: 12px;
height: 520px;
overflow: auto;
border-radius: 6px;
border: 1px solid #111;
}
body { background: #0f1720; }
</style>
"""
# Build Gradio interface (compatible with older/newer gradio: avoid passing css kwarg)
with gr.Blocks(title="Terminal-style Chat UI for main.py") as demo:
# Inject CSS via HTML
gr.HTML(TERMINAL_CSS)
gr.Markdown("<h2 style='color: #c8f7c5; font-family: monospace;'>🔌 main.py — Terminal Chat UI (Gradio)</h2>")
with gr.Row():
with gr.Column(scale=1):
chat_html = gr.HTML(render_terminal_html([("system", "Terminal chat started. Type 'run' to execute data merge.")]), elem_id="chat_terminal")
user_input = gr.Textbox(label="Terminal input", placeholder="Type command or message (e.g. run, help) and press Enter", lines=1)
send_btn = gr.Button("Send")
reset_btn = gr.Button("Reset")
with gr.Column(scale=1.6):
gr.Markdown("**Output sentences / last merged rows**")
right_panel = gr.HTML(load_last_sentences(), elem_id="right_sentences")
history_state = gr.State([("system", "Terminal chat started. Type 'run' to execute data merge.")])
def submit_and_update(message, history):
chat_html_str, new_history, right_html = handle_message(message, history)
return chat_html_str, new_history, right_html
send_btn.click(fn=submit_and_update, inputs=[user_input, history_state], outputs=[chat_html, history_state, right_panel])
user_input.submit(fn=submit_and_update, inputs=[user_input, history_state], outputs=[chat_html, history_state, right_panel])
reset_btn.click(fn=reset_chat, inputs=None, outputs=[chat_html, history_state, right_panel])
gr.Markdown("<small style='color:#9ea7b0'>Notes: Type 'run' to call main(). If you want the app to actually fetch from RapidAPI, set RAPIDAPI_HOST and RAPIDAPI_KEY in your environment or a .env file.</small>")
# Launch only if running as script in a server environment
if __name__ == "__main__":
demo.launch()