192 lines
6.5 KiB
Python
192 lines
6.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
from __future__ import annotations
|
||
|
||
__title__ = "sunholiday"
|
||
__author__ = 'Mehmet Karatay & "Saraswati" (ChatGPT)'
|
||
__purpose__ = "Astral ve eholidays_tr kullanarak güneş / tatil / mevsim bilgisi üretir"
|
||
__version__ = "0.1.1"
|
||
__date__ = "2025-11-22"
|
||
|
||
"""
|
||
ebuild/core/sunholiday.py
|
||
|
||
Revision : 2025-11-22
|
||
Authors : Mehmet Karatay & "Saraswati" (ChatGPT)
|
||
|
||
Amaç
|
||
-----
|
||
Eski sistemde kullandığın SunHolidayInfo sınıfını, ebuild projesi
|
||
için bağımsız bir modüle taşıdık. Bu modül:
|
||
|
||
- Astral ile gün doğumu / gün batımı / öğlen
|
||
- eholidays_tr ile resmi tatil (yoksa graceful fallback)
|
||
- Basit mevsim hesabı
|
||
|
||
bilgilerini üretir ve SeasonController tarafından kullanılır.
|
||
|
||
Dış arayüz:
|
||
class SunHolidayInfo:
|
||
- get_info() -> dict
|
||
- print_info()
|
||
- to_json()
|
||
"""
|
||
|
||
import json
|
||
from datetime import datetime
|
||
from enum import Enum
|
||
|
||
from astral import LocationInfo
|
||
from astral.sun import sun
|
||
import pytz
|
||
|
||
# eholidays_tr yoksa sistem göçmesin → dummy tracker
|
||
try:
|
||
from eholidays_tr import HolidayTracker # type: ignore
|
||
except Exception:
|
||
class HolidayTracker: # type: ignore
|
||
def __init__(self, year: int) -> None:
|
||
self.year = year
|
||
def is_holiday(self, dt) -> None:
|
||
return None
|
||
|
||
|
||
class Season(Enum):
|
||
KIS = "Kış"
|
||
ILKBAHAR = "İlkbahar"
|
||
YAZ = "Yaz"
|
||
SONBAHAR = "Sonbahar"
|
||
|
||
|
||
class SunHolidayInfo:
|
||
"""
|
||
Astral + eholidays_tr kombinasyonu ile gün / tatil / mevsim bilgisi üretir.
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
current_datetime=None,
|
||
city_name: str = "Ankara",
|
||
country: str = "Turkey",
|
||
timezone: str = "Europe/Istanbul",
|
||
latitude: float = 39.92077,
|
||
longitude: float = 32.85411,
|
||
) -> None:
|
||
self.city = city_name
|
||
self.country = country
|
||
self.timezone_str = timezone
|
||
self.timezone = pytz.timezone(timezone)
|
||
self.lat = latitude
|
||
self.lon = longitude
|
||
|
||
if current_datetime is None:
|
||
self.current_datetime = datetime.now(self.timezone)
|
||
else:
|
||
# Eğer naive datetime geldiyse timezone ekleyelim
|
||
if current_datetime.tzinfo is None:
|
||
self.current_datetime = self.timezone.localize(current_datetime)
|
||
else:
|
||
self.current_datetime = current_datetime.astimezone(self.timezone)
|
||
|
||
self.location = LocationInfo(
|
||
self.city, self.country, self.timezone_str, self.lat, self.lon
|
||
)
|
||
self.work_date = self.current_datetime
|
||
|
||
# ---------------------------------------------------------
|
||
# Yardımcılar
|
||
# ---------------------------------------------------------
|
||
def return_init(self) -> str:
|
||
return (
|
||
f"SunHolidayInfo: {self.country} {self.city} "
|
||
f"{self.timezone_str} {self.lat} - {self.lon}"
|
||
)
|
||
|
||
def get_season(self, date_obj: datetime):
|
||
"""
|
||
Astronomik mevsim aralıklarıyla kaba mevsim ve gün bilgisi üretir.
|
||
"""
|
||
ranges = {
|
||
Season.KIS: [
|
||
(date_obj.replace(month=1, day=1), date_obj.replace(month=3, day=20)),
|
||
(date_obj.replace(month=12, day=21), date_obj.replace(month=12, day=31)),
|
||
],
|
||
Season.ILKBAHAR: [
|
||
(date_obj.replace(month=3, day=21), date_obj.replace(month=6, day=20))
|
||
],
|
||
Season.YAZ: [
|
||
(date_obj.replace(month=6, day=21), date_obj.replace(month=9, day=22))
|
||
],
|
||
Season.SONBAHAR: [
|
||
(date_obj.replace(month=9, day=23), date_obj.replace(month=12, day=20))
|
||
],
|
||
}
|
||
|
||
for season, periods in ranges.items():
|
||
for start, end in periods:
|
||
if start <= date_obj <= end:
|
||
total_days = (end - start).days + 1
|
||
passed_days = (date_obj - start).days
|
||
remaining_days = (end - date_obj).days
|
||
return {
|
||
"season": season.value,
|
||
"season_start": start.isoformat(),
|
||
"season_end": end.isoformat(),
|
||
"season_day": total_days,
|
||
"season_passed": passed_days,
|
||
"season_remaining": remaining_days,
|
||
}
|
||
|
||
return None
|
||
|
||
# ---------------------------------------------------------
|
||
# Ana API
|
||
# ---------------------------------------------------------
|
||
def get_info(self) -> dict:
|
||
"""
|
||
Astral + HolidayTracker + mevsim hesabını bir dict olarak döner.
|
||
"""
|
||
sun_data = sun(
|
||
self.location.observer,
|
||
date=self.work_date,
|
||
tzinfo=self.timezone,
|
||
)
|
||
|
||
tracker = HolidayTracker(self.work_date.year)
|
||
holiday_label = tracker.is_holiday(self.work_date)
|
||
|
||
season_info = self.get_season(self.work_date)
|
||
|
||
return {
|
||
"date": self.work_date.isoformat(),
|
||
"sunrise": sun_data["sunrise"].strftime("%H:%M:%S"),
|
||
"sunset": sun_data["sunset"].strftime("%H:%M:%S"),
|
||
"noon": sun_data["noon"].strftime("%H:%M:%S"),
|
||
"is_holiday": bool(holiday_label),
|
||
"holiday_label": holiday_label if holiday_label else "Yok",
|
||
"season": season_info["season"] if season_info else None,
|
||
"season_start": season_info["season_start"] if season_info else None,
|
||
"season_end": season_info["season_end"] if season_info else None,
|
||
"season_day": season_info["season_day"] if season_info else None,
|
||
"season_passed": season_info["season_passed"] if season_info else None,
|
||
"season_remaining": season_info["season_remaining"] if season_info else None,
|
||
}
|
||
|
||
def print_info(self) -> None:
|
||
info = self.get_info()
|
||
print(f"Güneş bilgileri - {self.location.name}, {info['date']}")
|
||
print(f"Doğuş: {info['sunrise']}")
|
||
print(f"Batış: {info['sunset']}")
|
||
print(f"Öğlen: {info['noon']}")
|
||
print(f"Tatil mi?: {'Evet' if info['is_holiday'] else 'Hayır'}")
|
||
if info["is_holiday"]:
|
||
print(f"Etiket: {info['holiday_label']}")
|
||
|
||
def to_json(self) -> str:
|
||
return json.dumps(self.get_info(), ensure_ascii=False, indent=2)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
s = SunHolidayInfo()
|
||
print(s.return_init())
|
||
print(s.to_json())
|