Skip to content

Commit

Permalink
fix UX during search
Browse files Browse the repository at this point in the history
  • Loading branch information
manishshettym committed Feb 23, 2024
1 parent bcd49be commit 9a80f84
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 95 deletions.
19 changes: 16 additions & 3 deletions codescholar/apps/app_decl.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,22 @@ def search():
# search_main(args)

return flask.jsonify(
{
"status": "CodeScholar is now growing idioms for this API. Please try again in ~2 mins."
}
{"status": "CodeScholar is now growing idioms for this API."}
)


@scholarapp.route("/search_status", methods=["GET"])
def search_status():
api = flask.request.args.get("api")
# Check if the idioms for the API are ready
# This could involve checking a file, database, or cache status
idioms_dir = osp.join(api_cache_dir, api, "idioms", "progs")
idioms_ready = osp.exists(idioms_dir) and len(os.listdir(idioms_dir)) > 0
if idioms_ready:
return flask.jsonify({"status": "ready"})
else:
return flask.jsonify(
{"status": "CodeScholar is now growing idioms for this API."}
)


Expand Down
200 changes: 108 additions & 92 deletions codescholar/apps/streamlit_app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
import re
import math
import json
import pandas as pd
import numpy as np
from collections import namedtuple
import altair as alt

import time
import requests
import streamlit as st
import streamlit.components.v1 as components
Expand Down Expand Up @@ -97,6 +98,18 @@ def search_selected():
st.session_state["input_type"] = "search"


def check_search_status(api):
response = requests.get(f"http://{endpoint}/search_status", params={"api": api})
return response.json()


def get_idioms(api, size):
response = requests.post(
f"http://{endpoint}/search", json={"api": api, "size": size}
)
return response.json()


API = None

"""
Expand All @@ -105,12 +118,11 @@ def search_selected():
Further it also provides *LLM plugins* to automatically clean the idiom or generate some simple runnable code using it!
"""

with open(f"{root}/codescholar/apps/app_bench.json") as f:
benchmarks = json.load(f)

api_dirs = [d for d in os.listdir("cache") if os.path.isdir(f"./cache/{d}")]
api_options = set()
for lib in benchmarks:
for api in benchmarks[lib]:
for api in api_dirs:
api_progs = os.path.join("cache", api, "idioms", "progs")
if os.path.exists(api_progs) and len(os.listdir(api_progs)) > 0:
api_options.add(api)

