This commit is contained in:
@@ -14,12 +14,5 @@ hideWelcomeMessage = true
|
|||||||
toolbarMode = "viewer"
|
toolbarMode = "viewer"
|
||||||
showSidebarNavigation = false
|
showSidebarNavigation = false
|
||||||
|
|
||||||
[theme]
|
|
||||||
base="light"
|
|
||||||
backgroundColor = "#eee"
|
|
||||||
secondaryBackgroundColor = "#fff"
|
|
||||||
primaryColor = "black"
|
|
||||||
baseRadius = "none"
|
|
||||||
|
|
||||||
[runner]
|
[runner]
|
||||||
magicEnabled = false
|
magicEnabled = false
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
import streamlit as st
|
import streamlit as st
|
||||||
from pandas.core.ops.docstrings import key
|
|
||||||
from pandas.io.formats.style import Styler
|
|
||||||
|
|
||||||
import queries.crud as crud
|
import queries.crud as crud
|
||||||
|
import themes as th
|
||||||
|
|
||||||
is_login_enabled = hasattr(st, 'user') and hasattr(st.user, 'is_logged_in')
|
is_login_enabled = hasattr(st, 'user') and hasattr(st.user, 'is_logged_in')
|
||||||
is_logged_in = is_login_enabled and st.user.is_logged_in
|
is_logged_in = is_login_enabled and st.user.is_logged_in
|
||||||
|
|
||||||
st.title("Settings")
|
st.title("Settings")
|
||||||
|
|
||||||
if hasattr(st.session_state, 'user_name'):
|
if hasattr(st.session_state, 'user_name'):
|
||||||
st.markdown(f"Currently logged in as **{st.session_state.user_name}**")
|
st.markdown(f"Currently logged in as **{st.session_state.user_name}**")
|
||||||
|
|
||||||
|
|
||||||
|
st.header("Theme")
|
||||||
|
themes = st.session_state.themes
|
||||||
|
with st.container(horizontal=True, width="stretch"):
|
||||||
|
for theme in ["light", "dark"]:
|
||||||
|
if st.button(label=themes[theme]["button_face_label"],
|
||||||
|
icon = themes[theme]["button_face_icon"],
|
||||||
|
disabled = (theme == st.session_state.current_theme),
|
||||||
|
width = "stretch"):
|
||||||
|
th.change_theme(theme)
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
st.header("Colors")
|
st.header("Colors")
|
||||||
with st.container(key="settings-color-selector"):
|
with st.container(key="settings-color-selector"):
|
||||||
palettes = crud.get_color_palettes()
|
palettes = crud.get_color_palettes()
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ def get_color_palettes():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_color_palette():
|
def get_color_palette() -> int:
|
||||||
user_id = int(st.session_state.user_id)
|
user_id = int(st.session_state.user_id)
|
||||||
try:
|
try:
|
||||||
return int(connection().query('SELECT color_palette_id FROM users WHERE id = :id''', params={'id': user_id})['color_palette_id'][0])
|
return int(connection().query('SELECT color_palette_id FROM users WHERE id = :id''', params={'id': user_id})['color_palette_id'][0])
|
||||||
@@ -123,3 +123,26 @@ def get_colors():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def set_theme(theme:str):
|
||||||
|
user_id = int(st.session_state.user_id)
|
||||||
|
logger.info("Changing theme for user %d to %s", user_id, theme)
|
||||||
|
with connection().session as session:
|
||||||
|
try:
|
||||||
|
query = text('UPDATE users SET theme = :theme WHERE id = :user')
|
||||||
|
session.execute(query, {
|
||||||
|
'theme': theme,
|
||||||
|
'user': user_id
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
session.rollback()
|
||||||
|
|
||||||
|
def get_theme() -> str:
|
||||||
|
user_id = int(st.session_state.user_id)
|
||||||
|
try:
|
||||||
|
return connection().query('SELECT theme FROM users u WHERE u.id = :id', params={'id': user_id})['theme'][0]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return None
|
||||||
@@ -43,25 +43,27 @@ def create_user(email, name, oidc_user_id):
|
|||||||
|
|
||||||
|
|
||||||
def set_user_in_session(user: UserInfoProxy):
|
def set_user_in_session(user: UserInfoProxy):
|
||||||
email = user.email
|
|
||||||
user_id = user.sub
|
|
||||||
if hasattr(user, 'name'):
|
|
||||||
name = user.name
|
|
||||||
else:
|
|
||||||
name = None
|
|
||||||
|
|
||||||
user_entity = find_user_by_oidc_id(user_id)
|
email = user.email if hasattr(user, "email") else None
|
||||||
|
user_id = user.sub if hasattr(user, "sub") else None
|
||||||
|
name = user.name if hasattr(user, "name") else None
|
||||||
|
|
||||||
|
user_entity = find_user_by_oidc_id(user_id) if user_id else st.dataframe()
|
||||||
if user_entity.empty:
|
if user_entity.empty:
|
||||||
user_entity = find_user_by_email(email)
|
user_entity = find_user_by_email(email) if email else st.dataframe()
|
||||||
if user_entity.empty:
|
if user_entity.empty:
|
||||||
user_entity = find_default_user()
|
user_entity = find_default_user()
|
||||||
if user_entity.empty:
|
if user_entity.empty and email and name and user_id:
|
||||||
user_entity = create_user(email, name, user_id)
|
user_entity = create_user(email, name, user_id)
|
||||||
else:
|
elif name:
|
||||||
update_default_user(email, name, user_id)
|
update_default_user(email, name, user_id)
|
||||||
user_entity = find_user_by_oidc_id(user_id)
|
user_entity = find_user_by_oidc_id(user_id)
|
||||||
|
|
||||||
|
if not user_entity.empty:
|
||||||
st.session_state.user_id = user_entity["id"][0]
|
st.session_state.user_id = user_entity["id"][0]
|
||||||
st.session_state.user_name = user_entity["name"][0]
|
st.session_state.user_name = user_entity["name"][0]
|
||||||
st.session_state.user_email = user_entity["email"][0]
|
st.session_state.user_email = user_entity["email"][0]
|
||||||
st.session_state.user_external_id = user_entity["oidc_user_id"][0]
|
st.session_state.user_external_id = user_entity["oidc_user_id"][0]
|
||||||
|
st.session_state.current_theme = user_entity["theme"][0]
|
||||||
|
else:
|
||||||
|
logger.warn("No active user found!")
|
||||||
@@ -5,10 +5,12 @@ import queries.user as user_queries
|
|||||||
from logger import init_logger
|
from logger import init_logger
|
||||||
from styles import init_styles
|
from styles import init_styles
|
||||||
from user import init_user, is_login_enabled, is_logged_in
|
from user import init_user, is_login_enabled, is_logged_in
|
||||||
|
from themes import init_themes
|
||||||
|
|
||||||
init_logger()
|
init_logger()
|
||||||
init_user()
|
init_user()
|
||||||
init_styles()
|
init_styles()
|
||||||
|
init_themes()
|
||||||
|
|
||||||
if is_login_enabled and not is_logged_in:
|
if is_login_enabled and not is_logged_in:
|
||||||
with st.container(width="stretch", height="stretch", horizontal_alignment="center"):
|
with st.container(width="stretch", height="stretch", horizontal_alignment="center"):
|
||||||
|
|||||||
36
app/themes.py
Normal file
36
app/themes.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import streamlit as st
|
||||||
|
from queries import crud
|
||||||
|
|
||||||
|
def init_themes():
|
||||||
|
|
||||||
|
if 'themes' not in st.session_state:
|
||||||
|
st.session_state.themes = {
|
||||||
|
"dark": {
|
||||||
|
"theme.base": "dark",
|
||||||
|
"theme.backgroundColor": "black",
|
||||||
|
"theme.primaryColor": "#c98bdb",
|
||||||
|
"theme.secondaryBackgroundColor": "#5591f5",
|
||||||
|
"theme.textColor": "white",
|
||||||
|
"button_face_label": "Dark",
|
||||||
|
"button_face_icon": ":material/dark_mode:"
|
||||||
|
},
|
||||||
|
"light": {
|
||||||
|
"theme.base": "light",
|
||||||
|
"theme.backgroundColor": "white",
|
||||||
|
"theme.primaryColor": "#5591f5",
|
||||||
|
"theme.secondaryBackgroundColor": "#82E1D7",
|
||||||
|
"theme.textColor": "#0a1464",
|
||||||
|
"button_face_label": "Light",
|
||||||
|
"button_face_icon": ":material/light_mode:"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if 'current_theme' not in st.session_state:
|
||||||
|
st.session_state.current_theme = 'light'
|
||||||
|
change_theme('light')
|
||||||
|
|
||||||
|
def change_theme(theme):
|
||||||
|
crud.set_theme(theme)
|
||||||
|
for key, val in st.session_state.themes[theme].items():
|
||||||
|
if key.startswith("theme"):
|
||||||
|
st._config.set_option(key, val)
|
||||||
@@ -5,7 +5,4 @@ is_login_enabled = hasattr(st, 'user') and hasattr(st.user, 'is_logged_in')
|
|||||||
is_logged_in = is_login_enabled and st.user.is_logged_in
|
is_logged_in = is_login_enabled and st.user.is_logged_in
|
||||||
|
|
||||||
def init_user():
|
def init_user():
|
||||||
if is_logged_in:
|
|
||||||
user_queries.set_user_in_session(st.user)
|
user_queries.set_user_in_session(st.user)
|
||||||
else:
|
|
||||||
st.session_state.user_id = 1 # default user
|
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stPageLink a {
|
.stPageLink a {
|
||||||
background: whitesmoke;
|
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 45px;
|
width: 45px;
|
||||||
border: 1px solid silver;
|
border: 1px solid silver;
|
||||||
@@ -34,7 +33,6 @@
|
|||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: whitesmoke;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.st-key-new_counter_color_selector div[role = "radiogroup"] {
|
.st-key-new_counter_color_selector div[role = "radiogroup"] {
|
||||||
|
|||||||
25
migrations/versions/20260430181243_theme.py
Normal file
25
migrations/versions/20260430181243_theme.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""theme
|
||||||
|
|
||||||
|
Revision ID: 8be315e8a5dc
|
||||||
|
Revises: 720abfadcd44
|
||||||
|
Create Date: 2026-04-30 18:12:43.026620
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '8be315e8a5dc'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '720abfadcd44'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.add_column('users', sa.Column('theme', sa.String(), nullable=False, server_default='light'))
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_column('users', 'theme')
|
||||||
@@ -32,6 +32,7 @@ def user_config():
|
|||||||
st.session_state.user_name = "Test User"
|
st.session_state.user_name = "Test User"
|
||||||
st.session_state.user_email = "test@test.local"
|
st.session_state.user_email = "test@test.local"
|
||||||
st.session_state.user_external_id = "111-2222-3333"
|
st.session_state.user_external_id = "111-2222-3333"
|
||||||
|
st.session_state.current_theme = "light"
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app() -> AppTest:
|
def app() -> AppTest:
|
||||||
|
|||||||
@@ -1,15 +1,33 @@
|
|||||||
|
|
||||||
|
import queries.crud as crud
|
||||||
|
|
||||||
|
def test_change_theme(app):
|
||||||
|
app.run()
|
||||||
|
app.switch_page("pages/settings.py").run()
|
||||||
|
|
||||||
|
assert app.session_state.current_theme =="light", "Light theme should be default"
|
||||||
|
|
||||||
|
assert app.button[0].label == "Light"
|
||||||
|
assert app.button[0].disabled == True, "Light theme should be selected"
|
||||||
|
|
||||||
|
assert app.button[1].label == "Dark"
|
||||||
|
assert app.button[1].disabled == False, "Dark theme should be de-selected"
|
||||||
|
|
||||||
|
app.button[1].click().run()
|
||||||
|
|
||||||
|
assert "dark" == crud.get_theme()
|
||||||
|
|
||||||
def test_change_color_palette(app):
|
def test_change_color_palette(app):
|
||||||
app.run()
|
app.run()
|
||||||
app.switch_page("pages/settings.py").run()
|
app.switch_page("pages/settings.py").run()
|
||||||
|
|
||||||
assert app.button[0].disabled == True
|
assert app.button[2].disabled == True, "First palette should be selected"
|
||||||
assert app.button[0].label == "Flames **(selected)**"
|
assert app.button[2].label == "Flames **(selected)**"
|
||||||
|
|
||||||
app.button[1].click().run()
|
app.button[3].click().run()
|
||||||
|
|
||||||
assert app.button[0].disabled == False
|
assert app.button[2].disabled == False, "First palette should be de-selected"
|
||||||
assert app.button[0].label == "Flames"
|
assert app.button[2].label == "Flames"
|
||||||
|
|
||||||
assert app.button[1].disabled == True
|
assert app.button[3].disabled == True, "Second palette should be selected"
|
||||||
assert app.button[1].label == "Water **(selected)**"
|
assert app.button[3].label == "Water **(selected)**"
|
||||||
Reference in New Issue
Block a user