Creative Code

Lunar-(13)날씨(실시간 재난문자, 전세계 지역 현재 날씨 검색, 기상관련 사이트 링크) 본문

Projects

Lunar-(13)날씨(실시간 재난문자, 전세계 지역 현재 날씨 검색, 기상관련 사이트 링크)

빛하루 2024. 1. 8. 22:21

pages/weather.py

 

화면 왼쪽에는 실시간 재난 문자 정보를 받아오는 기능, 

화면 중앙에는 전세계의 지역명을 검색하면 해당 지역의 현재 날씨 정보를 받아와 아이콘으로 표시하는 기능,

화면 오른쪽에는 각종 기상관련 사이트로 이동할 수 있는 링크를 삽입해두었다.

 

※pages/weather.py 코드

# lunar.state.home 모듈에서 필요한 State 및 HomeState를 가져옵니다.
import reflex as rx
from lunar.state.base import State
from lunar.state.home import HomeState
from PIL import Image
# 컴포넌트를 가져옵니다.
from ..components import container

color = "rgb(107,99,246)"
# 탭 버튼을 생성하는 함수
def tab_button(imagepath, href):
    """A tab switcher button."""
    return rx.link(
        rx.image(
            src=imagepath,
            height='40px',
            width='40px',
        ),
        display="inline-flex",
        align_items="center",
        py=3,
        px=2,
        href=href,  # 버튼 클릭 시 이동할 경로
    )

def message(message):
    return rx.box(
        rx.button(
            rx.vstack(
                rx.box(
                    rx.text(
                        message['date'],
                        style={'white-space': 'normal'},
                        font_Size = '15px',
                        font_weight= 'bolder',
                    ),
                    bg = '#dec445',
                ),
                rx.text(),
                rx.text(
                    message['area'],
                    style={'white-space': 'normal'},
                    font_Size = '13px',
                    font_weight='bold',
                ),
                rx.divider(variant="dashed", border_color="black"),
                rx.text(
                    message['text'],
                    style={'white-space': 'normal'},
                    font_size = '15px',
                ),
                width = '100%',
            ),
            height='auto',
            width='90%',
        ),
        rx.container(height='5px'),
        align='start',
        padding='5px',  # 테두리와 내용 사이의 여백 지정
    )

# 왼쪽에 표시되는 탭 스위처
def tabs():
    """The tab switcher displayed on the left."""
    return rx.box(
        rx.vstack(
            rx.container(
                rx.hstack(
                    rx.image(
                        src='/moon.png',
                        height='60px',
                        width='60px',         
                    ),
                    rx.text(
                        "Lunar", 
                        style={
                            "fontSize": "40px",
                            "fontWeight": "bolder",
                            "fontFamily": "Calibri, Calibri",
                            "background": "-webkit-linear-gradient(-45deg, #e04a3f, #4e8be6)",
                            "-webkit-background-clip": "text",
                            "color": "transparent",
                        },
                        center_content=True,
                    ),  # 앱 이름
                ),
            ),
            rx.vstack(
                rx.hstack(
                    rx.container(width='5px'),
                    rx.vstack(
                        rx.container(height='10px'),
                        rx.vstack(
                            rx.button('Disaster',Font_size = '20px',on_click = HomeState.climate_message,bg = '#eb7373'),
                        ),
                        rx.container(height='5px'),
                        rx.foreach(
                            HomeState.weather_message_result,
                            message,
                        ),
                        rx.container(height='4px'),
                        align_items='start',
                        width ='100%',
                    ),
                ),
                align_items = 'start',
                margin_left = '20px',
                border = '3px solid #000000',
                border_radius = '30px',
                box_Shadow = '10px 10px 5px #d61c4e',
                width = '90%',
            ),
            align_items="start",
            gap=4,
        ),
        py=4,
        overflow='auto',
    )

