Upload 21 files
Browse files- __pycache__/app.cpython-312.pyc +0 -0
- app.py +10 -2
- assets/style.css +11 -5
- components/__pycache__/browser_time.cpython-312.pyc +0 -0
- components/__pycache__/card.cpython-312.pyc +0 -0
- components/browser_time.py +14 -0
- components/browser_time_component/index.html +28 -0
- components/card.py +4 -3
- utils/__pycache__/time_utils.cpython-312.pyc +0 -0
- utils/time_utils.py +10 -0
__pycache__/app.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
|
|
|
app.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
from datetime import datetime
|
| 3 |
|
|
|
|
| 4 |
from components.buttons import render_top_buttons
|
| 5 |
from components.card import render_card
|
| 6 |
from components.header import render_header
|
|
@@ -35,6 +35,8 @@ def initialize_state() -> None:
|
|
| 35 |
st.session_state.time_checked = False
|
| 36 |
if "checked_at" not in st.session_state:
|
| 37 |
st.session_state.checked_at = None
|
|
|
|
|
|
|
| 38 |
|
| 39 |
|
| 40 |
def set_status(message: str, status_type: str = "info") -> None:
|
|
@@ -67,8 +69,13 @@ def add_card() -> None:
|
|
| 67 |
|
| 68 |
|
| 69 |
def check_time() -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
st.session_state.time_checked = True
|
| 71 |
-
st.session_state.checked_at =
|
| 72 |
set_status("Time check completed.", "info")
|
| 73 |
|
| 74 |
|
|
@@ -115,6 +122,7 @@ def handle_save(index: int) -> None:
|
|
| 115 |
|
| 116 |
load_css()
|
| 117 |
initialize_state()
|
|
|
|
| 118 |
fetch_cards_once()
|
| 119 |
render_top_buttons(add_card, check_time)
|
| 120 |
render_header()
|
|
|
|
| 1 |
import streamlit as st
|
|
|
|
| 2 |
|
| 3 |
+
from components.browser_time import get_browser_time
|
| 4 |
from components.buttons import render_top_buttons
|
| 5 |
from components.card import render_card
|
| 6 |
from components.header import render_header
|
|
|
|
| 35 |
st.session_state.time_checked = False
|
| 36 |
if "checked_at" not in st.session_state:
|
| 37 |
st.session_state.checked_at = None
|
| 38 |
+
if "browser_time" not in st.session_state:
|
| 39 |
+
st.session_state.browser_time = None
|
| 40 |
|
| 41 |
|
| 42 |
def set_status(message: str, status_type: str = "info") -> None:
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
def check_time() -> None:
|
| 72 |
+
browser_time = st.session_state.get("browser_time")
|
| 73 |
+
if not browser_time:
|
| 74 |
+
set_status("Browser time is not ready yet. Try again in a moment.", "warning")
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
st.session_state.time_checked = True
|
| 78 |
+
st.session_state.checked_at = browser_time
|
| 79 |
set_status("Time check completed.", "info")
|
| 80 |
|
| 81 |
|
|
|
|
| 122 |
|
| 123 |
load_css()
|
| 124 |
initialize_state()
|
| 125 |
+
st.session_state.browser_time = get_browser_time()
|
| 126 |
fetch_cards_once()
|
| 127 |
render_top_buttons(add_card, check_time)
|
| 128 |
render_header()
|
assets/style.css
CHANGED
|
@@ -30,6 +30,12 @@
|
|
| 30 |
padding-right: 0.75rem;
|
| 31 |
}
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
[data-testid="stHeader"] {
|
| 34 |
background: transparent;
|
| 35 |
}
|
|
@@ -97,8 +103,8 @@
|
|
| 97 |
}
|
| 98 |
|
| 99 |
.tone-strip-default {
|
| 100 |
-
background: rgba(18, 32, 51, 0.
|
| 101 |
-
color:
|
| 102 |
}
|
| 103 |
|
| 104 |
.tone-strip-success {
|
|
@@ -151,15 +157,15 @@
|
|
| 151 |
}
|
| 152 |
|
| 153 |
.end-time-inline-default {
|
| 154 |
-
background: rgba(18, 32, 51, 0.
|
| 155 |
}
|
| 156 |
|
| 157 |
.end-time-inline-success {
|
| 158 |
-
background: rgba(20, 125, 114, 0.
|
| 159 |
}
|
| 160 |
|
| 161 |
.end-time-inline-danger {
|
| 162 |
-
background: rgba(214, 69, 69, 0.
|
| 163 |
}
|
| 164 |
|
| 165 |
.end-time-inline-label {
|
|
|
|
| 30 |
padding-right: 0.75rem;
|
| 31 |
}
|
| 32 |
|
| 33 |
+
iframe[title="browser_time_component.browser_time_component"] {
|
| 34 |
+
display: none !important;
|
| 35 |
+
height: 0 !important;
|
| 36 |
+
min-height: 0 !important;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
[data-testid="stHeader"] {
|
| 40 |
background: transparent;
|
| 41 |
}
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
.tone-strip-default {
|
| 106 |
+
background: rgba(18, 32, 51, 0.08);
|
| 107 |
+
color: #55657d;
|
| 108 |
}
|
| 109 |
|
| 110 |
.tone-strip-success {
|
|
|
|
| 157 |
}
|
| 158 |
|
| 159 |
.end-time-inline-default {
|
| 160 |
+
background: rgba(18, 32, 51, 0.08);
|
| 161 |
}
|
| 162 |
|
| 163 |
.end-time-inline-success {
|
| 164 |
+
background: rgba(20, 125, 114, 0.2);
|
| 165 |
}
|
| 166 |
|
| 167 |
.end-time-inline-danger {
|
| 168 |
+
background: rgba(214, 69, 69, 0.2);
|
| 169 |
}
|
| 170 |
|
| 171 |
.end-time-inline-label {
|
components/__pycache__/browser_time.cpython-312.pyc
ADDED
|
Binary file (771 Bytes). View file
|
|
|
components/__pycache__/card.cpython-312.pyc
CHANGED
|
Binary files a/components/__pycache__/card.cpython-312.pyc and b/components/__pycache__/card.cpython-312.pyc differ
|
|
|
components/browser_time.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
import streamlit.components.v1 as components
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
_COMPONENT_DIR = Path(__file__).resolve().parent / "browser_time_component"
|
| 7 |
+
_browser_time_component = components.declare_component(
|
| 8 |
+
"browser_time_component",
|
| 9 |
+
path=str(_COMPONENT_DIR),
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def get_browser_time(key: str = "browser_time_component") -> str | None:
|
| 14 |
+
return _browser_time_component(key=key, default=None)
|
components/browser_time_component/index.html
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<script src="https://unpkg.com/streamlit-component-lib@2.0.0/dist/index.js"></script>
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<script>
|
| 10 |
+
function sendBrowserTime() {
|
| 11 |
+
const now = new Date().toISOString();
|
| 12 |
+
Streamlit.setComponentValue(now);
|
| 13 |
+
Streamlit.setFrameHeight(0);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
function onRender(event) {
|
| 17 |
+
const { disabled } = event.detail.args || {};
|
| 18 |
+
if (!disabled) {
|
| 19 |
+
sendBrowserTime();
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender);
|
| 24 |
+
Streamlit.setComponentReady();
|
| 25 |
+
Streamlit.setFrameHeight(0);
|
| 26 |
+
</script>
|
| 27 |
+
</body>
|
| 28 |
+
</html>
|
components/card.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
from datetime import datetime
|
| 3 |
|
| 4 |
-
from utils.time_utils import compute_end_time, format_time, get_card_tone,
|
| 5 |
|
| 6 |
|
| 7 |
def render_card(index: int, on_save, on_delete) -> None:
|
|
@@ -17,13 +17,14 @@ def render_card(index: int, on_save, on_delete) -> None:
|
|
| 17 |
card["endMinute"] = end_minute
|
| 18 |
checked_at_raw = st.session_state.get("checked_at")
|
| 19 |
checked_at = datetime.fromisoformat(checked_at_raw) if checked_at_raw else None
|
|
|
|
| 20 |
tone = get_card_tone(
|
| 21 |
end_hour,
|
| 22 |
end_minute,
|
| 23 |
reference_time=checked_at,
|
| 24 |
-
enabled=
|
| 25 |
)
|
| 26 |
-
tone_message =
|
| 27 |
|
| 28 |
with st.container(border=True):
|
| 29 |
st.markdown(
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
from datetime import datetime
|
| 3 |
|
| 4 |
+
from utils.time_utils import compute_end_time, format_time, get_card_tone, get_check_message
|
| 5 |
|
| 6 |
|
| 7 |
def render_card(index: int, on_save, on_delete) -> None:
|
|
|
|
| 17 |
card["endMinute"] = end_minute
|
| 18 |
checked_at_raw = st.session_state.get("checked_at")
|
| 19 |
checked_at = datetime.fromisoformat(checked_at_raw) if checked_at_raw else None
|
| 20 |
+
is_checked = st.session_state.get("time_checked", False)
|
| 21 |
tone = get_card_tone(
|
| 22 |
end_hour,
|
| 23 |
end_minute,
|
| 24 |
reference_time=checked_at,
|
| 25 |
+
enabled=is_checked,
|
| 26 |
)
|
| 27 |
+
tone_message = get_check_message(tone, is_checked)
|
| 28 |
|
| 29 |
with st.container(border=True):
|
| 30 |
st.markdown(
|
utils/__pycache__/time_utils.cpython-312.pyc
CHANGED
|
Binary files a/utils/__pycache__/time_utils.cpython-312.pyc and b/utils/__pycache__/time_utils.cpython-312.pyc differ
|
|
|
utils/time_utils.py
CHANGED
|
@@ -50,6 +50,16 @@ def get_tone_message(tone: str) -> str:
|
|
| 50 |
return "Press Check Time"
|
| 51 |
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
def build_card_payload() -> dict[str, str]:
|
| 54 |
end_hour, end_minute = compute_end_time("08", "00")
|
| 55 |
return {
|
|
|
|
| 50 |
return "Press Check Time"
|
| 51 |
|
| 52 |
|
| 53 |
+
def get_check_message(tone: str, checked: bool) -> str:
|
| 54 |
+
if not checked:
|
| 55 |
+
return "Press Check Time"
|
| 56 |
+
if tone == "danger":
|
| 57 |
+
return "Less than 5 minutes left"
|
| 58 |
+
if tone == "success":
|
| 59 |
+
return "Less than 10 minutes left"
|
| 60 |
+
return "More than 10 minutes left"
|
| 61 |
+
|
| 62 |
+
|
| 63 |
def build_card_payload() -> dict[str, str]:
|
| 64 |
end_hour, end_minute = compute_end_time("08", "00")
|
| 65 |
return {
|