import io
import os
import re
import tempfile
import time
from datetime import datetime
from pathlib import Path
from typing import Any
import pythoncom
import requests
import win32com.client
import xlsxwriter
from bs4 import BeautifulSoup
from flask import Flask, jsonify, render_template_string, request, send_file
app = Flask(__name__)
SOURCE_PAGE = “https://www.istanbuleczaciodasi.org.tr/nobetci-eczane/”
SOURCE_API = “https://www.istanbuleczaciodasi.org.tr/nobetci-eczane/index.php”
CACHE_SECONDS = 900
cache: dict[str, tuple[float, list[dict[str, Any]]]] = {}
ILCELER = [
“Adalar”, “Arnavutköy”, “Ataşehir”, “Avcılar”, “Bağcılar”,
“Bahçelievler”, “Bakırköy”, “Başakşehir”, “Bayrampaşa”,
“Beşiktaş”, “Beykoz”, “Beylikdüzü”, “Beyoğlu”, “Büyükçekmece”,
“Çatalca”, “Çekmeköy”, “Esenler”, “Esenyurt”, “Eyüpsultan”,
“Fatih”, “Gaziosmanpaşa”, “Güngören”, “Kadıköy”, “Kağıthane”,
“Kartal”, “Küçükçekmece”, “Maltepe”, “Pendik”, “Sancaktepe”,
“Sarıyer”, “Silivri”, “Şile”, “Şişli”, “Sultanbeyli”,
“Sultangazi”, “Tuzla”, “Ümraniye”, “Üsküdar”, “Zeytinburnu”
]
AH_ISTANBUL_ILCELERI = [
“Bağcılar”, “Bahçelievler”, “Bakırköy”, “Başakşehir”,
“Bayrampaşa”, “Esenler”, “Fatih”, “Güngören”,
“Küçükçekmece”, “Zeytinburnu”
]
HEADERS = {
“User-Agent”: (
“Mozilla/5.0 (Windows NT 10.0; Win64; x64) ”
“AppleWebKit/537.36 (KHTML, like Gecko) ”
“Chrome/150.0.0.0 Safari/537.36”
),
“Accept-Language”: “tr-TR,tr;q=0.9”
}
def html_temizle(value: Any) -> str:
if value is None:
return “”
soup = BeautifulSoup(str(value), “html.parser”)
text = soup.get_text(” “, strip=True)
text = re.sub(r”^(Adres|Tarif)\s*:\s*”, “”, text, flags=re.IGNORECASE)
return re.sub(r”\s+”, ” “, text).strip()
def telefon_temizle(value: Any) -> str:
return re.sub(r”[^0-9+]”, “”, str(value or “”))
def token_al(session: requests.Session) -> str:
response = session.get(SOURCE_PAGE, headers=HEADERS, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, “html.parser”)
token_input = soup.find(“input”, {“name”: “h”})
if token_input and token_input.get(“value”):
return str(token_input[“value”])
patterns = [
r’name=[“\’]h[“\’][^>]*value=[“\’]([^”\’]+)[“\’]’,
r’value=[“\’]([^”\’]+)[“\’][^>]*name=[“\’]h[“\’]’
]
for pattern in patterns:
match = re.search(pattern, response.text)
if match:
return match.group(1)
raise RuntimeError(“Kaynak sayfadaki güvenlik değeri bulunamadı.”)
def eczaneleri_getir(ilce: str) -> list[dict[str, Any]]:
key = ilce.casefold()
now = time.time()
if key in cache:
cached_time, cached_data = cache[key]
if now – cached_time < CACHE_SECONDS:
return cached_data
session = requests.Session()
session.headers.update(HEADERS)
token = token_al(session)
response = session.post(
SOURCE_API,
data={
"jx": "1",
"islem": "get_ilce_eczane",
"ilce": ilce,
"h": token
},
headers={
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
"Referer": SOURCE_PAGE,
"Origin": "https://www.istanbuleczaciodasi.org.tr"
},
timeout=30
)
response.raise_for_status()
try:
result = response.json()
except ValueError as exc:
raise RuntimeError("Kaynak site JSON yanıtı döndürmedi.") from exc
records = result.get("eczaneler", [])
if not isinstance(records, list):
raise RuntimeError("Eczane listesi alınamadı.")
cleaned = []
for item in records:
cleaned.append({
"sicil": html_temizle(item.get("sicil", "")),
"eczane_ad": html_temizle(item.get("eczane_ad", "")),
"eczane_tel": telefon_temizle(item.get("eczane_tel", "")),
"adres": html_temizle(item.get("adres", "")),
"tarif": html_temizle(item.get("tarif", "")),
"lat": html_temizle(item.get("lat", "")),
"lng": html_temizle(item.get("lng", "")),
"nobet_bitis": html_temizle(item.get("nobet_bitis", ""))
})
cache[key] = (now, cleaned)
return cleaned
def secili_ilceleri_al_json() -> list[str]:
body = request.get_json(silent=True) or {}
selected = body.get(“ilceler”, [])
return [x for x in selected if x in ILCELER]
def secili_ilceleri_al_query() -> list[str]:
selected = request.args.getlist(“ilce”)
return [x for x in selected if x in ILCELER]
def tum_verileri_getir(ilceler: list[str]) -> list[dict[str, Any]]:
rows = []
for ilce in ilceler:
for pharmacy in eczaneleri_getir(ilce):
rows.append({“ilce”: ilce, **pharmacy})
return rows
def excel_olustur(ilceler: list[str]) -> tuple[bytes, str]:
rows = tum_verileri_getir(ilceler)
if not rows:
raise RuntimeError(“Excel’e aktarılacak kayıt bulunamadı.”)
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {“in_memory”: True})
worksheet = workbook.add_worksheet(“Nöbetçi Eczaneler”)
title_format = workbook.add_format({
“bold”: True,
“font_size”: 18,
“font_color”: “#FFFFFF”,
“bg_color”: “#087A55”,
“align”: “center”,
“valign”: “vcenter”
})
info_format = workbook.add_format({
“bold”: True,
“bg_color”: “#D1FAE5”
})
header_format = workbook.add_format({
“bold”: True,
“font_color”: “#FFFFFF”,
“bg_color”: “#0F766E”,
“border”: 1,
“align”: “center”,
“valign”: “vcenter”,
“text_wrap”: True
})
cell_format = workbook.add_format({
“border”: 1,
“valign”: “top”,
“text_wrap”: True
})
center_format = workbook.add_format({
“border”: 1,
“align”: “center”,
“valign”: “top”,
“text_wrap”: True
})
today = datetime.now().strftime(“%d.%m.%Y”)
file_date = datetime.now().strftime(“%Y-%m-%d”)
worksheet.merge_range(“A1:H1”, “İSTANBUL NÖBETÇİ ECZANELER”, title_format)
worksheet.merge_range(“A2:D2″, f”Tarih: {today}”, info_format)
worksheet.merge_range(“E2:H2”, “Seçilen ilçeler: ” + “, “.join(ilceler), info_format)
headers = [
“Sıra”, “İlçe”, “Sicil”, “Eczane Adı”,
“Telefon”, “Adres”, “Tarif”, “Nöbet Bitişi”
]
for col, name in enumerate(headers):
worksheet.write(3, col, name, header_format)
for row_index, item in enumerate(rows, start=4):
worksheet.write(row_index, 0, row_index – 3, center_format)
worksheet.write(row_index, 1, item[“ilce”], center_format)
worksheet.write_string(row_index, 2, item.get(“sicil”, “”), center_format)
worksheet.write(row_index, 3, item.get(“eczane_ad”, “”), cell_format)
worksheet.write_string(row_index, 4, item.get(“eczane_tel”, “”), center_format)
worksheet.write(row_index, 5, item.get(“adres”, “”), cell_format)
worksheet.write(row_index, 6, item.get(“tarif”, “”), cell_format)
worksheet.write(row_index, 7, item.get(“nobet_bitis”, “”) or “-“, center_format)
worksheet.set_column(“A:A”, 7)
worksheet.set_column(“B:B”, 17)
worksheet.set_column(“C:C”, 12)
worksheet.set_column(“D:D”, 28)
worksheet.set_column(“E:E”, 17)
worksheet.set_column(“F:G”, 45)
worksheet.set_column(“H:H”, 18)
worksheet.freeze_panes(4, 0)
worksheet.autofilter(3, 0, 3 + len(rows), 7)
worksheet.set_landscape()
worksheet.set_paper(9)
worksheet.fit_to_pages(1, 0)
worksheet.repeat_rows(0, 3)
workbook.close()
output.seek(0)
filename = f”istanbul_nobetci_eczaneler_{file_date}.xlsx”
return output.getvalue(), filename
def outlook_taslagi_ac(excel_bytes: bytes, filename: str) -> None:
temp_dir = Path(tempfile.gettempdir()) / “NobetciEczane”
temp_dir.mkdir(parents=True, exist_ok=True)
attachment_path = temp_dir / filename
attachment_path.write_bytes(excel_bytes)
pythoncom.CoInitialize()
try:
try:
outlook = win32com.client.GetActiveObject(“Outlook.Application”)
except Exception:
outlook = win32com.client.Dispatch(“Outlook.Application”)
mail = outlook.CreateItem(0)
mail.To = “”
mail.CC = “”
mail.Subject = (
“İstanbul Nöbetçi Eczaneler – ”
+ datetime.now().strftime(“%d.%m.%Y”)
)
mail.Body = (
“Merhaba,\r\n\r\n”
“Güncel nöbetçi eczane listesi ekte yer almaktadır.\r\n\r\n”
“İyi çalışmalar.”
)
mail.Attachments.Add(str(attachment_path.resolve()))
mail.Display(False)
finally:
pythoncom.CoUninitialize()
@app.get(“/api/eczaneler”)
def api_eczaneler():
ilceler = secili_ilceleri_al_query()
if not ilceler:
ilceler = [“Ataşehir”]
results = []
for ilce in ilceler:
pharmacies = eczaneleri_getir(ilce)
results.append({
“ilce”: ilce,
“adet”: len(pharmacies),
“eczaneler”: pharmacies
})
return jsonify({
“success”: True,
“tarih”: datetime.now().strftime(“%d.%m.%Y”),
“toplam_adet”: sum(x[“adet”] for x in results),
“sonuclar”: results
})
@app.get(“/excel”)
def excel_indir():
ilceler = secili_ilceleri_al_query()
if not ilceler:
return jsonify({
“success”: False,
“message”: “En az bir ilçe seçmelisiniz.”
}), 400
excel_bytes, filename = excel_olustur(ilceler)
return send_file(
io.BytesIO(excel_bytes),
as_attachment=True,
download_name=filename,
mimetype=(
“application/vnd.openxmlformats-officedocument.”
“spreadsheetml.sheet”
)
)
@app.post(“/outlook-hazirla”)
def outlook_hazirla():
ilceler = secili_ilceleri_al_json()
if not ilceler:
return jsonify({
“success”: False,
“message”: “En az bir ilçe seçmelisiniz.”
}), 400
try:
excel_bytes, filename = excel_olustur(ilceler)
outlook_taslagi_ac(excel_bytes, filename)
return jsonify({
“success”: True,
“message”: (
“Outlook taslağı açıldı. ”
“Alıcıları ekleyip Gönder’e basabilirsiniz.”
)
})
except Exception as exc:
return jsonify({
“success”: False,
“message”: (
“Outlook açılamadı. Klasik Outlook kurulu ve açık olmalı. ”
f”Detay: {exc}”
)
}), 500
PAGE = r”””
İstanbul Nöbetçi Eczaneler
İstanbul Nöbetçi Eczaneler
“””
@app.get(“/”)
def home():
return render_template_string(PAGE, ilceler=ILCELER)
if __name__ == “__main__”:
app.run(host=”0.0.0.0″, port=5000, debug=False)