-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add voilib management dashboard
- Loading branch information
1 parent
e4be96c
commit 4c2345a
Showing
18 changed files
with
410 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/usr/bin/env bash | ||
|
||
echo "Running docker-entrypoint initialization script" | ||
|
||
# run migrations on sqlite database | ||
alembic upgrade head | ||
|
||
# create, if needed, the admin user | ||
voilib-management --create-admin | ||
|
||
# run the CMD passed as command-line arguments | ||
exec "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Copyright (c) 2022-2023 Pablo González Carrizo (unmonoqueteclea) | ||
# All rights reserved. | ||
|
||
import asyncio | ||
import typing | ||
from datetime import timedelta | ||
|
||
import streamlit as st | ||
|
||
from voilib import auth | ||
|
||
st.set_page_config(page_title="Voilib", page_icon="🎧") | ||
st.title("🔑 Login") | ||
|
||
SHOW_LOGIN_FORM = True | ||
USERNAME_KEY = "logged_user_username" | ||
TOKEN_KEY = "logged_user_token" | ||
|
||
|
||
async def _login(username: str, password: str) -> typing.Optional[str]: | ||
user = await auth.authenticate_user(username, password) | ||
if user: | ||
return auth.create_access_token( | ||
data={"sub": user.username}, # type: ignore | ||
expires_delta=timedelta(minutes=auth.ACCESS_TOKEN_EXPIRE_MINUTES), | ||
) | ||
|
||
|
||
async def main(): | ||
global SHOW_LOGIN_FORM | ||
if USERNAME_KEY in st.session_state and TOKEN_KEY in st.session_state: | ||
SHOW_LOGIN_FORM = False | ||
if SHOW_LOGIN_FORM: | ||
with st.form(key="login"): | ||
username = st.text_input("Username") | ||
password = st.text_input("Password", type="password") | ||
clicked = st.form_submit_button("Login", use_container_width=True) | ||
if clicked: | ||
token = await _login(username, password) | ||
if token: | ||
st.session_state[USERNAME_KEY] = username | ||
st.session_state[TOKEN_KEY] = token | ||
SHOW_LOGIN_FORM = False | ||
st.experimental_rerun() | ||
else: | ||
st.error("Invalid credentials. Please, try again") | ||
else: | ||
username = st.session_state[USERNAME_KEY] | ||
st.info(f"""👤 Already logged in as **{username}**""") | ||
logout = st.button("Logout", use_container_width=True) | ||
if logout: | ||
del st.session_state[USERNAME_KEY] | ||
del st.session_state[TOKEN_KEY] | ||
SHOW_LOGIN_FORM = True | ||
st.experimental_rerun() | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Copyright (c) 2022-2023 Pablo González Carrizo (unmonoqueteclea) | ||
# All rights reserved. | ||
|
||
import asyncio | ||
|
||
import pandas as pd | ||
import streamlit as st | ||
|
||
from voilib.management import utils | ||
from voilib.models import analytics | ||
|
||
|
||
async def main(): | ||
st.set_page_config(page_title="Voilib", page_icon="🎧") | ||
st.title("📈 Stats") | ||
authenticated = utils.login_message(st.session_state) | ||
|
||
if authenticated: | ||
tab_last, tab_graphs = st.tabs(["Last queries", "Queries per day"]) | ||
with tab_last: | ||
st.write("Last 20 queries performed by Voilib users") | ||
qs = await analytics.Query.objects.order_by("-created_at").limit(20).all() | ||
markdown_queries = "" | ||
for query in qs: | ||
date = query.created_at.strftime("%Y-%m-%d, %H:%M:%S") # type: ignore | ||
markdown_queries += f"\n - `{date}` {query.text}" | ||
if len(qs) == 0: | ||
st.write("⚠️ No queries yet!") | ||
st.markdown(markdown_queries) | ||
with tab_graphs: | ||
qs = await analytics.Query.objects.order_by("-created_at").values( | ||
fields=["created_at", "text"] | ||
) | ||
df = pd.DataFrame(qs) | ||
if df.shape[0] == 0: | ||
st.write("⚠️ No queries yet!") | ||
else: | ||
df["created_at"] = pd.to_datetime(df["created_at"]).dt.date | ||
st.bar_chart(data=df.created_at.value_counts()) | ||
refresh = st.button("Refresh", use_container_width=True) | ||
if refresh: | ||
st.experimental_rerun() | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright (c) 2022-2023 Pablo González Carrizo (unmonoqueteclea) | ||
# All rights reserved. | ||
|
||
import asyncio | ||
|
||
import streamlit as st | ||
from voilib import collection, models, routers, settings | ||
from voilib.management import utils as m_utils | ||
|
||
|
||
async def add_channel(): | ||
st.header("Add new podcast") | ||
with st.form("my_form"): | ||
st.markdown( | ||
"""Write below the RSS feed url from a podcast and click `ADD` | ||
to include it in the database. """ | ||
) | ||
st.markdown( | ||
"""After adding a new channel, you should | ||
""" | ||
) | ||
channel_url = st.text_input("Channel RSS feed url") | ||
add_click = st.form_submit_button("Add channel", use_container_width=True) | ||
if add_click: | ||
with st.spinner("⌛ Adding new channel... Please, wait."): | ||
_, ch = await collection.get_or_create_channel(channel_url) | ||
settings.queue.enqueue( | ||
collection.update_channel, ch, job_timeout="600m" | ||
) | ||
st.success( | ||
f"""Channel "{ch.title}" correctly added to the | ||
database. Its episodes are being updated in a | ||
background task. This process can take a few minutes.""" | ||
) | ||
|
||
|
||
async def podcasts_and_episodes(): | ||
st.header("Podcasts and episodes") | ||
|
||
col1, col2, col3 = st.columns(3) | ||
col1.metric("Channels", await models.Channel.objects.count()) | ||
col2.metric( | ||
"Transcribed episodes", | ||
await models.Episode.objects.filter(transcribed=True).count(), | ||
) | ||
col3.metric( | ||
"Indexed episodes", | ||
await models.Episode.objects.filter(embeddings=True).count(), | ||
) | ||
with st.spinner("⌛ Loading channels..."): | ||
for ch in (await routers.analytics._media()).channels: | ||
with st.expander( | ||
f"**{ch.title}**. Indexed {ch.available_episodes}/{ch.total_episodes}" | ||
): | ||
st.image(ch.image) | ||
st.markdown(ch.description) | ||
|
||
|
||
async def main(): | ||
st.set_page_config(page_title="Voilib", page_icon="🎧") | ||
st.title("📻 Media") | ||
authenticated = m_utils.login_message(st.session_state) | ||
if authenticated: | ||
await add_channel() | ||
st.divider() | ||
await podcasts_and_episodes() | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) |
Oops, something went wrong.