# 오른쪽에 표시되는 사이드바
def sidebar(HomeState):
    """The sidebar displayed on the right."""
    return rx.vstack(
        rx.container(height='10px'),
        rx.heading(
            'Weather sites',
            font_Size = '30px',
            font_weight='bolder',
            bg = 'blue.400',
        ),
        rx.hstack(
            rx.container(width='1px'),
            rx.vstack(
                rx.link(
                    rx.button("대한민국 기상청"),
                    href="https://www.weather.go.kr/w/index.do",
                    color="rgb(107,99,246)",
                    button=True,
                ),
                rx.link(
                    rx.button("일본 기상청"),
                    href="https://www.data.jma.go.jp/multi/index.html?lang=kr",
                    color="rgb(107,99,246)",
                    button=True,
                ),
                rx.link(
                    rx.button("미국 기상청"),
                    href="https://worldweather.wmo.int/kr/country.html?countryCode=93",
                    color="rgb(107,99,246)",
                    button=True,
                ),
                rx.link(
                    rx.button("Windy"),
                    href="https://www.windy.com/?36.342,127.389,5",
                    color="rgb(107,99,246)",
                    button=True,
                ),
                rx.link(
                    rx.button("AccuWeather"),
                    href="https://www.accuweather.com/",
                    color="rgb(107,99,246)",
                    button=True,
                ),
                align_items='start',
                width ='100%',
            ),
            border = '2px solid #000000',
            border_radius = '30px',
            box_Shadow = '10px 10px 5px #307849',
            width='80%',
        ),
        align_items="start",
        gap=4,
        height='100%',
        width = '100%',
        py=4,
        overflow='auto',
    )

# 피드의 헤더
def feed_header(HomeState):
    """The header of the feed."""
    return rx.hstack(
        rx.image(src='/find1.png',height='35px',width='35px'),
        rx.input(on_change=HomeState.set_weather_search, placeholder="Search place..!"),
        rx.button('search', on_click = HomeState.climate_websearch),
        justify="space-between",
        p=4,
        border_bottom="3px solid #000000",
    )

# 피드 영역
def feed(HomeState):
    """The feed."""
    return rx.box(
        feed_header(HomeState),
        rx.container(height='10px'),
        rx.vstack(
            rx.cond(
                HomeState.weather_search_show,
                rx.vstack(
                    rx.hstack(
                        rx.heading(
                            HomeState.weather_search,
                            as_='mark',
                            font_Size = '50px',
                        ),
                        margin_left = '5%',
                    ),
                    rx.hstack(
                        rx.vstack(
                            rx.text(
                                f"현재 기온 : {HomeState.status_temperature}'C",
                                font_Size = '25px',
                                font_weight='bold',
                            ),
                            rx.text(
                                f'현재 날씨 : {HomeState.status_climate}',
                                font_Size = '25px',
                                font_weight='bolder',
                            ),
                            rx.text(
                                f"체감 온도 : {HomeState.status_feel_temperature}'C",
                                font_Size = '25px',
                                font_weight='bolder',
                            ),
                            rx.text(
                                f"최저 온도 : {HomeState.today_min_temperature}'C",
                                font_Size = '25px',
                                font_weight='bolder',
                                color = 'blue',
                            ),
                            rx.text(
                                f"최고 온도 : {HomeState.today_max_temperature}'C",
                                font_Size = '25px',
                                font_weight='bolder',
                                color = 'red',
                            ),
                            rx.text(
                                f'현재 습도 : {HomeState.status_humidity}%',
                                font_Size = '25px',
                                font_weight='bolder',
                            ),
                            rx.text(
                                f'현재 기압 : {HomeState.status_atmospheric_pressure}hpa',
                                font_Size = '25px',
                                font_weight='bolder',
                            ),
                            rx.text(
                                f'풍속 : {HomeState.status_wind_speed}m/s',
                                font_Size = '25px',
                                font_weight='bolder',
                            ),
                            align_items='start',
                            margin_left = '5%',
                        ),
                        rx.container(width='5%'),
                        rx.vstack(
                            rx.image(
                                src = HomeState.image,
                                height='400px',
                                width = 'auto',
                            )
                        )
                    ),
                    width = '90%',
                    margin_left = '5%',
                    align_items='start',
                    border = '2px solid #000000',
                    border_radius = '30px',
                    box_Shadow = '10px 10px 10px #575050',
                ),
                rx.vstack(
                    rx.heading(
                        'Enter the area where you want to search for weather.',
                        font_Size = '40px',
                        font_weight='border',
                    ),
                    align_items='start',
                    width = '100%',
                )
            )
        ),
        rx.container(height='20px'),
        overflow='auto',
    )

