#!/usr/bin/env python3 import requests import json import hashlib from datetime import datetime from bs4 import BeautifulSoup # Importiere declarative_base aus sqlalchemy.orm (SQLAlchemy 2.0-konform) from sqlalchemy.orm import declarative_base, relationship, sessionmaker from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, ForeignKey, Text # Basis-Klasse für SQLAlchemy-Modelle Base = declarative_base() # Tabelle für das Bundle (statische Identifikation) class Bundle(Base): __tablename__ = 'bundles' id = Column(Integer, primary_key=True) machine_name = Column(String, unique=True) # z. B. "linuxfrombeginnertoprofessionaloreilly_bookbundle" human_name = Column(String) current_version_id = Column(Integer, ForeignKey('bundle_versions.id')) # Beziehung zur aktuellen Version current_version = relationship("BundleVersion", uselist=False, foreign_keys=[current_version_id]) # Alle Versionen (historisch) versions = relationship("BundleVersion", back_populates="bundle", foreign_keys='BundleVersion.bundle_id') # Verkaufshistorie sales_history = relationship("BundleSalesHistory", back_populates="bundle") # Tabelle für Versionen eines Bundles class BundleVersion(Base): __tablename__ = 'bundle_versions' id = Column(Integer, primary_key=True) bundle_id = Column(Integer, ForeignKey('bundles.id')) version_hash = Column(String) # SHA-256 Hash der relevanten Daten version_data = Column(Text) # Alle relevanten Bundle-Daten als JSON-String timestamp = Column(DateTime, default=datetime.utcnow) bundle = relationship("Bundle", back_populates="versions") # Tabelle für Verkaufshistorie (zur zeitlichen Analyse der Verkaufszahlen) class BundleSalesHistory(Base): __tablename__ = 'bundle_sales_history' id = Column(Integer, primary_key=True) bundle_id = Column(Integer, ForeignKey('bundles.id')) bundles_sold = Column(Float) timestamp = Column(DateTime, default=datetime.utcnow) bundle = relationship("Bundle", back_populates="sales_history") def calculate_hash(data: dict) -> str: """Berechnet einen SHA-256 Hash aus dem sortierten JSON-String der relevanten Daten.""" json_string = json.dumps(data, sort_keys=True, ensure_ascii=False) return hashlib.sha256(json_string.encode('utf-8')).hexdigest() def fetch_bundle_data(url: str) -> dict: """Lädt die Detailseite eines Bundles und extrahiert den JSON-Inhalt aus dem