Add app.py

This commit is contained in:
2026-06-10 20:11:59 +00:00
parent 7f4d435337
commit b9a8e2591c

96
app.py Normal file
View File

@@ -0,0 +1,96 @@
from flask import Flask, jsonify
from flask_cors import CORS
from scraper import scrape_bokadirekt_appointments
import datetime
import threading
import os
app = Flask(__name__)
CORS(app) # Enable CORS for all origins; adjust for production
# --- Configuration ---
# Salon URLs to scrape from Bokadirekt. These should be actual salon profile pages.
# Replace with real URLs as identified from the frontend 'faq' section.
SALON_URLS = [
"https://www.bokadirekt.se/places/klipphuset-41113", # Add other Bokadirekt salon URLs in Kungsbacka here:
# e.g., "https://www.bokadirekt.se/places/klippa-kungsbacka-XXXXX", # "https://www.bokadirekt.se/places/studio-siss-XXXXX", # "https://www.bokadirekt.se/places/det-hander-XXXXX", # "https://www.bokadirekt.se/places/by-u.s.-XXXXX"]
# Cache for scraped data
cached_appointments = []
last_scraped_time = None
SCRAPE_INTERVAL_MINUTES = 30 # As mentioned in the frontend description
# --- Scraper Functions ---
# This function runs in a background thread to update the cache periodically.
def update_cached_appointments():
global cached_appointments, last_scraped_time
print(f"[{datetime.datetime.now()}] Starting periodic scrape...")
try:
scraped_data = scrape_bokadirekt_appointments(SALON_URLS)
if scraped_data:
cached_appointments = scraped_data
last_scraped_time = datetime.datetime.now()
print(f"[{datetime.datetime.now()}] Scrape successful. {len(cached_appointments)} appointments cached.")
else:
print(f"[{datetime.datetime.now()}] Scraper returned no data. Keeping previous cache if any.")
except Exception as e:
print(f"[{datetime.datetime.now()}] Error during periodic scrape: {e}")
# Schedule the next scrape
threading.Timer(SCRAPE_INTERVAL_MINUTES * 60, update_cached_appointments).start()
# Synchronous update for initial load or immediate refresh if needed.
def update_cached_appointments_sync():
global cached_appointments, last_scraped_time
print(f"[{datetime.datetime.now()}] Performing synchronous scrape...")
try:
scraped_data = scrape_bokadirekt_appointments(SALON_URLS)
if scraped_data:
cached_appointments = scraped_data
last_scraped_time = datetime.datetime.now()
print(f"[{datetime.datetime.now()}] Synchronous scrape successful. {len(cached_appointments)} appointments cached.")
else:
print(f"[{datetime.datetime.now()}] Synchronous scraper returned no data.")
except Exception as e:
print(f"[{datetime.datetime.now()}] Error during synchronous scrape: {e}")
# --- API Endpoints ---
@app.route('/api/appointments', methods=['GET'])
def get_appointments():
"""
Returns the latest available appointments from cache.
The cache is updated periodically by a background thread.
"""
# In a production environment, you might want to handle cache staleness
# differently, e.g., serve stale data while a fresh scrape runs.
# For this example, we rely on the background thread to keep it updated.
return jsonify({
"data": cached_appointments,
"last_updated": last_scraped_time.isoformat() if last_scraped_time else "Never", "message": "Appointments data from Klipptider backend."
})
@app.route('/', methods=['GET'])
def health_check():
return "Klipptider Backend is running!"
# --- Main execution ---
if __name__ == '__main__':
# Perform an initial scrape to populate the cache immediately on startup.
print("Performing initial scrape...")
update_cached_appointments_sync()
print(f"Initial scrape complete. Cache size: {len(cached_appointments)}")
# Start the periodic scraper in a background thread if there are URLs to scrape.
# Note: For production, consider using a dedicated task queue (e.g., Celery) or
# a cron job for robust background task management, instead of Flask's built-in threading.
if SALON_URLS:
print(f"Starting background scraper to update every {SCRAPE_INTERVAL_MINUTES} minutes.")
# Start the timer, it will call update_cached_appointments and then reschedule itself.
threading.Timer(SCRAPE_INTERVAL_MINUTES * 60, update_cached_appointments).start()
else:
print("No salon URLs configured, background scraper not started.")
# Run the Flask app.
# Set debug=False for production deployments, as debug=True can interfere with threading.Timer.
app.run(host='0.0.0.0', port=5000, debug=False)