# 홈 페이지
def weather():
    State.check_login
    return rx.vstack(
        rx.grid(
            tabs(),
            feed(HomeState),
            sidebar(HomeState),
            grid_template_columns="2fr 5fr 2fr",
            width='97%',
            h="90vh",
            gap=4,
        ),
        rx.grid(
            rx.vstack(
                rx.container(height='5px'),
                rx.button(
                    rx.icon(tag="moon"),
                    on_click=rx.toggle_color_mode,
                    width = '80%',
                    _hover={"bg": "orange.400"},
                ),
                width = '100%',
            ),
            rx.vstack(
                rx.hstack(
                    tab_button('/Home.png','/'),
                    tab_button('/profile.png','/profile'),
                    tab_button('/map.png','/map'),
                    tab_button('/chat.png','/chat'),
                    tab_button('/Aichat.png','/aichat'),
                    tab_button('/diary.png','/diary'),
                    tab_button('/video.png','/video'),
                    tab_button('/game.png','/game'),
                    tab_button('/music.png','/music'),
                    tab_button('/weather.png','/weather'),
                    tab_button('/setting.png','/setting'),
                    margin_right='5px',
                    border="1px solid #000000",
                    border_radius="full",
                    box_Shadow = '10px 10px 5px #3083f0',
                ),
                width = '100%',
            ),
            rx.vstack(
                rx.container(height='5px'),
                rx.button(
                    "Sign out",
                    on_click=State.logout,
                    bg="#212963",
                    color="white",
                    _hover={"bg": "blue.600"},
                    width = '80%',
                ),
                width = '100%',
            ),
            width='97%',
            grid_template_columns="2fr 5fr 2fr",
        ),
    )

 

※ state/home.py의 기상관련 함수를 담당하는 코드

 


# 재난문자 웹 크롤링
    def climate_message(self):
        url = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%EC%9E%AC%EB%82%9C%EB%AC%B8%EC%9E%90"
        response = requests.get(url)
        html = response.text
        soup = bs(html, 'html.parser')
        areas = soup.select(".area")
        disaster_texts = soup.select(".disaster_text")
        dates = soup.select(".date")
        self.weather_message_result = []
        # 결과 출력
        for area, disaster_text, date in zip(areas, disaster_texts, dates):
            data = {'area' : area.get_text(strip=True), 'date' : date.get_text(strip=True), 'text':disaster_text.get_text(strip = True) }
            self.weather_message_result.append(data)

    # 날씨 웹 크롤링
    def climate_websearch(self):
        key=''
        with open('weatherapikey.json','r')as f:                                               
            key = json.load(f)
        weather_api_key = key['key']
        url = f"http://api.openweathermap.org/data/2.5/weather?q={self.weather_search}&appid={weather_api_key}&lang=kr&units=metric"
        result = requests.get(url)
        data = json.loads(result.text)
        self.status_climate = data['weather'][0]['description']
        self.status_temperature = data['main']['temp']
        self.status_feel_temperature = data['main']['feels_like']
        self.today_min_temperature = data['main']['temp_min']
        self.today_max_temperature = data['main']['temp_max']
        self.status_humidity = data['main']['humidity']
        self.status_atmospheric_pressure = data['main']['pressure']
        self.status_wind_direction = data['wind']['deg']
        self.status_wind_speed = data['wind']['speed']
        self.status_climate_icon_url=  f"https://openweathermap.org/img/wn/{data['weather'][0]['icon']}@2x.png"
        self.image = Image.open((requests.get(self.status_climate_icon_url, stream=True).raw))
        self.weather_search_show=True