Commit 832a665b authored by Marco Schmiedel's avatar Marco Schmiedel

Initial commit

parents
.ipynb_checkpoints
__pycache__
workbench/*.bak
cache/*.html
cache/*.csv
\ No newline at end of file
{
"paths": [
"./work/"
],
"exclude": [
"Workbench.mwb.bak",
"__pycache__",
".ipynb_checkpoints",
"./work/cache",
".DS_Store",
"node_modules",
"package-lock.json",
"package.json",
".gitignore",
".sidekick",
".vscode",
".git"
]
}
{
"fileId": "22983490-9c01-4bd1-8649-dfe87c659225",
"originalPath": "work/config/MauiConfig.py",
"currentPath": "work/config/MauiConfig.py",
"hash": "7749ff881e9fac6aaeffe5274aa9566b2b5692a1ec4e7f47e20b79671a0788a3",
"docContent": "<p>In this configuration, you’ll find the credentials required to log in to Freenet-Maui.</p>",
"checkedStatus": "done",
"comments": [
{
"commentId": "3bc16f5e-4032-44a8-9012-4b632849ba50",
"text": "This data is currently stored statically and should be dynamically linked to the Docker container at the appropriate time.",
"timestamp": 1744614418809
}
],
"lastCheckedTimestamp": 1744614348029,
"lastFileModificationTimestamp": 1744614095522.7373
}
{
"fileId": "36e791b4-e235-42f6-ac61-8560f1762892",
"originalPath": "work/workbench/Workbench.mwb",
"currentPath": "work/workbench/Workbench.mwb",
"hash": "050435dbe63c623b2d9a4201250dad0b5b1c43601ece3e390392e4541b182c9b",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744619680767,
"lastFileModificationTimestamp": 1744619453253.4011
}
{
"fileId": "5ec4e9ba-309d-438b-8e17-ce5802f3deb2",
"originalPath": "work/workbench/Documentation.md",
"currentPath": "work/workbench/Documentation.md",
"hash": "7f2058d974b71f0a0da97fc41b918c41c33a4905c15cd54d189ec8d1311fa972",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744619683307,
"lastFileModificationTimestamp": 1744619461883.1462
}
{
"fileId": "d470fd53-8f95-47d0-a63b-5851586c0eda",
"originalPath": "work/manager/SeleniumManager.py",
"currentPath": "work/manager/SeleniumManager.py",
"hash": "be7dde51af30a8ea1f80ea3090f39d2e9a84168cf7d3e15086c16bdbbfde2c26",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744620379152,
"lastFileModificationTimestamp": 1744547804595.8647
}
{
"files.exclude": {
"**/__pycache__": true,
"**/.ipynb_checkpoints" : true,
"**/*.mwb.bak" : true,
"cache/*.html" : true,
"cache/.gitkeep" : true,
".gitignore" : false
}
}
# This variable stores the username for accessing Maui.
MAUI_USERNAME = "28009594-198"
# This variable stores the password for accessing Maui.
MAUI_PASSWORD = "8v#5YeeQyh"
# This variable stores the authentication code (2FA code) for accessing Maui.
MAUI_AUTHCODE = "2D3JJNG3WWGSWRDI5KRW2MZKL3NJEZXJ"
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
from selenium import webdriver
import time
import random
import os
import uuid
import glob
# Diese Klasse verwaltet eine Selenium-Instanz und stellt Methoden zum Abrufen von Webseiten bereit.
class SeleniumManager:
# Diese Methode ist der Konstruktor und initialisiert den Firefox-Browser mit den gewünschten Optionen.
def __init__(
self,
window_size: str = "1920,1080",
headless: bool = True,
disable_gpu: bool = True,
no_sandbox: bool = True,
disable_dev_shm_usage: bool = True,
geckodriver_path: str = '/usr/bin/geckodriver'
):
# Diese Variable speichert die Firefox-Optionen, die für den Browser gelten.
firefox_options = Options()
# Diese Anweisung setzt die Fenstergröße nach den Vorgaben.
firefox_options.add_argument(f"--window-size={window_size}")
# Diese Abfrage fügt die Headless-Option nur hinzu, wenn headless True ist.
if headless:
# Diese Anweisung macht den Browser unsichtbar.
firefox_options.add_argument("--headless")
# Diese Anweisung deaktiviert die GPU, wenn disable_gpu True ist, ansonsten wird nichts hinzugefügt.
firefox_options.add_argument("--disable-gpu" if disable_gpu else "")
# Diese Anweisung deaktiviert die Sandbox, wenn no_sandbox True ist, ansonsten wird nichts hinzugefügt.
firefox_options.add_argument("--no-sandbox" if no_sandbox else "")
# Diese Anweisung deaktiviert dev-shm, wenn disable_dev_shm_usage True ist, ansonsten wird nichts hinzugefügt.
firefox_options.add_argument("--disable-dev-shm-usage" if disable_dev_shm_usage else "")
# Diese Anweisung setzt den User-Agent auf einen zufällig generierten Wert.
firefox_options.set_preference("general.useragent.override", self.getRandomUserAgent())
# Diese Variable erstellt den Dienst für den Firefox-Treiber unter Verwendung des angegebenen Pfads.
service = Service(geckodriver_path)
# Diese Variable initialisiert den Firefox-WebDriver mit dem Dienst und den Optionen.
self.driver = webdriver.Firefox(service=service, options=firefox_options)
# Diese Methode führt eine einfache Anfrage an eine bestimmte URL durch und speichert den HTML-Inhalt im Cache.
def simpleRequest(self, url):
# Diese Anweisung öffnet die angegebene URL mit dem Selenium-Treiber.
self.driver.get(url)
# Diese Variable speichert den HTML-Quellcode der aktuellen Seite.
html = self.driver.page_source
# Diese Variable legt den Pfad für den Cache-Ordner fest, relativ zum Skript.
cache_dir = os.path.join(os.path.dirname(__file__), "..", "cache")
# Diese Abfrage prüft, ob der Cache-Ordner existiert.
if not os.path.exists(cache_dir):
# Diese Anweisung erstellt den Cache-Ordner, falls er nicht vorhanden ist.
os.makedirs(cache_dir)
# Diese Variable generiert einen eindeutigen Dateinamen anhand einer UUID.
unique_filename = f"{uuid.uuid4().hex}.html"
# Diese Variable kombiniert den Cache-Pfad mit dem eindeutigen Dateinamen.
file_path = os.path.join(cache_dir, unique_filename)
# Diese Anweisung öffnet die Datei und schreibt den HTML-Inhalt hinein.
with open(file_path, "w", encoding="utf-8") as f:
f.write(html)
# Diese Variable sammelt alle HTML-Dateien im Cache-Ordner.
files = glob.glob(os.path.join(cache_dir, "*.html"))
# Diese Anweisung sortiert die Dateien nach Änderungsdatum, wobei die neueste zuerst steht.
files.sort(key=os.path.getmtime, reverse=True)
# Diese Schleife löscht alle Dateien außer den 10 neuesten.
for old_file in files[10:]:
# Diese Fehlerbehandlung versucht die Datei zu entfernen und gibt einen Fehler aus, wenn es fehlschlägt.
try:
os.remove(old_file)
except Exception as e:
print(f"Fehler beim Löschen der Datei {old_file}: {e}")
# Diese Anweisung gibt den Selenium-Treiber zurück.
return self.driver
# Diese Methode scrollt die Seite schrittweise, um möglichst alle dynamischen Inhalte zu laden.
def performScroll(self, scroll_step=600, scroll_pause=0.01, max_tries=10):
# Diese Variable speichert die anfängliche Höhe des Dokuments.
last_height = self.driver.execute_script("return document.body.scrollHeight")
# Diese Variable zählt, wie oft sich die Höhe nacheinander nicht geändert hat.
stop_counter = 0
# Diese Schleife wird durchlaufen, um mehrmals zu scrollen und somit alle Inhalte zu erfassen.
for _ in range(max_tries):
# Diese Schleife scrollt schrittweise durch die Seite.
for pos in range(0, last_height, scroll_step):
# Diese Anweisung scrollt zum jeweiligen Abschnitt.
self.driver.execute_script(f"window.scrollTo(0, {pos});")
time.sleep(scroll_pause)
# Diese Anweisung scrollt an das Ende des Dokuments.
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(scroll_pause)
# Diese Variable liest die neue Höhe nach dem Scrollen.
new_height = self.driver.execute_script("return document.body.scrollHeight")
# Diese Abfrage prüft, ob sich die Höhe im Vergleich zum letzten Durchlauf nicht geändert hat.
if new_height == last_height:
# Diese Anweisung erhöht den Zähler, wenn keine Änderung festgestellt wird.
stop_counter += 1
# Diese Abfrage bricht die Schleife ab, wenn keine Änderung zweimal in Folge festgestellt wurde.
if stop_counter >= 2:
break
else:
# Diese Anweisung setzt den Zähler zurück, wenn eine Veränderung festgestellt wird.
stop_counter = 0
# Diese Anweisung aktualisiert last_height mit der neuen Höhe.
last_height = new_height
# Diese Anweisung gibt den Selenium-Treiber zurück.
return self.driver
# Diese Methode schließt die Selenium-Instanz und beendet den Browser.
def closeDriver(self):
self.driver.quit()
# Diese Methode generiert einen zufälligen User-Agent-String, um die Browsererkennung zu erschweren.
def getRandomUserAgent(self):
# Diese Variable enthält typische Betriebssystem-Angaben.
operating_systems = [
"Windows NT 10.0; Win64; x64",
"Windows NT 6.1; Win64; x64",
"Macintosh; Intel Mac OS X 10_15_7",
"Macintosh; Intel Mac OS X 13_3_1",
"X11; Linux x86_64",
"iPhone; CPU iPhone OS 16_0 like Mac OS X",
"Android 11; Mobile; rv:68.0"
]
# Diese Variable listet verschiedene Browsertypen auf.
browsers = ["chrome", "firefox", "safari", "edge", "opera"]
# Diese Anweisung wählt ein zufälliges Betriebssystem aus.
os_ = random.choice(operating_systems)
# Diese Anweisung wählt einen zufälligen Browser aus.
browser = random.choice(browsers)
# Diese Variablen generieren zufällige Versionsnummern.
major_version = random.randint(60, 120)
minor_version = random.randint(0, 4000)
build_version = random.randint(0, 999)
patch_version = random.randint(0, 99)
# Diese Abfrage erstellt den User-Agent für Chrome.
if browser == "chrome":
ua = (
f"Mozilla/5.0 ({os_}) AppleWebKit/537.36 "
f"(KHTML, like Gecko) Chrome/{major_version}.{build_version}.{minor_version}.{patch_version} "
f"Safari/537.36"
)
# Diese Abfrage erstellt den User-Agent für Firefox.
elif browser == "firefox":
rv_version = f"{major_version}.0"
ua = (
f"Mozilla/5.0 ({os_}; rv:{rv_version}) "
f"Gecko/20100101 Firefox/{rv_version}"
)
# Diese Abfrage erstellt den User-Agent für Safari.
elif browser == "safari":
safari_version = f"{major_version}.{random.randint(0, 9)}"
webkit_version = f"605.1.{random.randint(0, 99)}"
ua = (
f"Mozilla/5.0 ({os_}) AppleWebKit/{webkit_version} "
f"(KHTML, like Gecko) Version/{safari_version} Safari/{webkit_version}"
)
# Diese Abfrage erstellt den User-Agent für Edge.
elif browser == "edge":
blink_version = f"{major_version}.{build_version}.{minor_version}.{patch_version}"
ua = (
f"Mozilla/5.0 ({os_}) AppleWebKit/537.36 "
f"(KHTML, like Gecko) Chrome/{blink_version} Safari/537.36 Edg/{blink_version}"
)
# Diese Abfrage erstellt den User-Agent für Opera.
else:
blink_version = f"{major_version}.{build_version}.{minor_version}.{patch_version}"
ua = (
f"Mozilla/5.0 ({os_}) AppleWebKit/537.36 "
f"(KHTML, like Gecko) Chrome/{blink_version} Safari/537.36 OPR/{blink_version}"
)
# Diese Anweisung gibt den generierten User-Agent zurück.
return ua
This diff is collapsed.
jupyter lab
docker build --platform linux/amd64 -t obsidian:latest .
# dev
docker run -it -v ./commands:/obsidian/commands -v ./cache:/obsidian/cache -v ./config:/obsidian/config -v ./manager:/obsidian/manager -v ./models:/obsidian/models -v ./files:/obsidian/files -p 80:80 obsidian:latest /bin/bash
# detached
docker run -it -d obsidian:latest
# ecr
aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 181802255479.dkr.ecr.eu-central-1.amazonaws.com
docker tag obsidian:latest 181802255479.dkr.ecr.eu-central-1.amazonaws.com/obsidian:latest
docker push 181802255479.dkr.ecr.eu-central-1.amazonaws.com/obsidian:latest
docker pull 181802255479.dkr.ecr.eu-central-1.amazonaws.com/obsidian:latest
docker run -it -d --restart always -p 2000:80 181802255479.dkr.ecr.eu-central-1.amazonaws.com/obsidian:latest
####
# MAUI Data Toolkit
## Jupyter Lab
You can use "jupyter lab" to further develop or test this project.
```bash
jupyter lab
```
## Sidekick Documentation
Please use Sidekick for extended documentation and for maintaining the data structure.
```bash
docker run -it --rm -p 2000:8888 -v .:/app/work ceetrox/sidekick:latest
```
# Documentation
## Base (base_base)
In dieser Tabelle sind die grundlegenden Tarife gespeichert, die als Ausgangsbasis für das gesamte Datenbankschema dienen. Die hinterlegten Tarife bilden das Fundament, auf dem alle weiteren Strukturen aufbauen.
Beispielhafte Tarife:
```
Green Allnet Flat
Green Allnet Flat mit Handy 5
Green Allnet Flat mit Handy 10
```
### id_base
Dieser Primärschlüssel identifiziert jeden Tarif eindeutig in der Tabelle.
### basegroup_base
Dieses optionale Feld dient als Fremdschlüssel und verweist auf die Tabelle **Basegroup (basegroup_bgro)**, wodurch eine thematische Gruppierung der Tarife ermöglicht wird.
### provider_base
Hier wird der Name des Providers gespeichert, beispielsweise "Freenet" oder "Klarmobil".
### providercode_base
Ein optionales Feld, in dem ein zusätzlicher Provider-Code (Fremdschlüssel) hinterlegt werden kann.
### name_base
Dieses Feld enthält den offiziellen Tarifnamen, wie er vom Provider vorgegeben wird.
### alias_base
Hier kann ein alternativer Name definiert werden, der den offiziellen Tarifnamen ersetzt.
### network_base
In diesem Feld wird das verwendete Funknetz definiert.
**Mögliche Werte:**
- **1**: D1/Telekom
- **2**: D2/Vodafone
- **4**: O2/Telefonica
### type_base
Dieses Feld legt den Tariftyp fest.
**Mögliche Werte:**
- **1**: Voice
- **2**: Daten
### created_base
Der Zeitstempel, der angibt, wann der Datensatz erstellt wurde.
### updated_base
Der Zeitstempel, an dem der Datensatz zuletzt aktualisiert wurde.
---
## Basegroup (basegroup_bgro)
Diese Tabelle fasst Tarife in Gruppen zusammen, um eine übersichtliche Struktur zu ermöglichen. Die Gruppierung hilft dabei, ähnliche Tarife gemeinsam zu verwalten.
Beispielhafte Tarifgruppen:
```
Red Allnet Flat Tarifgruppe
Green Allnet Flat Tarifgruppe
Blue Allnet Flat Tarifgruppe
```
### id_bgro
Der Primärschlüssel der Tabelle, der jede Tarifgruppe eindeutig identifiziert.
### name_bgro
Hier wird der Name der Tarifgruppe festgelegt.
### created_bgro
Der Zeitpunkt, zu dem die Tarifgruppe erstellt wurde.
### updated_bgro
Der Zeitpunkt, zu dem die Tarifgruppe zuletzt aktualisiert wurde.
---
## Deal (deal_deal)
In dieser Tabelle werden zeitlich befristete Aktionen erfasst. Jede Aktion, jeder Deal verknüpft einen Basis-Tarif aus der Tabelle **Base (base_base)** mit einer Provisiongruppe und definiert spezielle Konditionen.
### id_deal
Der Primärschlüssel der Tabelle, der jede Aktion eindeutig identifiziert.
### base_deal
Dieses Feld ist ein Fremdschlüssel, der auf einen Tarif in der Tabelle **Base (base_base)** verweist.
### provisiongroup_deal
Dieses Feld definiert, unter welcher Provisionsgruppe dieser Tarif verwaltet wird.
### providercode_deal
Ein optionales Feld, das einen zusätzlichen Provider-Code (Fremdschlüssel) speichern kann.
### alias_deal
Ein optionaler, alternativer Name des Deals.
### price_deal
In diesem Feld wird der Preis des Deals als Dezimalwert festgehalten (Netto).
### starts_deal
Das Datum und die Uhrzeit, ab wann der Deal aktiv wird.
### stops_deal
Ein optionales Datum und eine Uhrzeit, bis zu denen der Deal gültig ist.
### provision1_deal bis provision4_deal
Diese Felder enthalten verschiedene Provisionswerte für für die Aktion.
Alle Provisionen zusammengerechnet ergeben die Gesamtprovision.
### created_deal
Der Zeitpunkt, zu dem der Deal-Datensatz erstellt wurde.
### updated_deal
Der Zeitpunkt, zu dem der Deal zuletzt aktualisiert wurde.
---
## Option (option_opti)
Diese Tabelle erfasst spezifische Optionen zu Tarifen, die zusätzliche Leistungen oder Konfigurationen darstellen. Optionen ergänzen die Basistarife und ermöglichen erweiterte Angebotsvarianten.
### id_opti
Der Primärschlüssel der Tabelle, der jede Option eindeutig identifiziert.
### base_opti
Dieses Feld fungiert als Fremdschlüssel und verweist auf den zugehörigen Tarif in der Tabelle **Base (base_base)**.
### provisiongroup_opti
Dieses Feld definiert, unter welcher Provisionsgruppe dieser Tarif verwaltet wird.
### providercode_opti
Ein optionales Feld zur Speicherung eines zusätzlichen Provider-Codes.
### providercategory_opti
Dieses optionale Feld dient der Kategorisierung der Option (Providerkategorie).
### name_opti
Das Feld enthält den offiziellen Namen der Option, wie er vom Provider vorgegeben wird.
### alias_opti
Ein optionaler Alias, der den offiziellen Namen ergänzen oder ersetzen kann.
### price_opti
In diesem Feld wird der Preis der Option als Dezimalwert festgehalten (Netto).
### starts_opti
Das Datum und die Uhrzeit, ab wann die Option aktiv wird.
### stops_opti
Ein optionales Datum und eine Uhrzeit, bis zu denen die Option gültig ist.
### provision1_opti bis provision4_opti
Diese Felder enthalten verschiedene Provisionswerte für für die Option.
Alle Provisionen zusammengerechnet ergeben die Gesamtprovision.
### created_opti
Der Zeitpunkt, zu dem der Options-Datensatz erstellt wurde.
### updated_opti
Der Zeitpunkt, zu dem der Options-Datensatz zuletzt aktualisiert wurde.
---
## Provisiongroup (provisiongroup_pgro)
In dieser Tabelle werden Provisiongruppen verwaltet, die sowohl in Deals als auch in Optionen Anwendung finden. Eine Provisiongruppe legt fest, wie viel Provision prozentual für den Vertrieb freigegeben wird.
### id_pgro
Der Primärschlüssel der Tabelle, der jede Provisiongruppe eindeutig identifiziert.
### name_pgro
Hier wird der Name der Provisiongruppe gespeichert.
### percent_pgro
Dieser Dezimalwert legt den Provisionsprozentsatz fest. Standardmäßig beträgt dieser 0.00.
### created_pgro
Der Zeitpunkt, zu dem der Datensatz der Provisiongruppe erstellt wurde.
### updated_pgro
Ein optionales Feld, das den Zeitpunkt der letzten Aktualisierung der Provisiongruppe festhält.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment