WeatherAI / app.py
JAMESPARK3's picture
Update app.py
1492316 verified
raw
history blame
22.9 kB
import streamlit as st
import requests
import xmltodict
import pandas as pd
from datetime import datetime, timedelta
import streamlit.components.v1 as components
import plotly.express as px
import time
import plotly.io as pio
from openai import OpenAI
# plotly์˜ JSON ์ง๋ ฌํ™” ์—”์ง„์„ ๊ธฐ๋ณธ json์œผ๋กœ ์„ค์ •
pio.json.config.default_engine = 'json'
# ํŽ˜์ด์ง€ ์„ค์ •
st.set_page_config(
page_title="์šฐ๋ฆฌ์ง‘ ๋‚ ์”จ ์ •๋ณด",
page_icon="๐ŸŒค๏ธ",
layout="wide",
menu_items={
'Get Help': None,
'Report a bug': None,
'About': None
}
)
# CSS ์Šคํƒ€์ผ
st.markdown("""
<style>
section[data-testid="stSidebar"] {
display: none;
}
#MainMenu {
display: none;
}
header {
display: none;
}
.block-container {
padding: 0 !important;
max-width: 100% !important;
}
.element-container {
margin: 0 !important;
}
.stApp > header {
display: none;
}
#other-info {
display: none;
}
.stPlotlyChart {
width: 100%;
margin: 0 !important;
padding: 0 !important;
}
[data-testid="stMetricValue"] {
font-size: 3rem;
}
.time-container {
width: 100%;
text-align: center;
margin: 0 auto;
padding: 15px 0;
}
.date-text {
font-size: 6em !important;
font-weight: bold !important;
color: rgb(0, 0, 0) !important;
font-family: Arial, sans-serif !important;
text-shadow: none !important;
background: transparent !important;
display: block !important;
line-height: 1.2 !important;
margin-bottom: 0.5px !important;
}
h1, h2, h3, h4, h5, h6, p, .stMetric > div > div {
color: black !important;
}
.plotly-graph-div {
overflow-x: scroll !important;
min-width: 100% !important;
}
div[data-testid="stVerticalBlock"] > div {
padding: 0 !important;
}
.main {
padding: 0 !important;
}
.stApp {
margin: 0 !important;
}
[data-testid="stHeader"] {
display: none;
}
.section-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
padding: 1rem;
box-sizing: border-box;
background-color: inherit;
}
.graph-container {
width: 100%;
height: calc(100vh - 100px);
display: flex;
flex-direction: column;
align-items: center;
}
iframe {
margin: 0 !important;
padding: 0 !important;
}
[data-testid="column"] {
padding: 0 !important;
}
[data-testid="stVerticalBlock"] {
padding: 0 !important;
gap: 0 !important;
}
.dust-status {
font-size: 2em;
font-weight: bold;
color: black;
padding: 0.3rem 1rem;
border-radius: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: inline-block;
}
@keyframes scroll-text {
from {
transform: translateX(100%);
}
to {
transform: translateX(-100%);
}
}
.scroll-container {
position: fixed;
bottom: 20px;
left: 0;
width: 100%;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 0;
z-index: 1000;
}
.scroll-text {
display: inline-block;
white-space: nowrap;
animation: scrolling 60s linear infinite;
font-size: 1.5em;
font-weight: bold;
color: #333;
}
@keyframes scrolling {
0% {transform: translateX(100%);}
100% {transform: translateX(-100%);}
}
</style>
""", unsafe_allow_html=True)
def get_korean_weekday(date):
weekday = date.strftime('%a')
weekday_dict = {
'Mon': '์›”',
'Tue': 'ํ™”',
'Wed': '์ˆ˜',
'Thu': '๋ชฉ',
'Fri': '๊ธˆ',
'Sat': 'ํ† ',
'Sun': '์ผ'
}
return weekday_dict[weekday]
@st.cache_data(ttl=300) # 5๋ถ„๋งˆ๋‹ค ์บ์‹œ ๊ฐฑ์‹ 
def get_weather_data():
url = "http://openapi.seoul.go.kr:8088/77544e69764a414d363647424a655a/xml/citydata/1/5/์‹ ๋ฆผ์—ญ"
try:
response = requests.get(url)
response.raise_for_status() # HTTPError์— ๋Œ€ํ•ด ์˜ˆ์™ธ ๋ฐœ์ƒ
if not response.text.strip(): # ๋นˆ ์‘๋‹ต ์ฒ˜๋ฆฌ
raise ValueError("์‘๋‹ต์ด ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค.")
data = xmltodict.parse(response.text)
return data['SeoulRtd.citydata']['CITYDATA']['WEATHER_STTS']['WEATHER_STTS']
except requests.exceptions.RequestException as e:
st.error(f"API ํ˜ธ์ถœ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
except Exception as e:
st.error(f"๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}")
return None
def get_background_color(pm10_value):
try:
pm10 = float(pm10_value)
if pm10 <= 30:
return "#87CEEB" # ํŒŒ๋ž‘ (์ข‹์Œ)
elif pm10 <= 80:
return "#90EE90" # ์ดˆ๋ก (๋ณดํ†ต)
elif pm10 <= 150:
return "#FFD700" # ๋…ธ๋ž‘ (๋‚˜์จ)
else:
return "#FF6B6B" # ๋นจ๊ฐ• (๋งค์šฐ ๋‚˜์จ)
except:
return "#FFFFFF" # ๊ธฐ๋ณธ ํฐ์ƒ‰
def get_current_sky_status(data):
current_time = datetime.utcnow() + timedelta(hours=9)
current_hour = current_time.hour
forecast_data = data['FCST24HOURS']['FCST24HOURS']
if not isinstance(forecast_data, list):
forecast_data = [forecast_data]
closest_forecast = None
min_time_diff = float('inf')
for forecast in forecast_data:
forecast_hour = int(forecast['FCST_DT'][8:10])
time_diff = abs(forecast_hour - current_hour)
if time_diff < min_time_diff:
min_time_diff = time_diff
closest_forecast = forecast
return closest_forecast['SKY_STTS'] if closest_forecast else "์ •๋ณด์—†์Œ"
def format_news_message(news_list):
if not isinstance(news_list, list):
news_list = [news_list]
current_warnings = []
for news in news_list:
if not isinstance(news, dict):
continue
warn_val = news.get('WARN_VAL', '')
warn_stress = news.get('WARN_STRESS', '')
command = news.get('COMMAND', '')
warn_msg = news.get('WARN_MSG', '')
announce_time = news.get('ANNOUNCE_TIME', '')
if announce_time and len(announce_time) == 12:
year = announce_time[0:4]
month = announce_time[4:6]
day = announce_time[6:8]
hour = announce_time[8:10]
minute = announce_time[10:12]
formatted_time = f"({year}๋…„{month}์›”{day}์ผ{hour}์‹œ{minute}๋ถ„)"
else:
formatted_time = ""
if command == 'ํ•ด์ œ':
warning_text = f"โœ… {warn_val}{warn_stress} ํ•ด์ œ {formatted_time} {warn_msg}"
else:
warning_text = f"โš ๏ธ {warn_val}{warn_stress} ๋ฐœ๋ น {formatted_time} {warn_msg}"
current_warnings.append(warning_text)
return ' | '.join(current_warnings)
def show_weather_info(data):
st.markdown('<div class="section-container">', unsafe_allow_html=True)
# Add update time display using the last API call timestamp (already in KST)
refresh_time = datetime.fromtimestamp(st.session_state.last_api_call) if st.session_state.last_api_call else (datetime.utcnow() + timedelta(hours=9))
st.markdown(f'''
<div style="text-align: center; font-size: 0.8em; color: gray;">
Data refreshed at: {refresh_time.strftime('%Y-%m-%d %H:%M:%S')}
</div>
''', unsafe_allow_html=True)
# Add this code to define formatted_date
current_time = datetime.utcnow() + timedelta(hours=9)
weekday = get_korean_weekday(current_time)
formatted_date = f"{current_time.strftime('%Y-%m-%d')}({weekday})"
pm10 = float(data['PM10'])
if pm10 <= 30:
dust_status = "์ข‹์Œ"
dust_color = "#87CEEB" # Blue
elif pm10 <= 80:
dust_status = "๋ณดํ†ต"
dust_color = "#90EE90" # Green
elif pm10 <= 150:
dust_status = "๋‚˜์จ"
dust_color = "#FFD700" # Yellow
else:
dust_status = "๋งค์šฐ๋‚˜์จ"
dust_color = "#FF6B6B" # Red
temp = data.get('TEMP', "์ •๋ณด์—†์Œ")
precip_type = data.get('PRECPT_TYPE', "์ •๋ณด์—†์Œ")
try:
temp = f"{float(temp):.1f}ยฐC"
except:
temp = "์ •๋ณด์—†์Œ"
# ๋‚ด์ผ ์•„์นจ 6์‹œ ์˜ˆ๋ณด ์ฐพ๊ธฐ
tomorrow_morning_weather = "์—†์Œ"
forecast_data = data['FCST24HOURS']['FCST24HOURS']
if not isinstance(forecast_data, list):
forecast_data = [forecast_data]
# ํ˜„์žฌ ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ ๋‚ด์ผ ๋‚ ์งœ ๊ณ„์‚ฐ
current_time = datetime.utcnow() + timedelta(hours=9)
tomorrow = current_time + timedelta(days=1)
tomorrow_date = tomorrow.strftime('%Y%m%d')
for forecast in forecast_data:
# ๋‚ ์งœ์™€ ์‹œ๊ฐ„์ด ๋ชจ๋‘ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
if forecast['FCST_DT'][:8] == tomorrow_date and forecast['FCST_DT'][8:10] == '06':
tomorrow_temp = forecast['TEMP']
precip_type = forecast['PRECPT_TYPE']
# ๊ฐ•์ˆ˜ ํƒ€์ž…์— ๋”ฐ๋ฅธ ํ‘œ์‹œ
precip_mark = ""
if precip_type == "๋น„":
precip_mark = "(๋น„)"
elif precip_type == "๋ˆˆ":
precip_mark = "(๋ˆˆ)"
elif precip_type == "๋น„/๋ˆˆ":
precip_mark = "(๋น„/๋ˆˆ)"
tomorrow_morning_weather = f"{tomorrow_temp}ยฐC{precip_mark}"
break
# ํ™”๋ฉด์— ํ‘œ์‹œ
st.markdown(f'''
<div class="time-container">
<div style="text-align: center; margin-bottom: 0.5rem; font-size: 10em; font-weight: bold; color: black;">
{temp} &nbsp;&nbsp;&nbsp;/&nbsp;&nbsp;&nbsp; {tomorrow_morning_weather}
</div>
<span class="date-text">{formatted_date}</span>
</div>
''', unsafe_allow_html=True)
clock_html = """
<div style="width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 20px;">
<div style="text-align: center; height: 300px; display: flex; align-items: center; justify-content: center;">
<span id="clock" style="font-size: 15em; font-weight: bold; color: black; line-height: 1.2; white-space: nowrap;"></span>
</div>
</div>
<script>
function updateClock() {
const now = new Date();
const options = {
timeZone: 'Asia/Seoul',
hour12: true,
hour: 'numeric',
minute: '2-digit'
};
document.getElementById('clock').textContent = now.toLocaleTimeString('ko-KR', options);
}
setInterval(updateClock, 1000);
updateClock();
</script>
"""
components.html(clock_html, height=300)
st.button("์‹œ๊ฐ„๋Œ€๋ณ„ ์˜จ๋„ ๋ณด๊ธฐ", on_click=lambda: st.session_state.update({'current_section': 'temperature'}))
st.markdown('</div>', unsafe_allow_html=True)
def show_temperature_graph(data):
st.markdown('<div class="section-container">', unsafe_allow_html=True)
st.markdown('<h1 style="text-align: center; margin-bottom: 1rem;">์‹œ๊ฐ„๋Œ€๋ณ„ ์˜จ๋„</h1>', unsafe_allow_html=True)
forecast_data = data['FCST24HOURS']['FCST24HOURS']
if not isinstance(forecast_data, list):
forecast_data = [forecast_data]
# Sort forecast data by FCST_DT to ensure correct time ordering
forecast_data = sorted(forecast_data, key=lambda x: x['FCST_DT'])
# ํ˜„์žฌ ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ ์œ ํšจํ•œ ์˜ˆ๋ณด ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋ง
current_time = datetime.utcnow() + timedelta(hours=9)
current_time_str = current_time.strftime('%Y%m%d%H%M')
# ํ˜„์žฌ ์‹œ๊ฐ„ ์ดํ›„์˜ ์˜ˆ๋ณด ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋ง
valid_forecast_data = [fcst for fcst in forecast_data if fcst['FCST_DT'] >= current_time_str]
# ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ์ „์ฒด ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
if not valid_forecast_data:
valid_forecast_data = forecast_data
times = []
temps = []
weather_icons = []
weather_descriptions = []
date_changes = []
# Find the index closest to current time
time_differences = []
for fcst in valid_forecast_data:
forecast_time = datetime.strptime(fcst['FCST_DT'], '%Y%m%d%H%M')
time_diff = abs((forecast_time - current_time).total_seconds())
time_differences.append(time_diff)
current_index = time_differences.index(min(time_differences))
# Reorder forecast data to start from current time
valid_forecast_data = valid_forecast_data[current_index:] + valid_forecast_data[:current_index]
for i, forecast in enumerate(valid_forecast_data):
time_str = forecast['FCST_DT']
date = time_str[6:8]
hour = time_str[8:10]
if i > 0 and valid_forecast_data[i-1]['FCST_DT'][6:8] != date:
date_changes.append(i)
times.append(f"{hour}์‹œ")
temps.append(float(forecast['TEMP']))
sky_status = forecast['SKY_STTS']
precip_type = forecast['PRECPT_TYPE']
if precip_type == "๋น„":
icon = "๐ŸŒง๏ธ"
description = "๋น„"
elif precip_type == "๋ˆˆ":
icon = "๐ŸŒจ๏ธ"
description = "๋ˆˆ"
elif precip_type == "๋น„/๋ˆˆ":
icon = "๐ŸŒจ๏ธ๐ŸŒง๏ธ"
description = "๋น„/๋ˆˆ"
elif sky_status == "๋ง‘์Œ":
icon = "โ˜€๏ธ"
description = "๋ง‘์Œ"
elif sky_status == "๊ตฌ๋ฆ„๋งŽ์Œ":
icon = "โ›…"
description = "๊ตฌ๋ฆ„<br>๋งŽ์Œ"
elif sky_status == "ํ๋ฆผ":
icon = "โ˜๏ธ"
description = "ํ๋ฆผ"
else:
icon = "โ˜€๏ธ"
description = "์ •๋ณด์—†์Œ"
weather_icons.append(icon)
weather_descriptions.append(description)
df = pd.DataFrame({
'์‹œ๊ฐ„': times,
'๊ธฐ์˜จ': temps,
'๋‚ ์”จ': weather_icons,
'์„ค๋ช…': weather_descriptions,
'FCST_DT': [f['FCST_DT'] for f in valid_forecast_data]
})
fig = px.line(df, x='์‹œ๊ฐ„', y='๊ธฐ์˜จ', markers=True)
# Add nighttime overlay (18:00-06:00)
for i in range(len(times)):
hour = int(times[i].replace('์‹œ', ''))
if hour >= 18 or hour < 6:
fig.add_vrect(
x0=times[i],
x1=times[i+1] if i < len(times)-1 else times[-1],
fillcolor='rgba(0, 0, 0, 0.1)',
layer='below',
line_width=0,
annotation_text="",
annotation_position="top left"
)
# ํ˜„์žฌ ์‹œ๊ฐ๊ณผ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์˜ˆ๋ณด ์‹œ๊ฐ„ ์ฐพ๊ธฐ
current_time = datetime.utcnow() + timedelta(hours=9)
# ๋…น์ƒ‰ ์„ธ๋กœ์„  ์ถ”๊ฐ€ ๋ฐ "ํ˜„์žฌ" ํ…์ŠคํŠธ ํ‘œ์‹œ - ์ด์ œ ํ•ญ์ƒ ์ฒซ ๋ฒˆ์งธ ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ์— ํ‘œ์‹œ
fig.add_vline(x=times[0], line_width=2, line_dash="dash", line_color="green")
fig.add_annotation(
x=times[0],
y=max(temps) + 4,
text="<b>ํ˜„์žฌ</b>",
showarrow=True,
arrowhead=2,
)
bold_times = ["00์‹œ", "06์‹œ", "12์‹œ", "18์‹œ", "24์‹œ"]
for time in bold_times:
if time in times:
index = times.index(time)
fig.add_annotation(
x=time,
y=min(temps),
text=time,
showarrow=False,
font=dict(size=30, color="black", family="Arial")
)
fig.add_vline(x='12์‹œ', line_width=2, line_dash="dash", line_color="rgba(0,0,0,0.5)")
# ์˜ค๋Š˜๊ณผ ๋‚ด์ผ, ์˜ค์ „๊ณผ ์˜คํ›„ ํ…์ŠคํŠธ๋Š” ํ•ด๋‹น ์‹œ๊ฐ„๋Œ€์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ํ‘œ์‹œ
time_set = set(times)
if '11์‹œ' in time_set:
fig.add_annotation(x='11์‹œ', y=max(temps) + 4, text="์˜ค์ „", showarrow=False, font=dict(size=24))
if '13์‹œ' in time_set:
fig.add_annotation(x='13์‹œ', y=max(temps) + 4, text="์˜คํ›„", showarrow=False, font=dict(size=24))
if '23์‹œ' in time_set:
fig.add_annotation(x='23์‹œ', y=max(temps) + 4, text="์˜ค๋Š˜", showarrow=False, font=dict(size=24))
if '01์‹œ' in time_set:
fig.add_annotation(x='01์‹œ', y=max(temps) + 4, text="๋‚ด์ผ", showarrow=False, font=dict(size=24))
fig.update_traces(
line_color='#FF6B6B',
marker=dict(size=10, color='#FF6B6B'),
textposition="top center",
mode='lines+markers+text',
text=[f"<b>{int(round(temp))}ยฐ</b>" for temp in df['๊ธฐ์˜จ']],
textfont=dict(size=24)
)
for i, (icon, description) in enumerate(zip(weather_icons, weather_descriptions)):
fig.add_annotation(
x=times[i],
y=max(temps) + 3,
text=f"{icon}",
showarrow=False,
font=dict(size=30)
)
fig.add_annotation(
x=times[i],
y=max(temps) + 2,
text=f"{description}",
showarrow=False,
font=dict(size=16),
textangle=0
)
for date_change in date_changes:
fig.add_vline(
x=times[date_change],
line_width=2,
line_dash="dash",
line_color="rgba(255, 0, 0, 0.7)"
)
fig.update_layout(
title=None,
xaxis_title='',
yaxis_title='๊ธฐ์˜จ (ยฐC)',
height=600,
width=7200,
showlegend=False,
plot_bgcolor='rgba(255,255,255,0.9)',
paper_bgcolor='rgba(0,0,0,0)',
margin=dict(l=50, r=50, t=0, b=0),
xaxis=dict(
tickangle=0,
tickfont=dict(size=14),
gridcolor='rgba(0,0,0,0.1)',
dtick=1,
tickmode='array',
ticktext=[f"{i:02d}์‹œ" for i in range(24)],
tickvals=[f"{i:02d}์‹œ" for i in range(24)]
),
yaxis=dict(
tickfont=dict(size=14),
gridcolor='rgba(0,0,0,0.1)'
)
)
st.plotly_chart(fig, use_container_width=True)
# ๋‚ ์”จ ์˜ˆ๋ณด ์ƒ์„ฑ ๋ฐ ํ‘œ์‹œ ๋ถ€๋ถ„์„ ์„ธ์…˜ ์ƒํƒœ๋กœ ๊ด€๋ฆฌ
if 'weather_forecast' not in st.session_state:
client = OpenAI(
api_key="glhf_9ea0e0babe1e45353dd03b44cb979e22",
base_url="https://glhf.chat/api/openai/v1"
)
forecast_data_str = "\n".join([f"{time}: {temp}๋„, {description}" for time, temp, description in zip(times, temps, weather_descriptions)])
response = client.chat.completions.create(
model="hf:Nexusflow/Athene-V2-Chat",
messages=[
{"role": "system", "content": "๋‹น์‹ ์€ ๋‚ ์”จ ์˜ˆ๋ณด๊ด€์ž…๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ์‹œ๊ฐ„๋Œ€๋ณ„ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ •ํ™•ํ•œ ๋‚ ์”จ ์˜ˆ๋ณด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."},
{"role": "user", "content": f"๋‹ค์Œ ์‹œ๊ฐ„๋Œ€๋ณ„ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ  ์‹ค์ œ ๋‚ ์”จ ์ƒํ™ฉ์— ๋งž๋Š” ์ •ํ™•ํ•œ ๋‚ ์”จ ์˜ˆ๋ณด๋ฅผ 200์ž๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”. ๋น„๋‚˜ ๋ˆˆ ์˜ˆ๋ณด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์šฐ์‚ฐ์„ ์ค€๋น„ํ•˜๋„๋ก ์•ˆ๋‚ดํ•ด์ฃผ์„ธ์š”. ์˜ท์ฐจ๋ฆผ์€ ๋‹ค์Œ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”. \n27ยฐC์ด์ƒ:๋ฐ˜ํŒ”ํ‹ฐ, ๋ฐ˜๋ฐ”์ง€, ๋ฏผ์†Œ๋งค.\n23ยฐC~26ยฐC: ์–‡์€ ์…”์ธ , ๋ฐ˜ํŒ”ํ‹ฐ, ๋ฐ˜๋ฐ”์ง€, ๋ฉด๋ฐ”์ง€.\n20ยฐC~22ยฐC: ์–‡์€ ๊ฐ€๋””๊ฑด, ๊ธดํŒ”ํ‹ฐ, ๊ธด๋ฐ”์ง€.\n17ยฐC~19ยฐC: ์–‡์€ ๋‹ˆํŠธ, ๊ฐ€๋””๊ฑด, ๋งจํˆฌ๋งจ, ์–‡์€ ์ž์ผ“, ๊ธด๋ฐ”์ง€.\n12ยฐC~16ยฐC: ์ž์ผ“, ๊ฐ€๋””๊ฑด, ์•ผ์ƒ, ๋งจํˆฌ๋งจ, ๋‹ˆํŠธ, ์Šคํƒ€ํ‚น, ๊ธด๋ฐ”์ง€\n9ยฐC~11ยฐC: ํŠธ๋ Œ์น˜์ฝ”ํŠธ, ์•ผ์ƒ, ๊ฐ€์ฃฝ ์ž์ผ“, ์Šคํƒ€ํ‚น, ๊ธด๋ฐ”์ง€\n5ยฐC~8ยฐC: ์ฝ”ํŠธ, ํžˆํŠธํ…, ๋‹ˆํŠธ, ๊ธด๋ฐ”์ง€\n4ยฐC์ดํ•˜: ํŒจ๋”ฉ, ๋‘๊บผ์šด ์ฝ”ํŠธ, ๋ชฉ๋„๋ฆฌ, ๊ธฐ๋ชจ์ œํ’ˆ: {forecast_data_str}"}
]
)
st.session_state.weather_forecast = response.choices[0].message.content
# ์ €์žฅ๋œ ๋‚ ์”จ ์˜ˆ๋ณด ํ‘œ์‹œ
st.markdown(f'''
<div class="scroll-container">
<div class="scroll-text">{st.session_state.weather_forecast}</div>
</div>
''', unsafe_allow_html=True)
# ์šฐ๋ฆฌ์ง‘ ๋‚ ์”จ ์ •๋ณด๋กœ ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ ์ถ”๊ฐ€
st.button("์šฐ๋ฆฌ์ง‘ ๋‚ ์”จ ์ •๋ณด๋กœ ๋Œ์•„๊ฐ€๊ธฐ", on_click=lambda: st.session_state.update({'current_section': 'weather'}))
st.markdown('</div>', unsafe_allow_html=True)
def main():
if 'current_section' not in st.session_state:
st.session_state.current_section = 'weather'
st.session_state.last_api_call = 0
st.session_state.weather_data = None
# ํ˜„์žฌ ์‹œ๊ฐ„์„ ์„œ์šธ ์‹œ๊ฐ„์œผ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
current_time = datetime.utcnow() + timedelta(hours=9)
current_timestamp = current_time.timestamp()
# ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ ์ฒดํฌ
if 'last_api_call' not in st.session_state:
st.session_state.last_api_call = 0
time_since_last_call = current_timestamp - st.session_state.last_api_call
# ์ž๋™ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ placeholder
refresh_placeholder = st.empty()
# ๋ฐ์ดํ„ฐ ๊ฐฑ์‹ ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
if not st.session_state.weather_data or time_since_last_call >= 300:
try:
new_data = get_weather_data()
if new_data: # ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋ฐ›์•„์™”์„ ๋•Œ๋งŒ ์—…๋ฐ์ดํŠธ
st.session_state.weather_data = new_data
st.session_state.last_api_call = current_timestamp
st.rerun() # ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ
except Exception as e:
st.error(f"Failed to refresh data: {str(e)}")
data = st.session_state.weather_data
if data:
pm10_value = data['PM10']
background_color = get_background_color(pm10_value)
st.markdown(f"""
<style>
.stApp {{
background-color: {background_color};
}}
</style>
""", unsafe_allow_html=True)
if st.session_state.current_section == 'weather':
show_weather_info(data)
else:
show_temperature_graph(data)
# ์ž๋™ ์ƒˆ๋กœ๊ณ ์นจ์„ ์œ„ํ•œ ํƒ€์ด๋จธ
with refresh_placeholder:
if time_since_last_call < 300:
remaining_time = 300 - time_since_last_call
time.sleep(min(remaining_time, 1)) # ์ตœ๋Œ€ 1์ดˆ์”ฉ ๋Œ€๊ธฐ
st.rerun()
if __name__ == "__main__":
main()