st.session_state.dropdown_api = st.selectbox(
Expand Down Expand Up @@ -161,97 +173,101 @@ def search_selected():

size = st.slider("Choose the size of your idiom:", 4, 20, 4)

status_placeholder = st.empty()

with st.spinner("Growing your idioms 🌱..."):
response = requests.post(
f"http://{endpoint}/search", json={"api": API, "size": size}
)
idioms = response.json()
idioms = get_idioms(API, size)
status = check_search_status(API)["status"]
i = 0
while status != "ready":
status_placeholder.info(f"[{i}] {API} API is new to CodeScholar! {status}")
time.sleep(5)
status = check_search_status(API)["status"]
i += 1

status_placeholder.empty()
idioms = get_idioms(API, size)


if len(idioms) == 0:
st.error("No idioms found for API: {} 🫙".format(API))
else:
idioms = [
v
for k, v in sorted(
idioms.items(), key=lambda item: int(item[1]["freq"]), reverse=True
)
]
tabs = st.tabs(["Idiom {}".format(i + 1) for i in range(len(idioms))])

for idiom, tab in zip(idioms, tabs):
with tab:
st.write("🎓: Found this idiom in {} programs!".format(idiom["freq"]))
highlighted_idiom_html = highlight_code_with_html(idiom["idiom"])
maxh = max(100, min(30 * idiom["idiom"].count("\n"), 500))
components.html(highlighted_idiom_html, height=maxh, scrolling=True)

colbut1, colbut2 = st.columns([0.25, 0.8])
with colbut1:
but1 = st.button("Clean this idiom?", key=f"clean_{tabs.index(tab)}")
with colbut2:
but2 = st.button(
"Code with this idiom?", key=f"write_{tabs.index(tab)}"
)

if "status" in idioms:
st.info("{} API is new to CodeScholar! {}".format(API, idioms["status"]))
elif len(idioms) == 0:
st.error("No idioms found for API: {} 🫙".format(API))
else:
idioms = [
v
for k, v in sorted(
idioms.items(), key=lambda item: int(item[1]["freq"]), reverse=True
)
]
tabs = st.tabs(["Idiom {}".format(i + 1) for i in range(len(idioms))])

for idiom, tab in zip(idioms, tabs):
with tab:
st.write("🎓: Found this idiom in {} programs!".format(idiom["freq"]))
highlighted_idiom_html = highlight_code_with_html(idiom["idiom"])
maxh = max(100, min(30 * idiom["idiom"].count("\n"), 500))
components.html(highlighted_idiom_html, height=maxh, scrolling=True)

colbut1, colbut2 = st.columns([0.25, 0.8])
with colbut1:
but1 = st.button(
"Clean this idiom?", key=f"clean_{tabs.index(tab)}"
if but1:
with st.spinner("Cleaning your idioms 🧹..."):
response = requests.post(
f"http://{endpoint}/clean",
json={"api": API, "idiom": idiom["idiom"]},
)
with colbut2:
but2 = st.button(
"Code with this idiom?", key=f"write_{tabs.index(tab)}"
# st.error("This feature is not available yet! 🫙")
st.code(response.json()["idiom"], language="python")
if but2:
with st.spinner("Writing some code 👩🏻‍💻..."):
response = requests.post(
f"http://{endpoint}/write",
json={"api": API, "idiom": idiom["idiom"]},
)
# st.error("This feature is not available yet! 🫙")
st.code(response.json()["idiom"], language="python")

if but1:
with st.spinner("Cleaning your idioms 🧹..."):
response = requests.post(
f"http://{endpoint}/clean",
json={"api": API, "idiom": idiom["idiom"]},
)
# st.error("This feature is not available yet! 🫙")
st.code(response.json()["idiom"], language="python")
if but2:
with st.spinner("Writing some code 👩🏻‍💻..."):
response = requests.post(
f"http://{endpoint}/write",
json={"api": API, "idiom": idiom["idiom"]},
)
# st.error("This feature is not available yet! 🫙")
st.code(response.json()["idiom"], language="python")

st.divider()

"""
##### CodeScholar Suggestions
"""
with st.spinner("Analyzing your idioms 📊..."):
response = requests.post(f"http://{endpoint}/plot", json={"api": API})

metrics = response.json()
metrics_df = pd.DataFrame(
{
"size": metrics["sizes"],
"cluster": metrics["clusters"],
"freq": metrics["freq"],
}
)
st.divider()

x = metrics_df["size"].unique()
y1 = np.log(metrics_df.groupby("size")["cluster"].nunique())
y2 = np.log(metrics_df.groupby("size")["freq"].mean())
"""
##### CodeScholar Suggestions
"""
with st.spinner("Analyzing your idioms 📊..."):
response = requests.post(f"http://{endpoint}/plot", json={"api": API})

metrics = response.json()
metrics_df = pd.DataFrame(
{
"size": metrics["sizes"],
"cluster": metrics["clusters"],
"freq": metrics["freq"],
}
)

try:
ideal_size = np.where(y1 > y2)[0][0] + 3
except IndexError:
pass
else:
chart_data = pd.DataFrame(
{"Size (Expressivity)": sorted(x), "Diversity": y1, "Reusability": y2}
)
chart_data = chart_data[chart_data["Reusability"] > 0]
x = metrics_df["size"].unique()
y1 = np.log(metrics_df.groupby("size")["cluster"].nunique())
y2 = np.log(metrics_df.groupby("size")["freq"].mean())

col1, col2 = st.columns([2, 1])
col1.line_chart(
chart_data, x="Size (Expressivity)", y=["Diversity", "Reusability"]
)
col2.write(
"Ideal Idiom Size is **{} (± 1)** for {}".format(ideal_size, API)
)
col2.write(
"At this size, reusability, diversity, and expressivity are at an equilibrium!!"
)
try:
ideal_size = np.where(y1 > y2)[0][0] + 3
except IndexError:
pass
else:
chart_data = pd.DataFrame(
{"Size (Expressivity)": sorted(x), "Diversity": y1, "Reusability": y2}
)
chart_data = chart_data[chart_data["Reusability"] > 0]

col1, col2 = st.columns([2, 1])
col1.line_chart(
chart_data, x="Size (Expressivity)", y=["Diversity", "Reusability"]
)
col2.write("Ideal Idiom Size is **{} (± 1)** for {}".format(ideal_size, API))
col2.write(
"At this size, reusability, diversity, and expressivity are at an equilibrium!!"
)

0 comments on commit 9a80f84

Please sign in to comment.