07092026
test / build-docs (push) Successful in 1s

This commit is contained in:
jonnybravo
2026-09-07 17:02:48 +02:00
parent 6678aed520
commit 23b7df0037
118 changed files with 2029 additions and 1467 deletions
+17
View File
@@ -0,0 +1,17 @@
# Python
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
.pytest_cache/
.mypy_cache/
.ruff_cache/
*.egg
# venv
.venv/
venv/
# Projekt-spezifisch
create_selfsign/crt/
+7 -6
View File
@@ -1,6 +1,7 @@
#! /usr/bin/env python3.10 #! /usr/bin/env python3
import os, configparser import os
import configparser
# Set Skript Vars # Set Skript Vars
user_gitconfig = os.environ["HOME"] + os.sep + ".gitconfig" user_gitconfig = os.environ["HOME"] + os.sep + ".gitconfig"
@@ -51,14 +52,14 @@ if not config_ini.has_section("alias"):
for my_git_site in list_my_git_projects: for my_git_site in list_my_git_projects:
if not config_ini.has_section("credential " + '"' + my_git_site + '"'): if not config_ini.has_section("credential " + '"' + my_git_site + '"'):
config_ini.add_section("credential " + '"' + my_git_site + '"') config_ini.add_section("credential " + '"' + my_git_site + '"')
config_ini.set("credential " + '"' + my_git_site + '"', "username", my_username) config_ini.set("credential " + '"' + my_git_site +
'"', "username", my_username)
if not config_ini.has_section("URL " + '"' + my_git_site + '"'): if not config_ini.has_section("URL " + '"' + my_git_site + '"'):
config_ini.add_section("URL " + '"' + my_git_site + '"') config_ini.add_section("URL " + '"' + my_git_site + '"')
config_ini.set("URL " + '"' + my_git_site + '"', "insteadOf", 'git@' + my_git_site + ":") config_ini.set("URL " + '"' + my_git_site + '"',
"insteadOf", 'git@' + my_git_site + ":")
# writing_all_configs # writing_all_configs
with open(user_gitconfig, "w") as configfile: with open(user_gitconfig, "w") as configfile:
config_ini.write(configfile) config_ini.write(configfile)
+91
View File
@@ -0,0 +1,91 @@
# selfsign_ca
CA-gestützte Erstellung selbst-signierter X.509-Zertifikate mit Python `cryptography`.
## Funktionsweise
Beim ersten Aufruf wird automatisch eine Root-CA erstellt (`crt/ca.key` + `crt/ca.crt`). Alle weiteren Zertifikate werden von dieser CA signiert. Existiert die CA bereits, wird sie wiederverwendet.
Jedes Zertifikat wird als Trio abgelegt:
| Datei | Inhalt |
|---|---|
| `crt/<name>.key` | RSA-2048 Private Key (PEM) |
| `crt/<name>.crt` | X.509-Zertifikat, signiert von der CA |
| `crt/<name>-chain.crt` | Server-Zertifikat + CA-Zertifikat (Chain) |
## Abhängigkeiten
```bash
pip install cryptography
```
## Verwendung
### Direktaufruf
```bash
python selfsign_ca.py
```
Erzeugt eine CA (falls nicht vorhanden) und das Zertifikat `test.*` mit den im `__main__`-Block hinterlegten Defaults.
### Als Modul
```python
from selfsign_ca import generate_and_save_cert
generate_and_save_cert(
cert_country="DE",
cert_state="Berlin",
cert_organization="Meine GmbH",
cert_locality="Berlin",
cert_organizational_unit="IT",
cert_common_name="myserver.local",
valid_days=365,
serial_number=100,
cert_name="myserver",
)
```
### CA-Parameter (optional)
Die CA-Daten werden nur beim ersten Erstellen der CA verwendet. Überschreiben via:
```python
generate_and_save_cert(
...,
ca_country="DE",
ca_state="Sachsen",
ca_organization="IchAG",
ca_locality="Dresden",
ca_common_name="My Root CA",
)
```
## Zertifikat prüfen
```bash
openssl verify -CAfile crt/ca.crt crt/<name>.crt
openssl x509 -in crt/<name>.crt -text -noout
```
## CA auf Clients importieren (Vertrauen)
Damit Clients dem Zertifikat vertrauen, muss `crt/ca.crt` im Trust-Store hinterlegt werden:
```bash
# Arch/Manjaro
sudo trust anchor crt/ca.crt
# Debian/Ubuntu
sudo cp crt/ca.crt /usr/local/share/ca-certificates/my-ca.crt
sudo update-ca-certificates
```
## Technische Details
- **CA**: RSA 4096 Bit, SHA256, 10 Jahre gültig, `basicConstraints: CA:TRUE`
- **Endzertifikat**: RSA 2048 Bit, SHA256, Gültigkeit nach `valid_days`, `basicConstraints: CA:FALSE`
- X.509 v3 Extensions: `BasicConstraints`, `KeyUsage`, `SubjectKeyIdentifier`, `AuthorityKeyIdentifier`
- Alle Dateien landen im Unterverzeichnis `crt/` (wird automatisch angelegt)
+218
View File
@@ -0,0 +1,218 @@
import os
from datetime import datetime, timedelta, timezone
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
CRT_DIR = "crt"
CA_KEY_FILE = os.path.join(CRT_DIR, "ca.key")
CA_CRT_FILE = os.path.join(CRT_DIR, "ca.crt")
def _ensure_crt_dir():
"""Create crt/ directory if it doesn't exist."""
os.makedirs(CRT_DIR, exist_ok=True)
def _save_pem(filename, data):
"""Write PEM data to file, converting \\n to real newlines."""
if isinstance(data, bytes):
data = data.decode()
with open(filename, "w") as f:
for line in data.replace("\r", "").split("\\n"):
f.write(line + "\n")
def _build_subject(country, state, organization, locality, common_name,
organizational_unit=None):
"""Build a cryptography X.509 Name from individual fields."""
attrs = [
x509.NameAttribute(NameOID.COUNTRY_NAME, country),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
x509.NameAttribute(NameOID.LOCALITY_NAME, locality),
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
]
if organizational_unit:
attrs.append(x509.NameAttribute(
NameOID.ORGANIZATIONAL_UNIT_NAME, organizational_unit))
return x509.Name(attrs)
def generate_ca_cert(cert_country, cert_state, cert_organization,
cert_locality, cert_common_name, valid_days=3650):
"""
Generate a self-signed CA certificate and save ca.key / ca.crt.
Returns (cert_pem, key_pem).
"""
subject = _build_subject(cert_country, cert_state, cert_organization,
cert_locality, cert_common_name)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
now = datetime.now(timezone.utc)
cert = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(subject) # self-signed
.public_key(private_key.public_key())
.serial_number(1)
.not_valid_before(now)
.not_valid_after(now + timedelta(days=valid_days))
.add_extension(x509.BasicConstraints(ca=True, path_length=None),
critical=True)
.add_extension(x509.KeyUsage(
key_cert_sign=True, crl_sign=True,
digital_signature=False, content_commitment=False,
key_encipherment=False, data_encipherment=False,
key_agreement=False, encipher_only=False, decipher_only=False,
), critical=True)
.add_extension(x509.SubjectKeyIdentifier.from_public_key(
private_key.public_key()), critical=False)
.sign(private_key, hashes.SHA256())
)
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
key_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
_ensure_crt_dir()
_save_pem(CA_KEY_FILE, key_pem)
_save_pem(CA_CRT_FILE, cert_pem)
print(f"CA erstellt: {CA_KEY_FILE}, {CA_CRT_FILE}")
return cert_pem, key_pem
def load_ca():
"""
Load CA certificate and private key from disk.
Returns (ca_cert, ca_pkey) as cryptography objects, or None if missing.
"""
if not os.path.exists(CA_CRT_FILE) or not os.path.exists(CA_KEY_FILE):
return None
with open(CA_CRT_FILE, "rb") as f:
ca_cert = x509.load_pem_x509_certificate(f.read())
with open(CA_KEY_FILE, "rb") as f:
ca_pkey = serialization.load_pem_private_key(f.read(), password=None)
return ca_cert, ca_pkey
def generate_cert_signed_by_ca(ca_cert, ca_pkey,
cert_country, cert_state, cert_organization,
cert_locality, cert_organizational_unit,
cert_common_name, valid_days, serial_number):
"""
Generate a certificate signed by the given CA.
Returns (cert_pem, key_pem) as strings.
"""
subject = _build_subject(cert_country, cert_state, cert_organization,
cert_locality, cert_common_name,
cert_organizational_unit)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
now = datetime.now(timezone.utc)
cert = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(ca_cert.subject)
.public_key(private_key.public_key())
.serial_number(serial_number)
.not_valid_before(now)
.not_valid_after(now + timedelta(days=valid_days))
.add_extension(x509.BasicConstraints(ca=False, path_length=None),
critical=True)
.add_extension(x509.KeyUsage(
digital_signature=True, key_encipherment=True,
key_cert_sign=False, crl_sign=False,
content_commitment=False, data_encipherment=False,
key_agreement=False, encipher_only=False, decipher_only=False,
), critical=True)
.add_extension(x509.SubjectKeyIdentifier.from_public_key(
private_key.public_key()), critical=False)
.add_extension(x509.AuthorityKeyIdentifier.from_issuer_public_key(
ca_pkey.public_key()), critical=False)
.sign(ca_pkey, hashes.SHA256())
)
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
key_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
return str(cert_pem.decode()), str(key_pem.decode())
def generate_and_save_cert(cert_country, cert_state, cert_organization,
cert_locality, cert_organizational_unit,
cert_common_name, valid_days=365, serial_number=100,
ca_country="DE", ca_state="Sachsen",
ca_organization="IchAG",
ca_locality="Dresden",
ca_common_name="My Root CA",
cert_name="server"):
"""
High-level entry point:
1. If CA not present, create it.
2. Generate certificate signed by the CA.
3. Save key and cert (with optional chain) to <cert_name>.key / <cert_name>.crt.
"""
ca = load_ca()
if ca is None:
print("CA nicht gefunden – erstelle CA ...")
generate_ca_cert(ca_country, ca_state, ca_organization,
ca_locality, ca_common_name)
ca = load_ca()
if ca is None:
raise RuntimeError("CA konnte nicht erstellt/geladen werden.")
ca_cert, ca_pkey = ca
cert_pem, key_pem = generate_cert_signed_by_ca(
ca_cert, ca_pkey,
cert_country, cert_state, cert_organization,
cert_locality, cert_organizational_unit,
cert_common_name, valid_days, serial_number,
)
key_file = os.path.join(CRT_DIR, f"{cert_name}.key")
crt_file = os.path.join(CRT_DIR, f"{cert_name}.crt")
chain_file = os.path.join(CRT_DIR, f"{cert_name}-chain.crt")
_ensure_crt_dir()
_save_pem(key_file, key_pem)
_save_pem(crt_file, cert_pem)
# Chain file: server cert + CA cert
chain = cert_pem.replace("\r", "")
ca_pem = ca_cert.public_bytes(serialization.Encoding.PEM).decode()
chain += "\n" + ca_pem
_save_pem(chain_file, chain)
print(f"Zertifikat erstellt: {key_file}, {crt_file}, {chain_file}")
return cert_pem, key_pem
if __name__ == "__main__":
generate_and_save_cert(
cert_country="DE",
cert_state="Dresden",
cert_organization="IchAG",
cert_locality="Sachsen",
cert_organizational_unit="mandan01",
cert_common_name="mandan01",
valid_days=365,
serial_number=12,
cert_name="test",
)
-50
View File
@@ -1,50 +0,0 @@
from OpenSSL import crypto
from Crypto.PublicKey import RSA
def generate_self_signed_cert(cert_country, cert_state, cert_organization,
cert_locality, cert_organizational_unit,
cert_common_name, valid_days, serial_number):
rsa_key = RSA.generate(2048)
pk = crypto.load_privatekey(crypto.FILETYPE_PEM,
rsa_key.exportKey('PEM', pkcs=1))
cert = crypto.X509()
sub = cert.get_subject()
sub.CN = cert_common_name
sub.C = cert_country
sub.ST = cert_state
sub.L = cert_locality
sub.O = cert_organization
# optional
if cert_organizational_unit:
sub.OU = cert_organizational_unit
cert.set_serial_number(serial_number)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(valid_days * 24 * 60 * 60) # Valid for a year
cert.set_issuer(sub)
cert.set_pubkey(pk)
cert.sign(pk, 'sha1')
cert_text = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
priv_key_text = rsa_key.exportKey('PEM', pkcs=1)
return str(cert_text), str(priv_key_text)
if __name__ == "__main__":
test = generate_self_signed_cert(cert_country="DE", cert_state="Dresden", cert_organization="IchAG", cert_locality="Sachsen", cert_organizational_unit="mandan01", cert_common_name="mandan01", valid_days=365, serial_number=12)
privatekey = test[1].rstrip("'").strip("b'")
cert = test[0].rstrip("'").strip("b'")
#print(privatekey)
for key in privatekey.split("\\n"):
with open("testkey", "a") as testcert:
print(key, file=testcert)
for cert_out in cert.split("\\n"):
with open("testcert", "a") as testcert_out:
print(cert_out, file=testcert_out)
#test2 = "xccj\njdkjd\n"
#print(test2)
@@ -0,0 +1,88 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"id": "70914fb0-e3d4-4233-ad33-70a1a2517a6b",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b04d189c-260b-427d-af65-9ad88ac5ecf2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['.bash_logout', '.bash_profile', '.bashrc', '.zshrc', '.config', '.Xclients', '.dir_colors', '.profile', '.zshenv', '.local', 'Schreibtisch', 'Downloads', 'Vorlagen', 'Öffentlich', 'Dokumente', 'Musik', 'Bilder', 'Videos', '.cache', '.mozilla', '.lyrics', '.gemini', 'my_sway_konfig', 'Projekte', '.ansible', '.ssh', '.vim', '.tmux.conf', '.npm', 'create_lxc', 'lxc_folder', '.docker', 'ebook', '.bash_history', '.zhistory', 'Nextcloud', '.yarn', '.pki', 'sonovum', '.wget-hsts', '.joplin', 'JoplinBackup', '.python_history', '.gnupg', '.gitconfig', '.gitignore', '.env', '.ollama', '.claude', '.steam', '.pulse-cookie', '.icons', 'cert', 'podcast-studio', 'scripts', 'Screenshots', '.claude.json.backup.1772740061126', '.claude.json.backup.1772740061140', '.claude.json.backup.1772740137318', '.claude.json.backup.1772740278244', '.claude.json.backup.1772740278252', '.claude.json', 'Media-Data', '.smbcredentials-media', 'nfs', '.mplayer', '.steampath', '.steampid', '.eclipse', '.swt', '.java', 'conf.ini', 'token_vaultwarden', 'jenkins_status_example', '.sigstore', 'tinydb_vs_sqlite.md', '.cargo', '.viminfo', '.ipython', '.jupyter']\n"
]
}
],
"source": [
"print(os.listdir(\"/home/jonnybravo/\"))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "55709fef-1402-4c9f-b2ee-2be8e55a137c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['ver_next_einbinden.md', 'yubikey_setup_guide.md', 'networkconfig_dns.md', 'blueray_install_manjaro.md', 'calcurse_tastenkürzel.md', 'Stretchen_laufen.md']\n"
]
}
],
"source": [
"all_files_with_md = [ x for x in os.listdir(\"/home/jonnybravo/Dokumente\") if x.endswith(\".md\") ]\n",
"print(all_files_with_md)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89b5f549-36c9-4a62-83ba-69ad84ec6be6",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "692cc39e-ddf8-4bcb-ad08-baed47e37a74",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,39 @@
#! /usr/bin/env Python3
import os
class create_unit_service():
def __init__(self, UnitName = str,systemd_sysfolder = "/etc/systemd/system", execstart = str, systemd_type = "simple") -> None:
self.UnitName = UnitName
self.systemd_systemfolder = systemd_sysfolder
self.execstart = execstart
if systemd_type == "simple" or systemd_type == "oneshot":
self.systemd_type = systemd_type
else:
print("Kein richtiger Typ")
raise "systemd_type ist nicht richtig"
def create_unit(self):
UnitFile = self.systemd_systemfolder + os.sep + self.UnitName + ".service"
if os.path.exists(self.systemd_systemfolder):
if os.path.exists(UnitFile):
print("Gibt es bereits")
else:
print("Wird angelegt !!")
with open(UnitFile, "w") as ufile:
print("[Unit]",
"[Service]",
"Type=oneshot",
"PrivateTmp=yes",
"DynamicUser=yes",
"ExecStart={my_execStart}".format(my_execStart=self.execstart), file=ufile, sep="\n")
os.popen("systemctl daemon-reload")
def delete_unit(self):
pass
if __name__ == "__main__":
new_unit = create_unit_service(UnitName="TestUnit",
execstart="""/usr/bin/echo "Ich bin eine TestUnit %p" """,
systemd_type="oneshot")
new_unit.create_unit()
@@ -0,0 +1,14 @@
import os
print(os.listdir("/home/jonnybravo/"))
all_files_with_md = [ x for x in os.listdir("/home/jonnybravo/Dokumente") if x.endswith(".md") ]
print(all_files_with_md)
+88
View File
@@ -0,0 +1,88 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"id": "70914fb0-e3d4-4233-ad33-70a1a2517a6b",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b04d189c-260b-427d-af65-9ad88ac5ecf2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['.bash_logout', '.bash_profile', '.bashrc', '.zshrc', '.config', '.Xclients', '.dir_colors', '.profile', '.zshenv', '.local', 'Schreibtisch', 'Downloads', 'Vorlagen', 'Öffentlich', 'Dokumente', 'Musik', 'Bilder', 'Videos', '.cache', '.mozilla', '.lyrics', '.gemini', 'my_sway_konfig', 'Projekte', '.ansible', '.ssh', '.vim', '.tmux.conf', '.npm', 'create_lxc', 'lxc_folder', '.docker', 'ebook', '.bash_history', '.zhistory', 'Nextcloud', '.yarn', '.pki', 'sonovum', '.wget-hsts', '.joplin', 'JoplinBackup', '.python_history', '.gnupg', '.gitconfig', '.gitignore', '.env', '.ollama', '.claude', '.steam', '.pulse-cookie', '.icons', 'cert', 'podcast-studio', 'scripts', 'Screenshots', '.claude.json.backup.1772740061126', '.claude.json.backup.1772740061140', '.claude.json.backup.1772740137318', '.claude.json.backup.1772740278244', '.claude.json.backup.1772740278252', '.claude.json', 'Media-Data', '.smbcredentials-media', 'nfs', '.mplayer', '.steampath', '.steampid', '.eclipse', '.swt', '.java', 'conf.ini', 'token_vaultwarden', 'jenkins_status_example', '.sigstore', 'tinydb_vs_sqlite.md', '.cargo', '.viminfo', '.ipython', '.jupyter']\n"
]
}
],
"source": [
"print(os.listdir(\"/home/jonnybravo/\"))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "55709fef-1402-4c9f-b2ee-2be8e55a137c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['ver_next_einbinden.md', 'yubikey_setup_guide.md', 'networkconfig_dns.md', 'blueray_install_manjaro.md', 'calcurse_tastenkürzel.md', 'Stretchen_laufen.md']\n"
]
}
],
"source": [
"all_files_with_md = [ x for x in os.listdir(\"/home/jonnybravo/Dokumente\") if x.endswith(\".md\") ]\n",
"print(all_files_with_md)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89b5f549-36c9-4a62-83ba-69ad84ec6be6",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "692cc39e-ddf8-4bcb-ad08-baed47e37a74",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+51 -11
View File
@@ -1,12 +1,34 @@
#! /usr/bin/env Python3 #! /usr/bin/env Python3
import os import os
from posix import stat
from jinja2 import Template
import subprocess
def is_root():
if os.geteuid() == 0:
return True
else:
return False
class create_unit_service(): class create_unit_service():
def __init__(self, UnitName = str,systemd_sysfolder = "/etc/systemd/system", execstart = str, systemd_type = "simple") -> None: def __init__(self,
UnitName: str,
systemd_sysfolder: str = "/etc/systemd/system",
UnitExecStart = str,
systemd_type: str = "simple",
UnitDescription: str = "Meine Unit",
UnitUser = str,
UnitWorkDir: str = "/home/jonnybravo/"
) -> None:
self.UnitName = UnitName self.UnitName = UnitName
self.systemd_systemfolder = systemd_sysfolder self.systemd_systemfolder = systemd_sysfolder
self.execstart = execstart self.UnitExecStart = UnitExecStart
self.UnitDescription = UnitDescription
self.UnitWorkDir = UnitWorkDir
self.UnitUser = UnitUser
if systemd_type == "simple" or systemd_type == "oneshot": if systemd_type == "simple" or systemd_type == "oneshot":
self.systemd_type = systemd_type self.systemd_type = systemd_type
else: else:
@@ -20,20 +42,38 @@ class create_unit_service():
print("Gibt es bereits") print("Gibt es bereits")
else: else:
print("Wird angelegt !!") print("Wird angelegt !!")
with open(UnitFile, "w") as ufile: with open('template/LinuxUnit.j2') as f:
print("[Unit]", tmpl = Template(f.read())
"[Service]", with open(UnitFile, "w") as uf:
"Type=oneshot", print(tmpl.render(
"PrivateTmp=yes", description = self.UnitDescription,
"DynamicUser=yes", working_dir = self.UnitWorkDir,
"ExecStart={my_execStart}".format(my_execStart=self.execstart), file=ufile, sep="\n") exec_command = self.UnitExecStart,
os.popen("systemctl daemon-reload") user = self.UnitUser,
env_vars = {"PYTHONPATH": "."},
service_type = self.systemd_type
),file=uf)
def unit_start_and_load(self):
UnitFile = self.systemd_systemfolder + os.sep + self.UnitName + ".service"
status_daemon_reload = subprocess.run(["systemctl", "daemon-reload"])
if status_daemon_reload.returncode != 0:
raise Exception( f'Systemd reload Invalid result: { status_daemon_reload.returncode }' )
else:
print(f"{self.UnitName} wurde angelegt und kann genutzt werden !!! Ein starten oder Enable wurde bewusst nicht eingebaut.")
def delete_unit(self): def delete_unit(self):
pass pass
if __name__ == "__main__": if __name__ == "__main__":
if is_root():
new_unit = create_unit_service(UnitName="TestUnit", new_unit = create_unit_service(UnitName="TestUnit",
execstart="""/usr/bin/echo "Ich bin eine TestUnit %p" """, UnitExecStart="""/usr/bin/echo "Ich bin eine TestUnit %p" """,
UnitUser="jonnybravo",
systemd_type="oneshot") systemd_type="oneshot")
new_unit.create_unit() new_unit.create_unit()
new_unit.unit_start_and_load()
else:
print("Du bist nicht root führe das Skript mit sudo aus")
print('sudo python <skript>')
+3
View File
@@ -0,0 +1,3 @@
- systemd reload
- systemd start
- systemd enable
+40
View File
@@ -0,0 +1,40 @@
[Unit]
Description={{ description }}
# Stellt sicher, dass das Netzwerk bereit ist
After=network-online.target
Wants=network-online.target
[Service]
# 'simple' ist der Standard, 'exec' wartet, bis der Prozess wirklich gestartet ist
Type={{ service_type }}
# Arbeitsverzeichnis (wichtig für relative Pfade im Skript)
WorkingDirectory={{ working_dir }}
# Ausführen als spezifischer Benutzer (Sicherheit!)
User={{ user }}
Group={{ user }}
# Umgebungsvariablen (z.B. für Python Pfade oder API Keys)
{% if env_vars -%}
{% for key, value in env_vars.items() -%}
Environment="{{ key }}={{ value }}"
{% endfor -%}
{% endif %}
# Vor dem Start kurz warten (optional, falls nötig)
{% if wait_before_start -%}
ExecStartPre=/usr/bin/sleep {{ wait_before_start }}
{% endif %}
# Der eigentliche Befehl
ExecStart={{ exec_command }}
# Neustart-Logik: Startet den Dienst automatisch neu, wenn er abstürzt
Restart=on-failure
# Wartezeit zwischen Neustartversuchen
RestartSec=5s
[Install]
# Definiert, wann die Unit beim Booten gestartet wird
WantedBy=multi-user.target
+59
View File
@@ -0,0 +1,59 @@
# Benötigt die Installation von 'requests' und 'beautifulsoup4'
# pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
import sys
import re
def suche_film_url(film_titel):
"""
Sucht auf IMDb nach einem Film und gibt die deutsche URL der Filmseite zurück.
Args:
film_titel: Der Titel des Films, nach dem gesucht werden soll.
Returns:
Die deutsche URL der Filmseite oder None, wenn nichts gefunden wurde.
"""
such_url = f"https://www.imdb.com/find?q={film_titel.replace(' ', '+')}"
# Header, um die deutsche Seite zu bevorzugen
headers = {
'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
}
try:
response = requests.get(such_url, headers=headers)
response.raise_for_status() # Löst eine Ausnahme für HTTP-Fehler aus
soup = BeautifulSoup(response.text, 'html.parser')
# Finden des ersten Links im Titel-Ergebnisbereich
ergebnis_bereich = soup.find('section', {'data-testid': 'find-results-section-title'})
if ergebnis_bereich:
link_tag = ergebnis_bereich.find('a', href=re.compile(r'/title/tt\d+'))
if link_tag and 'href' in link_tag.attrs:
pfad = link_tag['href']
# Extrahieren der Titel-ID (z.B. tt0133093)
match = re.search(r'(tt\d+)', pfad)
if match:
titel_id = match.group(1)
return f"https://www.imdb.com/de/title/{titel_id}"
except requests.exceptions.RequestException as e:
print(f"Ein Fehler bei der Anfrage ist aufgetreten: {e}")
return None
return None
if __name__ == "__main__":
if len(sys.argv) > 1:
film_name = " ".join(sys.argv[1:])
url = suche_film_url(film_name)
if url:
print(f"URL für '{film_name}' gefunden: {url}")
else:
print(f"Konnte keinen Film mit dem Titel '{film_name}' finden.")
else:
print("Benutzung: python imdb_suche.py <film_titel>")
+99
View File
@@ -0,0 +1,99 @@
import urllib.request
import json
import re
import ssl
import imdb_suche
import sys
def get_movie_details(imdb_url):
"""
Ruft eine IMDb-Filmseite auf und extrahiert den Regisseur und die Hauptdarsteller.
Args:
imdb_url (str): Die vollständige URL zur IMDb-Filmseite.
Returns:
dict: Ein Wörterbuch mit "director" und "actors" oder None bei einem Fehler.
"""
if not re.match(r"https://www.imdb.com/de/title/tt\d+/?", imdb_url):
print("Fehler: Ungültige IMDb-URL. Die URL sollte so aussehen: https://www.imdb.com/title/tt...")
return None
try:
# SSL-Kontext erstellen, um Zertifikatsüberprüfungsfehler zu umgehen
context = ssl._create_unverified_context()
# User-Agent setzen, um wie ein Browser auszusehen und Blockaden zu vermeiden
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
req = urllib.request.Request(imdb_url, headers=headers)
with urllib.request.urlopen(req, context=context) as response:
html_content = response.read().decode('utf-8')
# Suchen Sie nach dem strukturierten Datenblock (JSON-LD), der Filminformationen enthält
match = re.search(
r'<script type="application/ld\+json">(.*?)</script>', html_content, re.DOTALL)
if not match:
print("Fehler: Konnte den JSON-LD-Datenblock auf der Seite nicht finden.")
return None
json_data = json.loads(match.group(1))
# Extrahieren des Regisseurs
director = [d.get('name') for d in json_data.get('director', [])] if isinstance(
json_data.get('director'), list) else [json_data.get('director', {}).get('name')]
# Extrahieren der Schauspieler
actors = [a.get('name') for a in json_data.get('actor', [])]
# Extrahieren des Erscheinungsjahres
release_year = json_data.get('datePublished', '').split('-')[0]
return {
"director": [d for d in director if d],
"actors": [a for a in actors if a],
"release_year": release_year
}
except urllib.error.HTTPError as e:
print(f"HTTP-Fehler beim Abrufen der URL: {e.code} {e.reason}")
return None
except Exception as e:
print(f"Ein Fehler ist aufgetreten: {e}")
return None
if __name__ == "__main__":
if len(sys.argv) > 1:
film_name = " ".join(sys.argv[1:])
url = imdb_suche.suche_film_url(film_name)
if url:
print(f"URL für '{film_name}' gefunden: {url}")
# Beispiel-URL für den Film "Inception"
# Du kannst jede andere IMDb-Film-URL hier einfügen
movie_url = url
print(f"Rufe Informationen für den Film ab: {movie_url}")
details = get_movie_details(movie_url)
if details:
print("\n--- Filminformationen ---")
print(f"Regisseur(e): {', '.join(details['director'])}")
# Zeigt die ersten 5 Schauspieler an
print(f"Hauptdarsteller: {', '.join(details['actors'][:5])}")
if details.get('release_year'):
print(f"Erscheinungsjahr: {details['release_year']}")
print("-------------------------")
else:
print(f"Konnte keinen Film mit dem Titel '{film_name}' finden.")
else:
print("Benutzung: python imdb_suche.py <film_titel>")
@@ -1,7 +0,0 @@
Folgende Punkte
- erstellen eine SQlite DB
- Die DB soll folgende Tabellen haben Genre, MyList,
- Die Anzeige soll über Flask angezeigt werden
Weitere Schritte folgen !!!
@@ -1,69 +0,0 @@
import DBcm
def create_movie_database(db_name="movie_db.db"):
create_my_list = """
create table if not exists movie_list (
id integer not null primary key autoincrement,
titel varchar(64) not null,
genre_id integer not null,
regie_id integer not null
)
"""
create_genre = """
create table if not exists genre (
id integer not null primary key autoincrement,
name varchar(64) not null
)
"""
create_regie = """
create table if not exists regie (
id integer not null primary key autoincrement,
surname varchar(64) not null,
lastname varchr(64) not null
)
"""
create_medium = """
create table if not exists medium (
id integer not null primary key autoincrement,
medium varchar(64) not null
)
"""
with DBcm.UseDatabase(db_name) as db:
db.execute(create_my_list)
db.execute(create_genre)
db.execute(create_regie)
db.execute(create_medium)
def all_genres(db_name="movie_db.db"):
ALL_GENRE = "SELECT * from genre"
with DBcm.UseDatabase(db_name) as db:
db.execute(ALL_GENRE)
all_genre = [i[1] for i in db.fetchall()]
return all_genre
def search_genre_id(db_name="movie_db.db", genre_name=str):
GENRE_QUERY = """
select id from genre
where name = ?
"""
try:
with DBcm.UseDatabase(db_name) as db:
db.execute(GENRE_QUERY, (genre_name,))
genre_id = db.fetchone()[0]
return int(genre_id)
except:
return int(0)
if __name__ == "__main__":
create_movie_database()
print(all_genres())
print(search_genre_id(genre_name="war"))
@@ -1,534 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "5263a987-da36-46d7-a2e7-d0658cda09c1",
"metadata": {},
"outputs": [],
"source": [
"import DBcm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3f6b0763-4106-4bfa-9aef-7afbd180c6d4",
"metadata": {},
"outputs": [],
"source": [
"db_name = \"movie_db.db\"\n",
"\n",
"create_my_list = \"\"\"\n",
" create table if not exists movie_list (\n",
" id integer not null primary key autoincrement,\n",
" titel varchar(64) not null,\n",
" genre_id integer not null,\n",
" regie_id integer not null\n",
" \n",
" )\n",
"\n",
"\"\"\"\n",
"\n",
"create_genre = \"\"\"\n",
" create table if not exists genre (\n",
" id integer not null primary key autoincrement,\n",
" name varchar(64) not null\n",
" )\n",
"\"\"\"\n",
"\n",
"create_regie = \"\"\"\n",
" create table if not exists regie (\n",
" id integer not null primary key autoincrement,\n",
" surname varchar(64) not null,\n",
" lastname varchr(64) not null\n",
" )\n",
"\"\"\"\n",
"\n",
"\n",
"with DBcm.UseDatabase(db_name) as db: \n",
" db.execute(create_my_list)\n",
" db.execute(create_genre)\n",
" db.execute(create_regie)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "376ef812-97b5-45ba-8ef1-eb8d7829494a",
"metadata": {},
"outputs": [],
"source": [
"# ADDed Genre values\n",
"\n",
"ADD_GENRE_VALUE = \"\"\"\n",
" INSERT INTO genre(name)\n",
" SELECT ?\n",
" WHERE NOT EXISTS (SELECT 1 FROM genre WHERE name = ?);\n",
" \"\"\"\n",
"\n",
"with open(\"genre_list\", \"r\") as fs: \n",
" for genre_value in fs.readlines():\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(ADD_GENRE_VALUE, (genre_value.strip(), genre_value.strip()))\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "63b16a41-88bf-4832-a26c-09180832f597",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['action', 'adventure', 'animation', 'biography', 'comedy', 'crime', 'cult movie', 'disney', 'documentary', 'drama', 'erotic', 'family', 'fantasy', 'film-noir', 'gangster', 'gay and lesbian', 'history', 'horror', 'military', 'music', 'musical', 'mystery', 'nature', 'neo-noir', 'period', 'pixar', 'road movie', 'romance', 'sci-fi', 'short', 'spy', 'super hero', 'thriller', 'visually stunning', 'war', 'western']\n"
]
}
],
"source": [
"def all_genres():\n",
" ALL_GENRE = \"\"\"\n",
" SELECT * from genre \n",
" \"\"\" \n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(ALL_GENRE)\n",
" all_genre = [i[1] for i in db.fetchall()]\n",
" \n",
" return all_genre\n",
"\n",
"print(all_genres())\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "89b20b5f-34aa-4490-a4c0-c186c9fa30bd",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"36\n"
]
}
],
"source": [
"def search_genre_id(genre_name):\n",
" GENRE_QUERY = \"\"\"\n",
" select id from genre\n",
" where name = ?\n",
" \"\"\"\n",
" try:\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(GENRE_QUERY,(genre_name,))\n",
" genre_id = db.fetchone()[0]\n",
" return int(genre_id)\n",
" except:\n",
" return int(0)\n",
"\n",
"\n",
"def search_medium_id(genre_name):\n",
" GENRE_QUERY = \"\"\"\n",
" select id from genre\n",
" where medium = ?\n",
" \"\"\"\n",
" try:\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(GENRE_QUERY,(genre_name,))\n",
" genre_id = db.fetchone()[0]\n",
" return int(genre_id)\n",
" except:\n",
" return int(0)\n",
"\n",
"print(search_genre_id(genre_name=\"action\"))\n",
"print(search_genre_id(genre_name=\"western\"))"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "c70d91a5-7855-465d-a4a6-daebc065ee37",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0-John-Smith\n",
"1-James-Johnson\n",
"2-William-Williams\n",
"3-Michael-Brown\n",
"4-David-Davis\n",
"5-Richard-Miller\n",
"6-Joseph-Wilson\n",
"7-Charles-Moore\n",
"8-Thomas-Taylor\n",
"9-Daniel-Anderson\n",
"10-Paul-Thomas\n",
"11-Mark-Jackson\n",
"12-Donna-White\n",
"13-Michelle-Harris\n",
"14-Laura-Martin\n",
"15-Sara-Thompson\n",
"16-Ana-Garcia\n",
"17-Carlos-Rodriguez\n",
"18-Maria-Martinez\n",
"19-Jose-Hernandez\n",
"20-Luis-Lopez\n",
"21-Rosa-Gonzalez\n",
"22-Pedro-Perez\n",
"23-Miguel-Sanchez\n",
"24-Juan-Ramirez\n",
"25-Ana-Flores\n",
"26-Isabella-Cruz\n",
"27-Victor-Rivera\n",
"28-Kevin-Lee\n",
"29-Brian-Walker\n",
"30-Emily-Hall\n",
"31-Ryan-Allen\n",
"32-Aaron-Young\n",
"33-Jeffrey-King\n",
"34-Joshua-Wright\n",
"35-Brandon-Scott\n",
"36-Frank-Turner\n",
"37-Gregory-Carter\n",
"38-Samuel-Phillips\n",
"39-Chris-Evans\n",
"40-Anthony-Collins\n",
"41-Eric-Stewart\n",
"42-Frank-Snyder\n",
"43-Thomas-Baker\n",
"44-Jeremy-Nelson\n",
"45-Steven-Roberts\n",
"46-Edward-Campbell\n",
"47-Ryan-Miller\n",
"48-Jacob-Davis\n",
"49-David-Garcia\n",
"50-Sophia-Rodriguez\n",
"51-Emma-Martinez\n",
"52-Noah-Hernandez\n",
"53-Ava-Lopez\n",
"54-Ethan-Gonzalez\n",
"55-Mia-Perez\n",
"56-William-Sanchez\n",
"57-James-Ramirez\n",
"58-Olivia-Flores\n",
"59-Lucas-Cruz\n",
"60-Isabella-Rivera\n",
"61-David-Lee\n",
"62-Sophie-Walker\n",
"63-Matthew-Hall\n",
"64-Emma-Allen\n",
"65-Ryan-Young\n",
"66-Ava-King\n",
"67-Ethan-Wright\n",
"68-Mia-Scott\n",
"69-William-Turner\n",
"70-James-Carter\n",
"71-Olivia-Phillips\n",
"72-Lucas-Evans\n",
"73-Sophie-Collins\n",
"74-Noah-Stewart\n",
"75-Ava-Snyder\n",
"76-Ethan-Baker\n",
"77-Mia-Nelson\n",
"78-Noah-Roberts\n",
"79-Emma-Campbell\n",
"80-William-Miller\n",
"81-James-Davis\n",
"82-Olivia-Garcia\n",
"83-Lucas-Rodriguez\n",
"84-Sophie-Martinez\n",
"85-Noah-Hernandez\n",
"86-Ava-Lopez\n",
"87-Ethan-Gonzalez\n",
"88-Mia-Perez\n",
"89-William-Sanchez\n",
"90-James-Ramirez\n",
"91-Olivia-Flores\n",
"92-Lucas-Cruz\n",
"93-Isabella-Rivera\n",
"94-David-Lee\n",
"95-Sophie-Walker\n",
"96-Matthew-Hall\n",
"97-Emma-Allen\n",
"98-Ryan-Young\n",
"99-Ava-King\n",
"100-Ethan-Wright\n",
"101-Mia-Scott\n",
"102-William-Turner\n",
"103-James-Carter\n",
"104-Olivia-Phillips\n",
"105-Lucas-Evans\n",
"106-Sophie-Collins\n",
"107-Noah-Stewart\n",
"108-Ava-Snyder\n",
"109-Ethan-Baker\n",
"110-Mia-Nelson\n",
"111-Noah-Roberts\n",
"112-Emma-Campbell\n",
"113-William-Miller\n",
"114-James-Davis\n",
"115-Olivia-Garcia\n",
"116-Lucas-Rodriguez\n",
"117-Sophie-Martinez\n",
"118-Noah-Hernandez\n",
"119-Ava-Lopez\n",
"120-Ethan-Gonzalez\n",
"121-Mia-Perez\n",
"122-William-Sanchez\n",
"123-James-Ramirez\n",
"124-Olivia-Flores\n",
"125-Lucas-Cruz\n",
"126-Isabella-Rivera\n",
"127-David-Lee\n",
"128-Sophie-Walker\n",
"129-Matthew-Hall\n",
"130-Emma-Allen\n",
"131-Ryan-Young\n",
"132-Ava-King\n",
"133-Ethan-Wright\n",
"134-Mia-Scott\n",
"135-William-Turner\n",
"136-James-Carter\n",
"137-Olivia-Phillips\n",
"138-Lucas-Evans\n",
"139-Sophie-Collins\n",
"140-Noah-Stewart\n",
"141-Ava-Snyder\n",
"142-Ethan-Baker\n",
"143-Mia-Nelson\n",
"144-Noah-Roberts\n",
"145-Emma-Campbell\n",
"146-William-Miller\n",
"147-James-Davis\n",
"148-Olivia-Garcia\n",
"149-Lucas-Rodriguez\n",
"150-Sophie-Martinez\n",
"151-Noah-Hernandez\n",
"152-Ava-Lopez\n",
"153-Ethan-Gonzalez\n",
"154-Mia-Perez\n",
"155-William-Sanchez\n",
"156-James-Ramirez\n",
"157-Olivia-Flores\n",
"158-Lucas-Cruz\n",
"159-Isabella-Rivera\n",
"160-David-Lee\n",
"161-Sophie-Walker\n",
"162-Matthew-Hall\n",
"163-Emma-Allen\n",
"164-Ryan-Young\n",
"165-Ava-King\n",
"166-Ethan-Wright\n",
"167-Mia-Scott\n",
"168-William-Turner\n",
"169-James-Carter\n",
"170-Olivia-Phillips\n",
"171-Lucas-Evans\n",
"172-Sophie-Collins\n",
"173-Noah-Stewart\n",
"174-Ava-Snyder\n",
"175-Ethan-Baker\n",
"176-Mia-Nelson\n",
"177-Noah-Roberts\n",
"178-Emma-Campbell\n",
"179-William-Miller\n",
"180-James-Davis\n",
"181-Olivia-Garcia\n",
"182-Lucas-Rodriguez\n",
"183-Sophie-Martinez\n",
"184-Noah-Hernandez\n",
"185-Ava-Lopez\n",
"186-Ethan-Gonzalez\n",
"187-Mia-Perez\n",
"188-William-Sanchez\n",
"189-James-Ramirez\n",
"190-Olivia-Flores\n",
"191-Lucas-Cruz\n",
"192-Isabella-Rivera\n",
"193-David-Lee\n",
"194-Sophie-Walker\n",
"195-Matthew-Hall\n",
"196-Emma-Allen\n",
"197-Ryan-Young\n",
"198-Ava-King\n",
"199-Ethan-Wright\n",
"200-Mia-Scott\n",
"201-William-Turner\n",
"202-James-Carter\n",
"203-Olivia-Phillips\n",
"204-Lucas-Evans\n",
"205-Sophie-Collins\n",
"206-Noah-Stewart\n",
"207-Ava-Snyder\n",
"208-Ethan-Baker\n",
"209-Mia-Nelson\n",
"210-Noah-Roberts\n",
"211-Emma-Campbell\n",
"212-William-Miller\n",
"213-James-Davis\n",
"214-Olivia-Garcia\n",
"215-Lucas-Rodriguez\n",
"216-Sophie-Martinez\n",
"217-Noah-Hernandez\n",
"218-Ava-Lopez\n",
"219-Ethan-Gonzalez\n",
"220-Mia-Perez\n",
"221-William-Sanchez\n",
"222-James-Ramirez\n",
"223-Olivia-Flores\n",
"224-Lucas-Cruz\n",
"225-Isabella-Rivera\n",
"226-David-Lee\n",
"227-Sophie-Walker\n",
"228-Matthew-Hall\n",
"229-Emma-Allen\n",
"230-Ryan-Young\n",
"231-Ava-King\n",
"232-Ethan-Wright\n",
"233-Mia-Scott\n",
"234-William-Turner\n",
"235-James-Carter\n",
"236-Olivia-Phillips\n",
"237-Lucas-Evans\n",
"238-Sophie-Collins\n",
"239-Noah-Stewart\n",
"240-Ava-Snyder\n",
"241-Ethan-Baker\n",
"242-Mia-Nelson\n",
"243-Noah-Roberts\n",
"244-Emma-Campbell\n",
"245-William-Miller\n",
"246-James-Davis\n",
"247-Olivia-Garcia\n",
"248-Lucas-Rodriguez\n",
"249-Sophie-Martinez\n",
"250-Noah-Hernandez\n",
"251-Ava-Lopez\n",
"252-Ethan-Gonzalez\n",
"253-Mia-Perez\n",
"254-William-Sanchez\n",
"255-James-Ramirez\n",
"256-Olivia-Flores\n",
"257-Lucas-Cruz\n",
"258-Isabella-Rivera\n",
"259-David-Lee\n",
"260-Sophie-Walker\n",
"261-Matthew-Hall\n",
"262-Emma-Allen\n",
"263-Ryan-Young\n",
"264-Ava-King\n",
"265-Ethan-Wright\n",
"266-Mia-Scott\n",
"267-William-Turner\n",
"268-James-Carter\n",
"269-Olivia-Phillips\n",
"270-Lucas-Evans\n",
"271-Sophie-Collins\n",
"272-Noah-Stewart\n",
"273-Ava-Snyder\n",
"274-Ethan-Baker\n",
"275-Mia-Nelson\n",
"276-Noah-Roberts\n",
"277-Emma-Campbell\n",
"278-William-Miller\n",
"279-James-Davis\n",
"280-Olivia-Garcia\n",
"281-Lucas-Rodriguez\n",
"282-Sophie-Martinez\n",
"283-Noah-Hernandez\n",
"284-Ava-Lopez\n",
"285-Ethan-Gonzalez\n",
"286-Mia-Perez\n",
"287-William-Sanchez\n",
"288-James-Ramirez\n",
"289-Olivia-Flores\n",
"290-Lucas-Cruz\n",
"291-Isabella-Rivera\n",
"292-David-Lee\n",
"293-Sophie-Walker\n",
"294-Matthew-Hall\n",
"295-Emma-Allen\n",
"296-Ryan-Young\n",
"297-Ava-King\n",
"298-Ethan-Wright\n",
"299-Mia-Scott\n",
"300-William-Turner\n",
"301-James-Carter\n",
"302-Olivia-Phillips\n",
"303-Lucas-Evans\n",
"304-Sophie-Collins\n",
"305-Noah-Stewart\n",
"306-Ava-Snyder\n",
"307-Ethan-Baker\n",
"308-Mia-Nelson\n",
"309-Noah-Roberts\n",
"310-Emma-Campbell\n",
"311-William-Miller\n",
"312-James-Davis\n",
"313-Olivia-Garcia\n",
"314-Lucas-Rodriguez\n",
"315-Sophie-Martinez\n",
"316-Noah-Hernandez\n",
"317-Ava-Lopez\n",
"318-Ethan-Gonzalez\n",
"319-Mia-Perez\n",
"320-William-Sanchez\n",
"321-James-Ramirez\n",
"322-Olivia-Flores\n",
"323-Lucas-Cruz\n",
"324-Isabella-Rivera\n",
"325-David-Lee\n",
"326-Sophie-Walker\n"
]
}
],
"source": [
"import pandas as pd\n",
"usecols = [\"Name\", \"Vorname\"]\n",
"air = pd.read_csv(\"regie_name.csv\", usecols=usecols)\n",
"\n",
"for count, (i, b) in enumerate(zip(air[\"Name\"], air[\"Vorname\"])):\n",
" print(count, b, i, sep=\"-\")\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee4b5dee-6b41-49eb-8d75-9db50d20fdef",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+19
View File
@@ -0,0 +1,19 @@
FROM python:3.9-slim-bookworm
WORKDIR /app
# Install build dependencies for mariadb-connector-c
RUN apt-get update && apt-get install -y \
default-libmysqlclient-dev \
gcc \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5082
CMD ["python3", "main.py"]
+116 -5
View File
@@ -1,7 +1,118 @@
Folgende Punkte # Movie-DB
- erstellen eine SQlite DB Eine webbasierte Anwendung zur Verwaltung einer persönlichen Filmsammlung.
- Die DB soll folgende Tabellen haben Genre, MyList,
- Die Anzeige soll über Flask angezeigt werden
Weitere Schritte folgen !!! ## Funktionsumfang (Features)
* **Benutzer-System:** Vollständiges Registrierungs- und Login-System, sodass jeder Benutzer seine eigene, private Filmsammlung verwalten kann.
* **Persönliche Filmlisten:** Jeder Benutzer sieht und durchsucht nur die Filme, die er selbst hinzugefügt hat.
* **Filme hinzufügen:** Filme können manuell oder durch ein automatisiertes Scraping von IMDb hinzugefügt werden.
* **Umfassende Suche:** Eine globale Suchleiste ermöglicht die gleichzeitige Suche nach Titel, Genre und Regisseur.
* **Paginierung:** Die Filmliste ist in Seiten aufgeteilt, um auch bei großen Sammlungen übersichtlich zu bleiben.
* **Sichere Verbindung:** Die Anwendung wird über HTTPS mit einem selbst-signierten SSL-Zertifikat bereitgestellt.
* **Containerisierung:** Die gesamte Anwendung ist mit Docker und Docker Compose containerisiert, was die Bereitstellung vereinfacht.
* **Automatisierte Bereitstellung:** Ein Ansible-Playbook automatisiert das Deployment der Anwendung.
## Voraussetzungen
Stellen Sie sicher, dass die folgende Software auf Ihrem System installiert ist:
* Python 3
* pip
* Docker
* Docker Compose
* Ansible
## Inbetriebnahme
Es gibt drei Möglichkeiten, die Anwendung zu starten.
### 1. Direkter Start mit Python
Dies ist die klassische Methode für die lokale Entwicklung.
1. **Abhängigkeiten installieren:**
```bash
pip install -r requirements.txt
```
2. **Anwendung starten:**
```bash
python3 main.py
```
3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`. Sie müssen eine Browser-Warnung aufgrund des selbst-signierten Zertifikats akzeptieren.
### 2. Start mit Docker
Diese Methode kapselt die Anwendung in einem Container.
1. **Docker-Image erstellen:**
```bash
docker build -t movie-db-app .
```
2. **Docker-Container starten:**
```bash
docker run -p 5082:5082 movie-db-app
```
3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`.
### 3. Start mit Ansible und Docker Compose (Empfohlen)
Dies ist die automatisierte Methode, die einen sauberen Deployment-Prozess sicherstellt.
1. **Ansible Docker-Sammlung installieren (falls noch nicht geschehen):**
```bash
ansible-galaxy collection install community.docker
```
2. **Ansible-Playbook ausführen:**
```bash
ansible-playbook start.yml
```
(Möglicherweise müssen Sie Ihr `sudo`-Passwort eingeben.)
3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`.
## Projektstruktur
Die wichtigsten Dateien und Ordner im Überblick:
```
.
├── Dockerfile
├── docker-compose.yml
├── main.py
├── moviedb_func.py
├── movie_db.db
├── README.md
├── requirements.txt
├── start.yml
├── cert.pem
├── key.pem
└── templates/
├── base.html
├── index.html
├── login.html
└── register.html
```
* `main.py`: Die Hauptdatei der Flask-Anwendung. Sie enthält die Routen und die Anwendungslogik.
* `moviedb_func.py`: Enthält die gesamte Geschäftslogik, Datenbankfunktionen und die `User`-Klasse.
* `templates/`: Der Ordner für alle HTML-Templates.
* `movie_db.db`: Die SQLite-Datenbankdatei.
* `Dockerfile`: Die Bauanleitung für das Docker-Image der Anwendung.
* `docker-compose.yml`: Definiert die Anwendung als Docker-Dienst für eine einfache Verwaltung.
* `start.yml`: Das Ansible-Playbook für die automatisierte Bereitstellung.
* `requirements.txt`: Liste der Python-Abhängigkeiten.
* `cert.pem`, `key.pem`: Das selbst-signierte SSL-Zertifikat und der private Schlüssel.
## Benutzung
1. **Registrieren:** Erstellen Sie auf der Registrierungsseite einen neuen Benutzeraccount.
2. **Einloggen:** Melden Sie sich mit Ihren neuen Zugangsdaten an.
3. **Filme hinzufügen:** Nutzen Sie den "Film hinzufügen"-Button, um Filme entweder durch die IMDb-Suche oder manuell zu Ihrer Sammlung hinzuzufügen.
4. **Suchen:** Verwenden Sie die Suchleiste, um Ihre Sammlung nach Titeln, Genres oder Regisseuren zu durchsuchen.
## Mögliche zukünftige Erweiterungen
* **Bearbeiten & Löschen:** Funktionen zum Bearbeiten und Löschen von bereits hinzugefügten Filmen.
* **Detailseite:** Eine eigene Seite für jeden Film mit mehr Details (Schauspieler, Handlung, etc.).
* **Sortieroptionen:** Die Möglichkeit, die Filmliste nach verschiedenen Kriterien zu sortieren.
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIFCTCCAvGgAwIBAgIUDplU7xn71S8Rb5v7dXQ9cZmD1S4wDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDgyNjExNTIwMVoXDTI2MDgy
NjExNTIwMVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA0J1PNqsrg+BL/zy7nTaRwfP96gGGvnQtHFHBifX7qF6e
bXKvxU77tGE9D0i5t36N2ktMl6PGI2CmhFz5mkcVrXnfWGLoYV2jLlYQOdKvwFJo
7R4QuGa8nLHLdGUkhSSgRxkol9rHUcok5ZpfA1iVFbrpgqel3Lmh0h3qXx7YiPTo
Y8ZhFQDoF4TXJYGrSC2BlAtbOSwT/L/n983VIA3dugzQ+3H78OWmfGcCB8Q8d5hN
FP+igWj/29U3vCmvlgXpzaM+X1TtL9uxqm/GWw/+jIj4Wn+2mxhMFS2B/OeWEW5y
oTYvZywK/eap9ieyKpzi+lND3eTvD+icpj1hFBt5qa+R9P1bj369q6IMSxNrf5YJ
cDesywy5MIIAPN+21nNYi13ZSwSnfEI/13klBk0rdhBulDAPGvH1DZEAc4ePagMQ
uhJtW6wl50yNJADxszAa1mbIIk/v7r+vPt32XTylqlaI3koU2XjjmyXcsAFLdgcS
ymCtxNWe37U58qb1J3QKDbaskq6kJiJBWZlASa3ANp19UdWvAnuznhhfXws0cV6b
qFoeKKkyWOsPln+Ifuw1VONJOMJmoT4L+kHuKnMDqdusuGdIlJXfeW00vMLIRx8O
8j8dUHfFqWRnE98VO9WZlsbagYgW96n9zyeZvfW3VgncrqMVZYC2YadbSH+hG40C
AwEAAaNTMFEwHQYDVR0OBBYEFIp7yQD9oKNeYS2hFMBzgIQWoZzGMB8GA1UdIwQY
MBaAFIp7yQD9oKNeYS2hFMBzgIQWoZzGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggIBAHQpvGXNoeHjmVxuMmZSuiccOdfJXiWBTM7g7Eqq0nzTuzK/
IvmBkTxK8BG1j9X+9vBDJ3KZAIMVviepqxUNMGMQNa9DcVnOxaB01UdRqM9hh+jg
EkBgN325BgrX5Vh/HS7MX2VboM+WmBcfWtt3afKv2Tv2mzZ6bPPDdbH/SJouxfXD
Ea+n3IXniskmY7kUVTy7uRQUAJXyLulcrXAJgOwws80JEXrrjZwcpt9PxzzxXu4c
TUZQ1A5FgHgXecYM1WJA9nQr7NH9TewssKaoYOEltwOxbqc0bxAOtFX02dSTpRY/
0LwLC1Nqm34c96CX1t/WLfWf/RY2j5FoqFKxOY6CnpCW4DXjMZ3OEPmikT6XJ85G
q9LB4AqT/G1cyQzoT78SwDk6Ln3ahzP0MiaQeZ/KBJbqnfxOtcDWAmRTtIPYpMYH
eW8K+Y0Z3UvR+FqawuR5pvz5T/XYC/tutK4+KLCecorruT/Ye5qEALb+XcugU5iE
3FeZ/iAUxn8UKAGtfogQHYMOZ5lllAgRCKYhEYp28Rlkrk9LBXPzMimUfPZ2RSwx
XSkTYZEFEMw+4YQdOGqesW/hGpG6Ojmb/bmqG5TAzTdFUXsc6YgrJIP7lCIkEBnb
jF+S8Yb9Gq1s3ohiyI71kdVoOgWUGbqkkQ9xEZQzRzB1zcOLKelpdI7BEkjE
-----END CERTIFICATE-----
+10
View File
@@ -0,0 +1,10 @@
services:
web:
build: .
container_name: movie-db-web
ports:
- "5082:5082"
volumes:
- .:/app
restart: always
# command: python3 main.py # Use this if you want to run in foreground for debugging
+59
View File
@@ -0,0 +1,59 @@
# Benötigt die Installation von 'requests' und 'beautifulsoup4'
# pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
import sys
import re
def suche_film_url(film_titel):
"""
Sucht auf IMDb nach einem Film und gibt die deutsche URL der Filmseite zurück.
Args:
film_titel: Der Titel des Films, nach dem gesucht werden soll.
Returns:
Die deutsche URL der Filmseite oder None, wenn nichts gefunden wurde.
"""
such_url = f"https://www.imdb.com/find?q={film_titel.replace(' ', '+')}"
# Header, um die deutsche Seite zu bevorzugen
headers = {
'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
}
try:
response = requests.get(such_url, headers=headers)
response.raise_for_status() # Löst eine Ausnahme für HTTP-Fehler aus
soup = BeautifulSoup(response.text, 'html.parser')
# Finden des ersten Links im Titel-Ergebnisbereich
ergebnis_bereich = soup.find('section', {'data-testid': 'find-results-section-title'})
if ergebnis_bereich:
link_tag = ergebnis_bereich.find('a', href=re.compile(r'/title/tt\d+'))
if link_tag and 'href' in link_tag.attrs:
pfad = link_tag['href']
# Extrahieren der Titel-ID (z.B. tt0133093)
match = re.search(r'(tt\d+)', pfad)
if match:
titel_id = match.group(1)
return f"https://www.imdb.com/de/title/{titel_id}"
except requests.exceptions.RequestException as e:
print(f"Ein Fehler bei der Anfrage ist aufgetreten: {e}")
return None
return None
if __name__ == "__main__":
if len(sys.argv) > 1:
film_name = " ".join(sys.argv[1:])
url = suche_film_url(film_name)
if url:
print(f"URL für '{film_name}' gefunden: {url}")
else:
print(f"Konnte keinen Film mit dem Titel '{film_name}' finden.")
else:
print("Benutzung: python imdb_suche.py <film_titel>")
+52
View File
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDQnU82qyuD4Ev/
PLudNpHB8/3qAYa+dC0cUcGJ9fuoXp5tcq/FTvu0YT0PSLm3fo3aS0yXo8YjYKaE
XPmaRxWted9YYuhhXaMuVhA50q/AUmjtHhC4Zrycsct0ZSSFJKBHGSiX2sdRyiTl
ml8DWJUVuumCp6XcuaHSHepfHtiI9OhjxmEVAOgXhNclgatILYGUC1s5LBP8v+f3
zdUgDd26DND7cfvw5aZ8ZwIHxDx3mE0U/6KBaP/b1Te8Ka+WBenNoz5fVO0v27Gq
b8ZbD/6MiPhaf7abGEwVLYH855YRbnKhNi9nLAr95qn2J7IqnOL6U0Pd5O8P6Jym
PWEUG3mpr5H0/VuPfr2rogxLE2t/lglwN6zLDLkwggA837bWc1iLXdlLBKd8Qj/X
eSUGTSt2EG6UMA8a8fUNkQBzh49qAxC6Em1brCXnTI0kAPGzMBrWZsgiT+/uv68+
3fZdPKWqVojeShTZeOObJdywAUt2BxLKYK3E1Z7ftTnypvUndAoNtqySrqQmIkFZ
mUBJrcA2nX1R1a8Ce7OeGF9fCzRxXpuoWh4oqTJY6w+Wf4h+7DVU40k4wmahPgv6
Qe4qcwOp26y4Z0iUld95bTS8wshHHw7yPx1Qd8WpZGcT3xU71ZmWxtqBiBb3qf3P
J5m99bdWCdyuoxVlgLZhp1tIf6EbjQIDAQABAoICAD8OHWBX0ppU1IO8drTpRptK
RYhSl8i/UtdJeudZW4yjtN0+GhTYb8owzgTtpXzqGrRNqEP5c61DzHEbrTJNpCbA
PJr5omWzqNwblWH05byK7D4npUQngYEXWHjBDY3U6UE0QInNzgtRv19LDsbzInTj
/yrHw6F41omtEydXKnoTFJ772mI/2Ocq9uDWVdBGOPFnkVYxlYUC+Hl1VKMQreFL
eGtYI5/Huk/Fzt5MGT7I4RukvRjChPuMcgFE1FbTSL0oR3UHNX4OuJ/ckyb5KaaO
zAqGqcotSOnpzG2GDl0Mdkd2GlPkAxqFev/1NawdqZmiS1Qpm3zCgX7Z9JrgRyBP
nMKMdH/+Yi6sQ16BLuq1/wBSoN4hh5AIjbS2SRV8P0RR9TfuKP0G3UZeXEJVu7VZ
LjMqfNc5Frco8iJdm4dv9bAjMgDgKBwRzECTSPcUByS7H72v7I81X9HDaNlMk9Py
B0sy5mduihQE4ZeSUVloiq/Mm8K3/6lPuC94+8hfZ7ZhmMTsXkeHtyo+tNNEHtFS
wnF96zzUE5l7quCmqPzz5l5ll9wYAjMXr48Fv9C5E5QshoYhmzBrF6+Kn315bSa6
PHf3v2nJ//lJHyJR6o/MWLAdW61nbvnlLbwBUt0blv+PUnoOFKQhDcHteU7Ht5zi
vyzJKEGK0nL+hScit94dAoIBAQD+ttlWLHVo32jX83cVLNQFo5TVQJlbf05yZUqm
rEVu+kH59JAUPYxypiQ+CWc3OMFqDHiSrP9VdL1HSfZURS3K8hw25ThWn5jXEoWF
SkyCWBcPp8EyCwW14DUCp45VKyZlNpM/halT+eG04lElEilUHDMbinz01R2Gwkrk
zgirxRBpAU1OMnvCTTjwmLUXCKvHBDek4AZBY0Gw9eWgir+If6Gyn88Nr2N1ZtFt
+kkHrLgyo+nanOu1N/02RC3Je3lSxI7VsLP0Lt/zs5Qag8r/a7lEDUI69eQbh/Rh
29NaqUyPOQn8DjfPzB8a0F7qXbcB94CYWfoKZ/RkdJ9dj4mTAoIBAQDRquN/ftwE
CBfgwE5BJNZuofj733LBrnvZH51UOMvnoTxPDdCyf684un8qylmYu4WIV5VvWZUH
G3gOw+zmmpEOHerh8/G7X9FPST8txTlAxlFBVN0iXMyU8SDypw2XF+CKcq0goz2s
/XA0b9NdtF9zziYsZSj+hR5E1Nf16GX2SeczSlaxPRTZ1pxqRbmC+1iE+yJuXcQ2
sJgr2zI9jUf0zGamYPqpKvZDKLclZSJAIYTLE/nUMR87QqgLU90x3bBeRR4uLnD3
yemtFuJDRcSZa+4j/m5fmLZvA2wpvZvvd70DC3xfh6RKZYyzj26rHgK3qnY9owzS
q44qLTbNH3pfAoIBAQCHmjy2XdDb1iRw41LAWS8/GUlBBrUeOvY+fKJzfT8xx9Wc
BpE11VaSKtUcmdDzWynNFXqTS7pbelzSORQE5Rdt9dsDdqC5rRmnVlrzDQ0ST2dn
lD2MMMYHf6d5FE74FJLKS3W02am95Ug+DkZ43+RUvPaQcPf5OAQSHBnXGcEQAUet
P14yhTnRxV7M599wtdMNjrc49XMfGIJ+fUqUX4l8V8Zz3BRVCsx+UhCo5hg/F7qw
awcytVvfDEGnxEBEBZIfeDk6lmoXU2UyzqXIP+WweV8WVaCu1v1rksON3YP1vi/e
B9XC9KbN37OniMsfJmkpiORTN4OgSpuXZ2b4XlQ1AoIBAQCsVFMMdc6MmcJWfnlT
Xd3JrOOi0fPWcX1l5nA/yzb9OS8Cow5HyF7St6bSGuMtf+OvPp2amfy9jLxPFoZe
u+hBvCFUelbTdVvqWP/OtO3r6ZTbp87h8XhNaytzhg7Lg1qRMNRPoRyOVPimgMsv
l5Nk8am6j6L0H6HTXUXr3on/tpqLM6yxnMzIh3akeo6Rs9j75BZWtEcT1G4ejdlr
cfYUpPQyCD++T4t9g7eUyt0t7N7is5aB66YJ5S2Nse56kAXoVdMKji1X3ONt2wM5
29xNZcKkXvmFJVW9RQjf/fJvDvGR0Rz2v9wvMexFobyKdO4y26631o4xkcQVsnf6
fMbtAoIBAE6rOneLYxM8X6hJ7NoyVIwUH52WKuqWn4SFHU42C8SGeUTxFtr6ze+L
z0ZYTowYufMOUMjtS4nwa2kWkohPJccDfJHG1NJf6WA80Yt/qQItsjv/TGItxDE0
4j47r+h1NGj01xzJHe0QG38NWnnpw0hHFSG5q73qWFyYSWs/unz+HMSGsg+KtpPT
K7RJMx2ROUXlMku8TmEWO0EfwhVmTp2g/1XoH7DvtAIv5GLiUL+pYp9qs7LtSVI/
3WYsk3Op/x5VRG/V2hByPf25IrpLS3eoDB3uPeeIHb0rXx0QdMr2rElGg4vgcLUw
krvHLxyb9R+o1UzlvYkUiF+lOMVpLB8=
-----END PRIVATE KEY-----
+96 -28
View File
@@ -1,59 +1,127 @@
import flask import flask
from flask import render_template, redirect, url_for, flash
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
import moviedb_func import moviedb_func
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.secret_key = "Start1234!" app.secret_key = "Start1234!"
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
@login_manager.user_loader
def load_user(user_id):
return moviedb_func.get_user_by_id(user_id)
@app.route("/register", methods=["GET", "POST"])
def register():
if flask.request.method == "POST":
username = flask.request.form.get("username")
password = flask.request.form.get("password")
if moviedb_func.get_user_by_username(username):
flash("Benutzername existiert bereits")
return redirect(url_for("register"))
moviedb_func.add_user(username, password)
return redirect(url_for("login"))
return render_template("register.html")
@app.route("/login", methods=["GET", "POST"])
def login():
if flask.request.method == "POST":
username = flask.request.form.get("username")
password = flask.request.form.get("password")
user = moviedb_func.get_user_by_username(username)
if user and user.check_password(password):
login_user(user)
return redirect(url_for("index"))
flash("Ungültiger Benutzername oder Passwort")
return render_template("login.html")
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("login"))
@app.route("/", methods=["GET", "POST"])
@login_required
def index():
page = flask.request.args.get('page', 1, type=int)
per_page = 20
search_query = None
if flask.request.method == "POST":
search_query = flask.request.form.get("search")
else:
search_query = flask.request.args.get("search")
data_all, total_pages = moviedb_func.show_movie_list(user_id=current_user.id, search_query=search_query, page=page, per_page=per_page)
return render_template(
"index.html",
sitename="Meine Movieliste !!!",
data_all=data_all,
total_pages=total_pages,
current_page=page,
search_query=search_query
)
@app.get("/add_movie") @app.get("/add_movie")
def add_movie(): def add_movie():
return flask.render_template( return render_template(
"add_movie.html", "add_movie.html",
sitename="Meine Movieliste !!!", sitename="Meine Movieliste !!!",
url="output", url="output",
select_movie="add_movie", select_movie="add_movie",
select_genre="add_genre", select_genre="add_genre",
select_medium="add_medium", select_medium="add_medium",
select_regie="add_regie",
data=moviedb_func.all_select(), data=moviedb_func.all_select(),
data_medium=moviedb_func.all_select(what_select="medium"), data_medium=moviedb_func.all_select(what_select="medium")
data_regie=moviedb_func.all_select(what_select="regie"),
data_all=moviedb_func.show_movie_list()
) )
@app.post("/output") @app.post("/output")
@login_required
def csv_output(): def csv_output():
select_add_movie = flask.request.form["add_movie"] select_add_movie = flask.request.form["add_movie"]
select_genre = flask.request.form["add_genre"]
select_genre_id = moviedb_func.search_id(
search_name=flask.request.form["add_genre"])
select_medium = flask.request.form["add_medium"] select_medium = flask.request.form["add_medium"]
select_medium_id = moviedb_func.search_id( select_medium_id = moviedb_func.search_id(
search_name=flask.request.form["add_medium"], select_from="medium", select_where="medium") search_name=select_medium, select_from="medium", select_where="medium")
select_regie = flask.request.form["add_regie"]
select_regie_id = moviedb_func.search_id(
search_name=flask.request.form["add_regie"], select_from="regie", select_where="regie")
moviedb_func.add_movie_to_list(movie_name=select_add_movie, regie_id=select_regie_id, if moviedb_func.scrape_and_add_movie(movie_name=select_add_movie,
medium_id=select_medium_id, genre_id=select_genre_id) medium_id=select_medium_id, user_id=current_user.id):
return redirect(url_for('index'))
else:
return redirect(url_for('add_movie_manually', movie_name=select_add_movie, medium_id=select_medium_id))
return flask.render_template( @app.get("/add_movie_manually")
"output.html", @login_required
sitename="Meine Movieliste !!!", def add_movie_manually():
add_movie=select_add_movie, movie_name = flask.request.args.get('movie_name')
add_genre=select_genre, medium_id = flask.request.args.get('medium_id')
add_medium=select_medium, return render_template(
add_regie=select_regie, "add_movie_manually.html",
add_genre_id=select_genre_id, sitename="Manuelle Eingabe",
add_medium_id=select_medium_id, movie_name=movie_name,
add_regie_id=select_regie_id medium_id=medium_id
# data=modify_csv.read_csv_file(),
# sum_all=modify_csv.sum_all(),
) )
@app.post("/save_manual")
@login_required
def save_manual():
movie_name = flask.request.form["movie_name"]
medium_id = flask.request.form["medium_id"]
director_name = flask.request.form["director_name"]
release_year = flask.request.form["release_year"]
genre_name = flask.request.form["genre_name"]
moviedb_func.add_manual_movie(movie_name=movie_name, medium_id=int(medium_id), director_name=director_name, release_year=int(release_year), genre_name=genre_name, user_id=current_user.id)
return redirect("/")
if __name__ == "__main__": if __name__ == "__main__":
app.run(port=5082, host="0.0.0.0", debug=True) moviedb_func.create_movie_database()
app.run(port=5082, host="0.0.0.0", debug=True, ssl_context=('cert.pem', 'key.pem'))
Binary file not shown.
+275 -23
View File
@@ -2,17 +2,103 @@ import DBcm
from mariadb import ProgrammingError from mariadb import ProgrammingError
import pandas as pd import pandas as pd
import sqlite3 import sqlite3
import urllib.request
import json
import re
import ssl
import imdb_suche
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
def get_movie_details(imdb_url):
"""
Ruft eine IMDb-Filmseite auf und extrahiert den Regisseur und die Hauptdarsteller.
Args:
imdb_url (str): Die vollständige URL zur IMDb-Filmseite.
Returns:
dict: Ein Wörterbuch mit "director" und "actors" oder None bei einem Fehler.
"""
if not re.match(r"https://www.imdb.com/de/title/tt\d+/?", imdb_url):
print(f"Fehler: Ungültige IMDb-URL: {imdb_url}. Die URL sollte so aussehen: https://www.imdb.com/de/title/tt...")
return None
try:
# SSL-Kontext erstellen, um Zertifikatsüberprüfungsfehler zu umgehen
context = ssl._create_unverified_context()
# User-Agent setzen, um wie ein Browser auszusehen und Blockaden zu vermeiden
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
req = urllib.request.Request(imdb_url, headers=headers)
with urllib.request.urlopen(req, context=context) as response:
html_content = response.read().decode('utf-8')
# Suchen Sie nach dem strukturierten Datenblock (JSON-LD), der Filminformationen enthält
match = re.search(
r'<script type="application/ld\+json">(.*?)</script>', html_content, re.DOTALL)
if not match:
print("Fehler: Konnte den JSON-LD-Datenblock auf der Seite nicht finden.")
return None
json_data = json.loads(match.group(1))
# Extrahieren des Regisseurs
director = [d.get('name') for d in json_data.get("director", [])] if isinstance(
json_data.get("director"), list) else [json_data.get("director", {}).get("name")]
# Extrahieren der Schauspieler
actors = [a.get('name') for a in json_data.get("actor", [])]
# Extrahieren des Erscheinungsjahres
release_year = json_data.get("datePublished", "").split("-")[0]
# Extrahieren des Genres
genre = json_data.get("genre", [])
if isinstance(genre, list):
genre = ", ".join(genre)
return {
"director": [d for d in director if d],
"actors": [a for a in actors if a],
"release_year": release_year,
"genre": genre
}
except urllib.error.HTTPError as e:
print(f"HTTP-Fehler beim Abrufen der URL: {e.code} {e.reason}")
return None
except Exception as e:
print(f"Ein Fehler ist aufgetreten: {e}")
return None
def create_movie_database(db_name="movie_db.db"): def create_movie_database(db_name="movie_db.db"):
create_user_table = """
CREATE TABLE IF NOT EXISTS user (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username VARCHAR(64) NOT NULL UNIQUE,
password_hash VARCHAR(128) NOT NULL
);
"""
create_my_list = """ create_my_list = """
create table if not exists movie_list ( create table if not exists movie_list (
id integer not null primary key autoincrement, id integer not null primary key autoincrement,
titel varchar(64) not null, titel varchar(64) not null,
genre_id integer not null, genre_id integer not null,
regie_id integer not null, regie_id integer,
medium_id integer not null medium_id integer not null,
release_year integer,
user_id integer not null,
FOREIGN KEY (user_id) REFERENCES user (id)
) )
""" """
@@ -60,6 +146,7 @@ def create_movie_database(db_name="movie_db.db"):
""" """
with DBcm.UseDatabase(db_name) as db: with DBcm.UseDatabase(db_name) as db:
db.execute(create_user_table)
db.execute(create_my_list) db.execute(create_my_list)
db.execute(create_genre) db.execute(create_genre)
db.execute(create_regie) db.execute(create_regie)
@@ -104,7 +191,39 @@ def all_select(db_name="movie_db.db", what_select="genre"):
return "Wrong Value !!!" return "Wrong Value !!!"
def search_id(db_name="movie_db.db", search_name=str, select_from="genre", select_where="name"): def get_or_create_regie(director_name: str, db_name: str = "movie_db.db"):
ADD_REGIE_VALUE = """
INSERT INTO regie (surname, lastname )
SELECT ?, ?
WHERE NOT EXISTS
(SELECT surname, lastname
FROM regie
WHERE surname = ? AND lastname = ?);
"""
director_split = director_name.split(" ")
surname = director_split[0]
lastname = " ".join(director_split[1:])
with DBcm.UseDatabase(db_name) as db:
db.execute(ADD_REGIE_VALUE, (surname, lastname, surname, lastname))
db.execute("SELECT id FROM regie WHERE surname = ? AND lastname = ?", (surname, lastname))
regie_id = db.fetchone()[0]
return regie_id
def get_or_create_genre(genre_name: str, db_name: str = "movie_db.db"):
ADD_GENRE_VALUE = """
INSERT INTO genre(name)
SELECT ?
WHERE NOT EXISTS (SELECT 1 FROM genre WHERE name = ?);
"""
with DBcm.UseDatabase(db_name) as db:
db.execute(ADD_GENRE_VALUE, (genre_name, genre_name))
db.execute("SELECT id FROM genre WHERE name = ?", (genre_name,))
genre_id = db.fetchone()[0]
return genre_id
def search_id(search_name: str, select_from: str = "genre", select_where: str = "name", db_name: str = "movie_db.db"):
if select_from == "regie": if select_from == "regie":
split_search = search_name.split(" ") split_search = search_name.split(" ")
GENRE_QUERY = f"""select id from {select_from} GENRE_QUERY = f"""select id from {select_from}
@@ -125,43 +244,176 @@ def search_id(db_name="movie_db.db", search_name=str, select_from="genre", selec
return int(0) return int(0)
def add_movie_to_list(db_name="movie_db.db", movie_name=str, genre_id=int, regie_id=int, medium_id=int): def scrape_and_add_movie(movie_name: str, medium_id: int, user_id: int, db_name: str = "movie_db.db"):
url = imdb_suche.suche_film_url(movie_name)
if not url:
return False
details = get_movie_details(url + "/")
if not details:
return False
regie_id = None
if details.get("director"):
regie_id = get_or_create_regie(director_name=details["director"][0])
release_year = details.get("release_year")
genre_name = details.get("genre")
genre_id = None
if genre_name:
genre_id = get_or_create_genre(genre_name=genre_name)
SQL_PARAM = f""" SQL_PARAM = f"""
INSERT INTO movie_list (titel, genre_id, regie_id, medium_id ) INSERT INTO movie_list (titel, genre_id, regie_id, medium_id, release_year, user_id)
SELECT ?, ?, ?, ? VALUES (?, ?, ?, ?, ?, ?);
WHERE NOT EXISTS
(SELECT titel FROM movie_list WHERE titel = ?);
""" """
try: try:
with DBcm.UseDatabase(db_name) as db: with DBcm.UseDatabase(db_name) as db:
db.execute(SQL_PARAM, (movie_name.lower(), genre_id, db.execute(SQL_PARAM, (movie_name.lower(), genre_id,
regie_id, medium_id, movie_name.lower(),)) regie_id, medium_id, release_year, user_id,))
except ProgrammingError: except ProgrammingError as e:
raise ProgrammingError("Konnte nicht in die DB schreiben") print(f"Error adding movie: {e}")
return False
return True return True
def show_movie_list(db_name="movie_db.db"): def add_manual_movie(movie_name: str, medium_id: int, director_name: str, release_year: int, genre_name: str, user_id: int, db_name: str = "movie_db.db"):
regie_id = get_or_create_regie(director_name=director_name)
genre_id = get_or_create_genre(genre_name=genre_name)
SQL_PARAM = f"""
INSERT INTO movie_list (titel, genre_id, regie_id, medium_id, release_year, user_id)
VALUES (?, ?, ?, ?, ?, ?);
"""
try:
with DBcm.UseDatabase(db_name) as db:
db.execute(SQL_PARAM, (movie_name.lower(), genre_id,
regie_id, medium_id, release_year, user_id,))
except ProgrammingError as e:
print(f"Error adding movie: {e}")
return False
return True
def show_movie_list(db_name="movie_db.db", user_id=None, search_query=None, page=1, per_page=20):
"""Zeigt eine paginierte Liste von Filmen an, die nach einem Suchbegriff gefiltert werden können."""
# Basis-SQL-Abfrage
SQL_BASE = """FROM movie_list
INNER JOIN genre ON movie_list.genre_id=genre.id
INNER JOIN regie ON movie_list.regie_id=regie.id
INNER JOIN medium ON movie_list.medium_id=medium.id"""
# Filter-Bedingungen
params = []
where_clauses = []
if user_id:
where_clauses.append("movie_list.user_id = ?")
params.append(user_id)
if search_query:
search_param = f"%{search_query}%"
where_clauses.append("(titel LIKE ? OR genre.name LIKE ? OR (regie.surname || ' ' || regie.lastname) LIKE ?)")
params.extend([search_param, search_param, search_param])
SQL_WHERE = ""
if where_clauses:
SQL_WHERE = " WHERE " + " AND ".join(where_clauses)
# Verbindung zur Datenbank
db = sqlite3.connect(db_name)
# Gesamtzahl der Filme für die Paginierung ermitteln
COUNT_SQL = f"SELECT COUNT(movie_list.id) {SQL_BASE}{SQL_WHERE}"
total_movies = db.execute(COUNT_SQL, params).fetchone()[0]
total_pages = (total_movies + per_page - 1) // per_page
# Filme für die aktuelle Seite abrufen
offset = (page - 1) * per_page
SELECT_SQL = f"""SELECT
movie_list.id,
titel,
genre.name AS genre,
regie.surname as regie_surname,
regie.lastname as regie_lastname,
medium.medium,
movie_list.release_year
{SQL_BASE}{SQL_WHERE}
LIMIT ? OFFSET ?"""
paginated_params = params + [per_page, offset]
SELCET_VALUE = pd.read_sql(SELECT_SQL, db, params=paginated_params)
# Ergebnis in eine Liste von Dictionaries umwandeln
return_list_dict = []
for id, titel, genre, regie_surname, regie_lastname, medium, release_year in zip(SELCET_VALUE["id"], SELCET_VALUE["titel"], SELCET_VALUE["genre"], SELCET_VALUE["regie_surname"], SELCET_VALUE["regie_lastname"], SELCET_VALUE["medium"], SELCET_VALUE["release_year"]):
return_list_dict.append(
{"id": id, "titel": titel, "genre": genre, "regie": regie_surname + " " + regie_lastname, "medium": medium, "release_year": release_year})
return return_list_dict, total_pages
def get_movie_by_title(movie_title: str, db_name: str = "movie_db.db"):
SQL_PARAM = f"""SELECT SQL_PARAM = f"""SELECT
movie_list.id, movie_list.id,
titel, titel,
genre.name AS genre, genre.name AS genre,
regie.surname as regie_surname, regie.surname as regie_surname,
regie.lastname as regie_lastname, regie.lastname as regie_lastname,
medium.medium medium.medium,
movie_list.release_year
FROM movie_list FROM movie_list
INNER JOIN genre ON movie_list.genre_id=genre.id INNER JOIN genre ON movie_list.genre_id=genre.id
INNER JOIN regie ON movie_list.regie_id=regie.id INNER JOIN regie ON movie_list.regie_id=regie.id
INNER JOIN medium ON movie_list.medium_id=medium.id INNER JOIN medium ON movie_list.medium_id=medium.id
WHERE movie_list.titel = ?
; ;
""" """
db = sqlite3.connect(db_name) with DBcm.UseDatabase(db_name) as db:
SELCET_VALUE = pd.read_sql(SQL_PARAM, db) db.execute(SQL_PARAM, (movie_title.lower(),))
return_list_dict = [] movie = db.fetchone()
for id, titel, genre, regie_surname, regie_lastname, medium in zip(SELCET_VALUE["id"], SELCET_VALUE["titel"], SELCET_VALUE["genre"], SELCET_VALUE["regie_surname"], SELCET_VALUE["regie_lastname"], SELCET_VALUE["medium"]): if movie:
return_list_dict.append( return {"id": movie[0], "titel": movie[1], "genre": movie[2], "regie": movie[3] + " " + movie[4], "medium": movie[5], "release_year": movie[6]}
{"id": id, "titel": titel, "genre": genre, "regie": regie_surname + " " + regie_lastname, "medium": medium}) return None
return return_list_dict
class User(UserMixin):
def __init__(self, id, username, password_hash):
self.id = id
self.username = username
self.password_hash = password_hash
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def add_user(username, password, db_name="movie_db.db"):
password_hash = generate_password_hash(password)
SQL = "INSERT INTO user (username, password_hash) VALUES (?, ?)"
with DBcm.UseDatabase(db_name) as db:
db.execute(SQL, (username, password_hash))
def get_user_by_username(username, db_name="movie_db.db"):
SQL = "SELECT * FROM user WHERE username = ?"
with DBcm.UseDatabase(db_name) as db:
db.execute(SQL, (username,))
user_data = db.fetchone()
if user_data:
return User(id=user_data[0], username=user_data[1], password_hash=user_data[2])
return None
def get_user_by_id(user_id, db_name="movie_db.db"):
SQL = "SELECT * FROM user WHERE id = ?"
with DBcm.UseDatabase(db_name) as db:
db.execute(SQL, (user_id,))
user_data = db.fetchone()
if user_data:
return User(id=user_data[0], username=user_data[1], password_hash=user_data[2])
return None
if __name__ == "__main__": if __name__ == "__main__":
@@ -169,7 +421,7 @@ if __name__ == "__main__":
# print(all_select()) # print(all_select())
# id_genre = search_id( # id_genre = search_id(
# search_name="DVD", select_from="medium", select_where="medium") # search_name="DVD", select_from="medium", select_where="medium")
add_movie_to_list(movie_name="Schlumpfland", scrape_and_add_movie(movie_name="Die unglaubliche Reise in einem verrückten Flugzeug",
genre_id=1, regie_id=1, medium_id=1) medium_id=1)
for test in show_movie_list(): for test in show_movie_list():
print(test.get("id"), test.get("medium")) print(test.get("id"), test.get("medium"), test.get("release_year"))
+4
View File
@@ -1,2 +1,6 @@
flask flask
dbcm dbcm
pandas
requests
beautifulsoup4
Flask-Login
+28
View File
@@ -0,0 +1,28 @@
---
- name: Deploy and start Movie-DB Application
hosts: localhost
connection: local
become: yes
vars:
project_name: movie-db
install_dir: "/opt/{{ project_name }}"
project_src: "{{ playbook_dir }}"
tasks:
- name: Create installation directory
ansible.builtin.file:
path: "{{ install_dir }}"
state: directory
mode: '0755'
- name: Copy docker-compose file
ansible.builtin.copy:
src: "{{ project_src }}/docker-compose.yml"
dest: "{{ install_dir }}/docker-compose.yml"
- name: Start application with Docker Compose
community.docker.docker_compose:
project_src: "{{ install_dir }}"
state: present
restarted: yes
+8 -38
View File
@@ -1,50 +1,20 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<form action="{{ url }}" method="POST"> <form action="{{ url }}" method="POST" class="mb-3">
<div class="mb-3">
<input type="text" <input type="text"
class="form-control"
placeholder="Movie" placeholder="Movie"
name="{{ select_movie }}" name="{{ select_movie }}"
id="{{ select_movie }}"> id="{{ select_movie }}">
&nbsp; </div>
<select name="{{ select_genre }}" id="{{ select_genre }}"> <div class="mb-3">
{% for name in data %} <select name="{{ select_medium }}" id="{{ select_medium }}" class="form-select">
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
</select>
&nbsp;
<select name="{{ select_medium }}" id="{{ select_medium }}">
{% for name in data_medium %} {% for name in data_medium %}
<option value="{{ name }}">{{ name }}</option> <option value="{{ name }}">{{ name }}</option>
{% endfor %} {% endfor %}
</select> </select>
&nbsp; </div>
<select name="{{ select_regie }}" id="{{ select_regie }}"> <button type="submit" class="btn btn-primary">Eingabe</button>
{% for name in data_regie %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
</select>
&nbsp;
<button type="submit"> Eingabe </button>
</form> </form>
<br>
<table border="1" width="50%">
<tr>
<th>Id</th>
<th>Name</th>
<th>Genre</th>
<th>Regie</th>
<th>Medium</th>
</tr>
{% for list_all in data_all %}
<tr>
<td>{{list_all.get("id")}}</td>
<td>{{list_all.get("titel")}}</td>
<td>{{list_all.get("genre")}}</td>
<td>{{list_all.get("regie")}}</td>
<td>{{list_all.get("medium")}}</td>
</tr>
{% endfor %}
</table>
{% endblock %} {% endblock %}
@@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block body %}
<h3 class="mb-3">Film nicht gefunden. Bitte manuell eingeben:</h3>
<form action="/save_manual" method="POST">
<input type="hidden" name="movie_name" value="{{ movie_name }}">
<input type="hidden" name="medium_id" value="{{ medium_id }}">
<div class="mb-3">
<label for="genre_name" class="form-label">Genre:</label>
<input type="text" class="form-control" id="genre_name" name="genre_name" required>
</div>
<div class="mb-3">
<label for="director_name" class="form-label">Regisseur:</label>
<input type="text" class="form-control" id="director_name" name="director_name" required>
</div>
<div class="mb-3">
<label for="release_year" class="form-label">Erscheinungsjahr:</label>
<input type="number" class="form-control" id="release_year" name="release_year" required>
</div>
<button type="submit" class="btn btn-primary">Speichern</button>
</form>
{% endblock %}
+30 -6
View File
@@ -1,12 +1,36 @@
<!DOCTYPE html> <!doctype html>
<html> <html lang="de">
<head> <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ sitename }}</title> <title>{{ sitename }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head> </head>
<body> <body>
<h2> {{ sitename }} </h2> <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<p> Es werden alle Filmtitel in einer Liste angezeigt. <div class="container-fluid">
{% block body %} <a class="navbar-brand" href="{{ url_for('index') }}">{{ sitename }}</a>
{% endblock %} <div class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
{% if current_user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register') }}">Registrieren</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% block body %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body> </body>
</html> </html>
+60
View File
@@ -0,0 +1,60 @@
{% extends "base.html" %}
{% block body %}
<a href="/add_movie" class="btn btn-primary mb-3">Film hinzufügen</a>
<form method="post" action="{{ url_for('index') }}">
<div class="row mb-3">
<div class="col">
<label for="search" class="form-label">Suche</label>
<input type="text" name="search" id="search" class="form-control" value="{{ search_query }}">
</div>
</div>
<button type="submit" class="btn btn-primary">Suchen</button>
</form>
<table class="table table-striped table-bordered mt-3">
<thead class="table-dark">
<tr>
<th>Id</th>
<th>Name</th>
<th>Genre</th>
<th>Regie</th>
<th>Medium</th>
<th>Erscheinungsjahr</th>
</tr>
</thead>
<tbody>
{% for list_all in data_all %}
<tr>
<td>{{list_all.get("id")}}</td>
<td>{{list_all.get("titel")}}</td>
<td>{{list_all.get("genre")}}</td>
<td>{{list_all.get("regie")}}</td>
<td>{{list_all.get("medium")}}</td>
<td>{{list_all.get("release_year")}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav aria-label="Page navigation">
<ul class="pagination">
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('index', page=1, search=search_query) }}">Erste</a>
</li>
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('index', page=current_page - 1, search=search_query) }}">Zurück</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#">Seite {{ current_page }} von {{ total_pages }}</a>
</li>
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('index', page=current_page + 1, search=search_query) }}">Weiter</a>
</li>
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('index', page=total_pages, search=search_query) }}">Letzte</a>
</li>
</ul>
</nav>
{% endblock %}
+25
View File
@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block body %}
<div class="container">
<h2>Login</h2>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Passwort</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
<p class="mt-3">Noch keinen Account? <a href="{{ url_for('register') }}">Hier registrieren</a>.</p>
</div>
{% endblock %}
-10
View File
@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block body %}
<p>
Film = {{ add_movie }} <br>
Genre = {{ add_genre }} <span>&nbsp genre_id = {{ add_genre_id }} <br>
Medium = {{ add_medium }} <span>&nbsp medium_id = {{add_medium_id }} <br>
Regie = {{ add_regie }} <span>&nbsp reg_id = {{ add_regie_id }} <br>
</p>
{% endblock %}
+18
View File
@@ -0,0 +1,18 @@
{% extends "base.html" %}
{% block body %}
<div class="container">
<h2>Registrieren</h2>
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Benutzername</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Passwort</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Registrieren</button>
</form>
<p class="mt-3">Schon einen Account? <a href="{{ url_for('login') }}">Hier einloggen</a>.</p>
</div>
{% endblock %}
-650
View File
@@ -1,650 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "5263a987-da36-46d7-a2e7-d0658cda09c1",
"metadata": {},
"outputs": [],
"source": [
"import DBcm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3f6b0763-4106-4bfa-9aef-7afbd180c6d4",
"metadata": {},
"outputs": [],
"source": [
"db_name = \"movie_db.db\"\n",
"\n",
"create_my_list = \"\"\"\n",
" create table if not exists movie_list (\n",
" id integer not null primary key autoincrement,\n",
" titel varchar(64) not null,\n",
" genre_id integer not null,\n",
" regie_id integer not null\n",
" \n",
" )\n",
"\n",
"\"\"\"\n",
"\n",
"create_genre = \"\"\"\n",
" create table if not exists genre (\n",
" id integer not null primary key autoincrement,\n",
" name varchar(64) not null\n",
" )\n",
"\"\"\"\n",
"\n",
"create_regie = \"\"\"\n",
" create table if not exists regie (\n",
" id integer not null primary key autoincrement,\n",
" surname varchar(64) not null,\n",
" lastname varchr(64) not null\n",
" )\n",
"\"\"\"\n",
"\n",
"\n",
"with DBcm.UseDatabase(db_name) as db: \n",
" db.execute(create_my_list)\n",
" db.execute(create_genre)\n",
" db.execute(create_regie)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "376ef812-97b5-45ba-8ef1-eb8d7829494a",
"metadata": {},
"outputs": [],
"source": [
"# ADDed Genre values\n",
"\n",
"ADD_GENRE_VALUE = \"\"\"\n",
" INSERT INTO genre(name)\n",
" SELECT ?\n",
" WHERE NOT EXISTS (SELECT 1 FROM genre WHERE name = ?);\n",
" \"\"\"\n",
"\n",
"with open(\"genre_list\", \"r\") as fs: \n",
" for genre_value in fs.readlines():\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(ADD_GENRE_VALUE, (genre_value.strip(), genre_value.strip()))\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "63b16a41-88bf-4832-a26c-09180832f597",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['action', 'adventure', 'animation', 'biography', 'comedy', 'crime', 'cult movie', 'disney', 'documentary', 'drama', 'erotic', 'family', 'fantasy', 'film-noir', 'gangster', 'gay and lesbian', 'history', 'horror', 'military', 'music', 'musical', 'mystery', 'nature', 'neo-noir', 'period', 'pixar', 'road movie', 'romance', 'sci-fi', 'short', 'spy', 'super hero', 'thriller', 'visually stunning', 'war', 'western']\n"
]
}
],
"source": [
"def all_genres():\n",
" ALL_GENRE = \"\"\"\n",
" SELECT * from genre \n",
" \"\"\" \n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(ALL_GENRE)\n",
" all_genre = [i[1] for i in db.fetchall()]\n",
" \n",
" return all_genre\n",
"\n",
"print(all_genres())\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "89b20b5f-34aa-4490-a4c0-c186c9fa30bd",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"36\n"
]
}
],
"source": [
"def search_genre_id(genre_name):\n",
" GENRE_QUERY = \"\"\"\n",
" select id from genre\n",
" where name = ?\n",
" \"\"\"\n",
" try:\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(GENRE_QUERY,(genre_name,))\n",
" genre_id = db.fetchone()[0]\n",
" return int(genre_id)\n",
" except:\n",
" return int(0)\n",
"\n",
"\n",
"def search_medium_id(genre_name):\n",
" GENRE_QUERY = \"\"\"\n",
" select id from genre\n",
" where medium = ?\n",
" \"\"\"\n",
" try:\n",
" with DBcm.UseDatabase(db_name) as db:\n",
" db.execute(GENRE_QUERY,(genre_name,))\n",
" genre_id = db.fetchone()[0]\n",
" return int(genre_id)\n",
" except:\n",
" return int(0)\n",
"\n",
"print(search_genre_id(genre_name=\"action\"))\n",
"print(search_genre_id(genre_name=\"western\"))"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "c70d91a5-7855-465d-a4a6-daebc065ee37",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0-John-Smith\n",
"1-James-Johnson\n",
"2-William-Williams\n",
"3-Michael-Brown\n",
"4-David-Davis\n",
"5-Richard-Miller\n",
"6-Joseph-Wilson\n",
"7-Charles-Moore\n",
"8-Thomas-Taylor\n",
"9-Daniel-Anderson\n",
"10-Paul-Thomas\n",
"11-Mark-Jackson\n",
"12-Donna-White\n",
"13-Michelle-Harris\n",
"14-Laura-Martin\n",
"15-Sara-Thompson\n",
"16-Ana-Garcia\n",
"17-Carlos-Rodriguez\n",
"18-Maria-Martinez\n",
"19-Jose-Hernandez\n",
"20-Luis-Lopez\n",
"21-Rosa-Gonzalez\n",
"22-Pedro-Perez\n",
"23-Miguel-Sanchez\n",
"24-Juan-Ramirez\n",
"25-Ana-Flores\n",
"26-Isabella-Cruz\n",
"27-Victor-Rivera\n",
"28-Kevin-Lee\n",
"29-Brian-Walker\n",
"30-Emily-Hall\n",
"31-Ryan-Allen\n",
"32-Aaron-Young\n",
"33-Jeffrey-King\n",
"34-Joshua-Wright\n",
"35-Brandon-Scott\n",
"36-Frank-Turner\n",
"37-Gregory-Carter\n",
"38-Samuel-Phillips\n",
"39-Chris-Evans\n",
"40-Anthony-Collins\n",
"41-Eric-Stewart\n",
"42-Frank-Snyder\n",
"43-Thomas-Baker\n",
"44-Jeremy-Nelson\n",
"45-Steven-Roberts\n",
"46-Edward-Campbell\n",
"47-Ryan-Miller\n",
"48-Jacob-Davis\n",
"49-David-Garcia\n",
"50-Sophia-Rodriguez\n",
"51-Emma-Martinez\n",
"52-Noah-Hernandez\n",
"53-Ava-Lopez\n",
"54-Ethan-Gonzalez\n",
"55-Mia-Perez\n",
"56-William-Sanchez\n",
"57-James-Ramirez\n",
"58-Olivia-Flores\n",
"59-Lucas-Cruz\n",
"60-Isabella-Rivera\n",
"61-David-Lee\n",
"62-Sophie-Walker\n",
"63-Matthew-Hall\n",
"64-Emma-Allen\n",
"65-Ryan-Young\n",
"66-Ava-King\n",
"67-Ethan-Wright\n",
"68-Mia-Scott\n",
"69-William-Turner\n",
"70-James-Carter\n",
"71-Olivia-Phillips\n",
"72-Lucas-Evans\n",
"73-Sophie-Collins\n",
"74-Noah-Stewart\n",
"75-Ava-Snyder\n",
"76-Ethan-Baker\n",
"77-Mia-Nelson\n",
"78-Noah-Roberts\n",
"79-Emma-Campbell\n",
"80-William-Miller\n",
"81-James-Davis\n",
"82-Olivia-Garcia\n",
"83-Lucas-Rodriguez\n",
"84-Sophie-Martinez\n",
"85-Noah-Hernandez\n",
"86-Ava-Lopez\n",
"87-Ethan-Gonzalez\n",
"88-Mia-Perez\n",
"89-William-Sanchez\n",
"90-James-Ramirez\n",
"91-Olivia-Flores\n",
"92-Lucas-Cruz\n",
"93-Isabella-Rivera\n",
"94-David-Lee\n",
"95-Sophie-Walker\n",
"96-Matthew-Hall\n",
"97-Emma-Allen\n",
"98-Ryan-Young\n",
"99-Ava-King\n",
"100-Ethan-Wright\n",
"101-Mia-Scott\n",
"102-William-Turner\n",
"103-James-Carter\n",
"104-Olivia-Phillips\n",
"105-Lucas-Evans\n",
"106-Sophie-Collins\n",
"107-Noah-Stewart\n",
"108-Ava-Snyder\n",
"109-Ethan-Baker\n",
"110-Mia-Nelson\n",
"111-Noah-Roberts\n",
"112-Emma-Campbell\n",
"113-William-Miller\n",
"114-James-Davis\n",
"115-Olivia-Garcia\n",
"116-Lucas-Rodriguez\n",
"117-Sophie-Martinez\n",
"118-Noah-Hernandez\n",
"119-Ava-Lopez\n",
"120-Ethan-Gonzalez\n",
"121-Mia-Perez\n",
"122-William-Sanchez\n",
"123-James-Ramirez\n",
"124-Olivia-Flores\n",
"125-Lucas-Cruz\n",
"126-Isabella-Rivera\n",
"127-David-Lee\n",
"128-Sophie-Walker\n",
"129-Matthew-Hall\n",
"130-Emma-Allen\n",
"131-Ryan-Young\n",
"132-Ava-King\n",
"133-Ethan-Wright\n",
"134-Mia-Scott\n",
"135-William-Turner\n",
"136-James-Carter\n",
"137-Olivia-Phillips\n",
"138-Lucas-Evans\n",
"139-Sophie-Collins\n",
"140-Noah-Stewart\n",
"141-Ava-Snyder\n",
"142-Ethan-Baker\n",
"143-Mia-Nelson\n",
"144-Noah-Roberts\n",
"145-Emma-Campbell\n",
"146-William-Miller\n",
"147-James-Davis\n",
"148-Olivia-Garcia\n",
"149-Lucas-Rodriguez\n",
"150-Sophie-Martinez\n",
"151-Noah-Hernandez\n",
"152-Ava-Lopez\n",
"153-Ethan-Gonzalez\n",
"154-Mia-Perez\n",
"155-William-Sanchez\n",
"156-James-Ramirez\n",
"157-Olivia-Flores\n",
"158-Lucas-Cruz\n",
"159-Isabella-Rivera\n",
"160-David-Lee\n",
"161-Sophie-Walker\n",
"162-Matthew-Hall\n",
"163-Emma-Allen\n",
"164-Ryan-Young\n",
"165-Ava-King\n",
"166-Ethan-Wright\n",
"167-Mia-Scott\n",
"168-William-Turner\n",
"169-James-Carter\n",
"170-Olivia-Phillips\n",
"171-Lucas-Evans\n",
"172-Sophie-Collins\n",
"173-Noah-Stewart\n",
"174-Ava-Snyder\n",
"175-Ethan-Baker\n",
"176-Mia-Nelson\n",
"177-Noah-Roberts\n",
"178-Emma-Campbell\n",
"179-William-Miller\n",
"180-James-Davis\n",
"181-Olivia-Garcia\n",
"182-Lucas-Rodriguez\n",
"183-Sophie-Martinez\n",
"184-Noah-Hernandez\n",
"185-Ava-Lopez\n",
"186-Ethan-Gonzalez\n",
"187-Mia-Perez\n",
"188-William-Sanchez\n",
"189-James-Ramirez\n",
"190-Olivia-Flores\n",
"191-Lucas-Cruz\n",
"192-Isabella-Rivera\n",
"193-David-Lee\n",
"194-Sophie-Walker\n",
"195-Matthew-Hall\n",
"196-Emma-Allen\n",
"197-Ryan-Young\n",
"198-Ava-King\n",
"199-Ethan-Wright\n",
"200-Mia-Scott\n",
"201-William-Turner\n",
"202-James-Carter\n",
"203-Olivia-Phillips\n",
"204-Lucas-Evans\n",
"205-Sophie-Collins\n",
"206-Noah-Stewart\n",
"207-Ava-Snyder\n",
"208-Ethan-Baker\n",
"209-Mia-Nelson\n",
"210-Noah-Roberts\n",
"211-Emma-Campbell\n",
"212-William-Miller\n",
"213-James-Davis\n",
"214-Olivia-Garcia\n",
"215-Lucas-Rodriguez\n",
"216-Sophie-Martinez\n",
"217-Noah-Hernandez\n",
"218-Ava-Lopez\n",
"219-Ethan-Gonzalez\n",
"220-Mia-Perez\n",
"221-William-Sanchez\n",
"222-James-Ramirez\n",
"223-Olivia-Flores\n",
"224-Lucas-Cruz\n",
"225-Isabella-Rivera\n",
"226-David-Lee\n",
"227-Sophie-Walker\n",
"228-Matthew-Hall\n",
"229-Emma-Allen\n",
"230-Ryan-Young\n",
"231-Ava-King\n",
"232-Ethan-Wright\n",
"233-Mia-Scott\n",
"234-William-Turner\n",
"235-James-Carter\n",
"236-Olivia-Phillips\n",
"237-Lucas-Evans\n",
"238-Sophie-Collins\n",
"239-Noah-Stewart\n",
"240-Ava-Snyder\n",
"241-Ethan-Baker\n",
"242-Mia-Nelson\n",
"243-Noah-Roberts\n",
"244-Emma-Campbell\n",
"245-William-Miller\n",
"246-James-Davis\n",
"247-Olivia-Garcia\n",
"248-Lucas-Rodriguez\n",
"249-Sophie-Martinez\n",
"250-Noah-Hernandez\n",
"251-Ava-Lopez\n",
"252-Ethan-Gonzalez\n",
"253-Mia-Perez\n",
"254-William-Sanchez\n",
"255-James-Ramirez\n",
"256-Olivia-Flores\n",
"257-Lucas-Cruz\n",
"258-Isabella-Rivera\n",
"259-David-Lee\n",
"260-Sophie-Walker\n",
"261-Matthew-Hall\n",
"262-Emma-Allen\n",
"263-Ryan-Young\n",
"264-Ava-King\n",
"265-Ethan-Wright\n",
"266-Mia-Scott\n",
"267-William-Turner\n",
"268-James-Carter\n",
"269-Olivia-Phillips\n",
"270-Lucas-Evans\n",
"271-Sophie-Collins\n",
"272-Noah-Stewart\n",
"273-Ava-Snyder\n",
"274-Ethan-Baker\n",
"275-Mia-Nelson\n",
"276-Noah-Roberts\n",
"277-Emma-Campbell\n",
"278-William-Miller\n",
"279-James-Davis\n",
"280-Olivia-Garcia\n",
"281-Lucas-Rodriguez\n",
"282-Sophie-Martinez\n",
"283-Noah-Hernandez\n",
"284-Ava-Lopez\n",
"285-Ethan-Gonzalez\n",
"286-Mia-Perez\n",
"287-William-Sanchez\n",
"288-James-Ramirez\n",
"289-Olivia-Flores\n",
"290-Lucas-Cruz\n",
"291-Isabella-Rivera\n",
"292-David-Lee\n",
"293-Sophie-Walker\n",
"294-Matthew-Hall\n",
"295-Emma-Allen\n",
"296-Ryan-Young\n",
"297-Ava-King\n",
"298-Ethan-Wright\n",
"299-Mia-Scott\n",
"300-William-Turner\n",
"301-James-Carter\n",
"302-Olivia-Phillips\n",
"303-Lucas-Evans\n",
"304-Sophie-Collins\n",
"305-Noah-Stewart\n",
"306-Ava-Snyder\n",
"307-Ethan-Baker\n",
"308-Mia-Nelson\n",
"309-Noah-Roberts\n",
"310-Emma-Campbell\n",
"311-William-Miller\n",
"312-James-Davis\n",
"313-Olivia-Garcia\n",
"314-Lucas-Rodriguez\n",
"315-Sophie-Martinez\n",
"316-Noah-Hernandez\n",
"317-Ava-Lopez\n",
"318-Ethan-Gonzalez\n",
"319-Mia-Perez\n",
"320-William-Sanchez\n",
"321-James-Ramirez\n",
"322-Olivia-Flores\n",
"323-Lucas-Cruz\n",
"324-Isabella-Rivera\n",
"325-David-Lee\n",
"326-Sophie-Walker\n"
]
}
],
"source": [
"import pandas as pd\n",
"usecols = [\"Name\", \"Vorname\"]\n",
"air = pd.read_csv(\"regie_name.csv\", usecols=usecols)\n",
"\n",
"for count, (i, b) in enumerate(zip(air[\"Name\"], air[\"Vorname\"])):\n",
" print(count, b, i, sep=\"-\")\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ee4b5dee-6b41-49eb-8d75-9db50d20fdef",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Name</th>\n",
" <th>Vorname</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Smith</td>\n",
" <td>John</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Johnson</td>\n",
" <td>James</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Williams</td>\n",
" <td>William</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Brown</td>\n",
" <td>Michael</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>Davis</td>\n",
" <td>David</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Name Vorname\n",
"0 Smith John\n",
"1 Johnson James\n",
"2 Williams William\n",
"3 Brown Michael\n",
"4 Davis David"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"air = pd.read_csv(\"regie_name.csv\", nrows=5)\n",
"air"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "8017742d-3b8e-4847-b5a3-4f28e0c9057a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Name\n",
"Flores 10.0\n",
"Davis 10.0\n",
"Gonzalez 10.0\n",
"Garcia 10.0\n",
"Cruz 10.0\n",
" ... \n",
"Taylor 1.0\n",
"White 1.0\n",
"Thompson 1.0\n",
"Wilson 1.0\n",
"Williams 1.0\n",
"Length: 47, dtype: float64\n"
]
}
],
"source": [
"chunker = pd.read_csv(\"regie_name.csv\", chunksize=1000)\n",
"tot = pd.Series([], dtype='int64')\n",
"for piece in chunker:\n",
" tot = tot.add(piece[\"Name\"].value_counts(), fill_value=0)\n",
"tot = tot.sort_values(ascending=False)\n",
"print(tot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7531f34-19a6-4606-88f2-1c4fdd3b0b7d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
-1
View File
@@ -1,7 +1,6 @@
[defaults] [defaults]
#inventory = /home/user06/hosts #inventory = /home/user06/hosts
inventory = ./ansible_inv_main.py inventory = ./ansible_inv_main.py
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S on {host}
private_key_file = /home/jonnybravo/.ssh/ansible-test private_key_file = /home/jonnybravo/.ssh/ansible-test
gathering = smart gathering = smart
fact_caching = yaml fact_caching = yaml
+5 -1
View File
@@ -44,7 +44,11 @@ def scan_csv(csvfile=str, colm="hostname"):
with open(csvfile) as csv_file: with open(csvfile) as csv_file:
csv_reader = csv.DictReader(csv_file, delimiter=",") csv_reader = csv.DictReader(csv_file, delimiter=",")
for count, row in enumerate(csv_reader, 1): for count, row in enumerate(csv_reader, 1):
hostlist.append(row[colm]) hostname = row[colm]
if port_open(address=hostname):
hostlist.append(hostname)
else:
print(f"INFO: Host {hostname} not reachable.", file=sys.stderr)
else: else:
raise FileNotFoundError( raise FileNotFoundError(
errno.ENOENT, os.strerror(errno.ENOENT), csvfile) errno.ENOENT, os.strerror(errno.ENOENT), csvfile)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "b95d21e9-b710-47f0-aebe-b4e5368347b5", "counter": 1, "stdout": "\u001b[1;35m[WARNING]: No inventory was parsed, only implicit localhost is available\u001b[0m", "start_line": 0, "end_line": 1, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "created": "2025-08-29T14:58:00.928440+00:00"}
@@ -0,0 +1 @@
{"uuid": "f337836a-fd03-4235-a519-a32ce2c8fba1", "counter": 10, "stdout": "\u001b[0;32mok: [localhost] => {\u001b[0m\r\n\u001b[0;32m \"msg\": \"Hello Ansible World\"\u001b[0m\r\n\u001b[0;32m}\u001b[0m", "start_line": 9, "end_line": 12, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "runner_on_ok", "pid": 902557, "created": "2025-08-29T14:58:02.503113+00:00", "parent_uuid": "c403a83c-0a7d-0892-b38a-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-0892-b38a-000000000003", "task_action": "debug", "resolved_action": "ansible.builtin.debug", "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "remote_addr": "127.0.0.1", "res": {"msg": "Hello Ansible World", "_ansible_verbose_always": true, "changed": false, "_ansible_no_log": false}, "start": "2025-08-29T14:58:02.494227+00:00", "end": "2025-08-29T14:58:02.502915+00:00", "duration": 0.008688, "event_loop": null, "uuid": "f337836a-fd03-4235-a519-a32ce2c8fba1"}}
@@ -0,0 +1 @@
{"uuid": "c2e28e9e-2cda-4522-9835-7504bd265a3c", "counter": 11, "stdout": "\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=2 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ", "start_line": 12, "end_line": 16, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "playbook_on_stats", "pid": 902557, "created": "2025-08-29T14:58:02.504219+00:00", "parent_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "changed": {}, "dark": {}, "failures": {}, "ignored": {}, "ok": {"localhost": 2}, "processed": {"localhost": 1}, "rescued": {}, "skipped": {}, "artifact_data": {}, "uuid": "c2e28e9e-2cda-4522-9835-7504bd265a3c"}}
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "ea718f2d-28ab-442f-8c62-75647a8ed9b4", "counter": 2, "stdout": "\u001b[1;35m[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\u001b[0m", "start_line": 1, "end_line": 2, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "created": "2025-08-29T14:58:00.932101+00:00"}
@@ -0,0 +1 @@
{"uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "counter": 3, "stdout": "", "start_line": 2, "end_line": 2, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "playbook_on_start", "pid": 902557, "created": "2025-08-29T14:58:00.985958+00:00", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-0892-b38a-000000000001", "counter": 4, "stdout": "\r\nPLAY [Simple playbook] *********************************************************", "start_line": 2, "end_line": 4, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "playbook_on_play_start", "pid": 902557, "created": "2025-08-29T14:58:00.987257+00:00", "parent_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "name": "Simple playbook", "pattern": "localhost", "uuid": "c403a83c-0a7d-0892-b38a-000000000001"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-0892-b38a-000000000008", "counter": 5, "stdout": "\r\nTASK [Gathering Facts] *********************************************************", "start_line": 4, "end_line": 6, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "playbook_on_task_start", "pid": 902557, "created": "2025-08-29T14:58:00.991864+00:00", "parent_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-0892-b38a-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "name": "Gathering Facts", "is_conditional": false, "uuid": "c403a83c-0a7d-0892-b38a-000000000008"}}
@@ -0,0 +1 @@
{"uuid": "85e3ef2e-d0a2-4736-a945-eed111e6bac7", "counter": 6, "stdout": "", "start_line": 6, "end_line": 6, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "runner_on_start", "pid": 902557, "created": "2025-08-29T14:58:00.992322+00:00", "parent_uuid": "c403a83c-0a7d-0892-b38a-000000000008", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-0892-b38a-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "host": "localhost", "uuid": "85e3ef2e-d0a2-4736-a945-eed111e6bac7"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-0892-b38a-000000000003", "counter": 8, "stdout": "\r\nTASK [Print message] ***********************************************************", "start_line": 7, "end_line": 9, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "playbook_on_task_start", "pid": 902557, "created": "2025-08-29T14:58:02.493698+00:00", "parent_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-0892-b38a-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "name": "Print message", "is_conditional": false, "uuid": "c403a83c-0a7d-0892-b38a-000000000003"}}
@@ -0,0 +1 @@
{"uuid": "52e95112-c3d4-4aad-92a1-593af4b67c14", "counter": 9, "stdout": "", "start_line": 9, "end_line": 9, "runner_ident": "58e17dd4-e88d-4c34-a94a-23bd985b69b4", "event": "runner_on_start", "pid": 902557, "created": "2025-08-29T14:58:02.494275+00:00", "parent_uuid": "c403a83c-0a7d-0892-b38a-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "3ba9434a-1057-46bc-8e9d-908ca73dd932", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-0892-b38a-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-0892-b38a-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "uuid": "52e95112-c3d4-4aad-92a1-593af4b67c14"}}
@@ -0,0 +1,15 @@
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Simple playbook] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Print message] ***********************************************************
ok: [localhost] => {
 "msg": "Hello Ansible World"
}
PLAY RECAP *********************************************************************
localhost : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "1cb860c3-420b-4605-97f1-2848d696677e", "counter": 1, "stdout": "\u001b[1;35m[WARNING]: No inventory was parsed, only implicit localhost is available\u001b[0m", "start_line": 0, "end_line": 1, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "created": "2025-08-29T15:00:26.442517+00:00"}
@@ -0,0 +1 @@
{"uuid": "4b1be9b2-1bb7-4043-9a31-4891fec9fdb7", "counter": 10, "stdout": "\u001b[0;32mok: [localhost] => {\u001b[0m\r\n\u001b[0;32m \"msg\": \"Hello Ansible World\"\u001b[0m\r\n\u001b[0;32m}\u001b[0m", "start_line": 9, "end_line": 12, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "runner_on_ok", "pid": 903202, "created": "2025-08-29T15:00:27.953243+00:00", "parent_uuid": "c403a83c-0a7d-c752-d28b-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-c752-d28b-000000000003", "task_action": "debug", "resolved_action": "ansible.builtin.debug", "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "remote_addr": "127.0.0.1", "res": {"msg": "Hello Ansible World", "_ansible_verbose_always": true, "changed": false, "_ansible_no_log": false}, "start": "2025-08-29T15:00:27.944028+00:00", "end": "2025-08-29T15:00:27.953032+00:00", "duration": 0.009004, "event_loop": null, "uuid": "4b1be9b2-1bb7-4043-9a31-4891fec9fdb7"}}
@@ -0,0 +1 @@
{"uuid": "b42a3b6b-b91b-43c9-ba52-439eb6939ca1", "counter": 11, "stdout": "\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=2 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ", "start_line": 12, "end_line": 16, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "playbook_on_stats", "pid": 903202, "created": "2025-08-29T15:00:27.954620+00:00", "parent_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "changed": {}, "dark": {}, "failures": {}, "ignored": {}, "ok": {"localhost": 2}, "processed": {"localhost": 1}, "rescued": {}, "skipped": {}, "artifact_data": {}, "uuid": "b42a3b6b-b91b-43c9-ba52-439eb6939ca1"}}
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "581f3687-da78-4de6-a346-01670cc6d1ac", "counter": 2, "stdout": "\u001b[1;35m[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\u001b[0m", "start_line": 1, "end_line": 2, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "created": "2025-08-29T15:00:26.446145+00:00"}
@@ -0,0 +1 @@
{"uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "counter": 3, "stdout": "", "start_line": 2, "end_line": 2, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "playbook_on_start", "pid": 903202, "created": "2025-08-29T15:00:26.501020+00:00", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-c752-d28b-000000000001", "counter": 4, "stdout": "\r\nPLAY [Simple playbook] *********************************************************", "start_line": 2, "end_line": 4, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "playbook_on_play_start", "pid": 903202, "created": "2025-08-29T15:00:26.502556+00:00", "parent_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "name": "Simple playbook", "pattern": "localhost", "uuid": "c403a83c-0a7d-c752-d28b-000000000001"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-c752-d28b-000000000008", "counter": 5, "stdout": "\r\nTASK [Gathering Facts] *********************************************************", "start_line": 4, "end_line": 6, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "playbook_on_task_start", "pid": 903202, "created": "2025-08-29T15:00:26.508043+00:00", "parent_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-c752-d28b-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "name": "Gathering Facts", "is_conditional": false, "uuid": "c403a83c-0a7d-c752-d28b-000000000008"}}
@@ -0,0 +1 @@
{"uuid": "65252c48-7cf3-47e6-b791-ceeb40f3d376", "counter": 6, "stdout": "", "start_line": 6, "end_line": 6, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "runner_on_start", "pid": 903202, "created": "2025-08-29T15:00:26.508649+00:00", "parent_uuid": "c403a83c-0a7d-c752-d28b-000000000008", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-c752-d28b-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "host": "localhost", "uuid": "65252c48-7cf3-47e6-b791-ceeb40f3d376"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-c752-d28b-000000000003", "counter": 8, "stdout": "\r\nTASK [Print message] ***********************************************************", "start_line": 7, "end_line": 9, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "playbook_on_task_start", "pid": 903202, "created": "2025-08-29T15:00:27.943544+00:00", "parent_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-c752-d28b-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "name": "Print message", "is_conditional": false, "uuid": "c403a83c-0a7d-c752-d28b-000000000003"}}
@@ -0,0 +1 @@
{"uuid": "131837f4-0404-468f-b1e2-269a3feafbce", "counter": 9, "stdout": "", "start_line": 9, "end_line": 9, "runner_ident": "593bddbd-810b-4e74-88dc-0cd7c9cdd6b9", "event": "runner_on_start", "pid": 903202, "created": "2025-08-29T15:00:27.944071+00:00", "parent_uuid": "c403a83c-0a7d-c752-d28b-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "02eef34c-064c-4e67-a88c-02bfa5c8eb89", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-c752-d28b-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-c752-d28b-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "uuid": "131837f4-0404-468f-b1e2-269a3feafbce"}}
@@ -0,0 +1,15 @@
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Simple playbook] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Print message] ***********************************************************
ok: [localhost] => {
 "msg": "Hello Ansible World"
}
PLAY RECAP *********************************************************************
localhost : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "ad6f46a1-dab9-4683-9083-ddd461acb8ba", "counter": 1, "stdout": "\u001b[1;35m[WARNING]: No inventory was parsed, only implicit localhost is available\u001b[0m", "start_line": 0, "end_line": 1, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "created": "2025-08-29T13:47:48.946761+00:00"}
@@ -0,0 +1 @@
{"uuid": "7d3ddbba-7e61-4dcd-b5ea-177985863b14", "counter": 10, "stdout": "\u001b[0;32mok: [localhost] => {\u001b[0m\r\n\u001b[0;32m \"msg\": \"Hello Ansible World\"\u001b[0m\r\n\u001b[0;32m}\u001b[0m", "start_line": 9, "end_line": 12, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "runner_on_ok", "pid": 898221, "created": "2025-08-29T13:47:50.458632+00:00", "parent_uuid": "c403a83c-0a7d-9960-2310-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-9960-2310-000000000003", "task_action": "debug", "resolved_action": "ansible.builtin.debug", "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "remote_addr": "127.0.0.1", "res": {"msg": "Hello Ansible World", "_ansible_verbose_always": true, "changed": false, "_ansible_no_log": false}, "start": "2025-08-29T13:47:50.449895+00:00", "end": "2025-08-29T13:47:50.458374+00:00", "duration": 0.008479, "event_loop": null, "uuid": "7d3ddbba-7e61-4dcd-b5ea-177985863b14"}}
@@ -0,0 +1 @@
{"uuid": "62d02f82-08db-492c-9483-8d93358a05be", "counter": 11, "stdout": "\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=2 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ", "start_line": 12, "end_line": 16, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "playbook_on_stats", "pid": 898221, "created": "2025-08-29T13:47:50.459807+00:00", "parent_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "changed": {}, "dark": {}, "failures": {}, "ignored": {}, "ok": {"localhost": 2}, "processed": {"localhost": 1}, "rescued": {}, "skipped": {}, "artifact_data": {}, "uuid": "62d02f82-08db-492c-9483-8d93358a05be"}}
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "99ea232e-0942-4238-8bd2-505ee5c9e351", "counter": 2, "stdout": "\u001b[1;35m[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\u001b[0m", "start_line": 1, "end_line": 2, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "created": "2025-08-29T13:47:48.950812+00:00"}
@@ -0,0 +1 @@
{"uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "counter": 3, "stdout": "", "start_line": 2, "end_line": 2, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "playbook_on_start", "pid": 898221, "created": "2025-08-29T13:47:49.013875+00:00", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-9960-2310-000000000001", "counter": 4, "stdout": "\r\nPLAY [Simple playbook] *********************************************************", "start_line": 2, "end_line": 4, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "playbook_on_play_start", "pid": 898221, "created": "2025-08-29T13:47:49.015282+00:00", "parent_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "name": "Simple playbook", "pattern": "localhost", "uuid": "c403a83c-0a7d-9960-2310-000000000001"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-9960-2310-000000000008", "counter": 5, "stdout": "\r\nTASK [Gathering Facts] *********************************************************", "start_line": 4, "end_line": 6, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "playbook_on_task_start", "pid": 898221, "created": "2025-08-29T13:47:49.020611+00:00", "parent_uuid": "c403a83c-0a7d-9960-2310-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-9960-2310-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "name": "Gathering Facts", "is_conditional": false, "uuid": "c403a83c-0a7d-9960-2310-000000000008"}}
@@ -0,0 +1 @@
{"uuid": "cfd400a6-69ce-4d3c-8e38-b75c0a9ffbfc", "counter": 6, "stdout": "", "start_line": 6, "end_line": 6, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "runner_on_start", "pid": 898221, "created": "2025-08-29T13:47:49.021050+00:00", "parent_uuid": "c403a83c-0a7d-9960-2310-000000000008", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-9960-2310-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "host": "localhost", "uuid": "cfd400a6-69ce-4d3c-8e38-b75c0a9ffbfc"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-9960-2310-000000000003", "counter": 8, "stdout": "\r\nTASK [Print message] ***********************************************************", "start_line": 7, "end_line": 9, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "playbook_on_task_start", "pid": 898221, "created": "2025-08-29T13:47:50.449363+00:00", "parent_uuid": "c403a83c-0a7d-9960-2310-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-9960-2310-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "name": "Print message", "is_conditional": false, "uuid": "c403a83c-0a7d-9960-2310-000000000003"}}
@@ -0,0 +1 @@
{"uuid": "0f3383fe-bf86-410a-944d-8de2101092b7", "counter": 9, "stdout": "", "start_line": 9, "end_line": 9, "runner_ident": "cecc2a8d-546f-48e0-acd9-4c3d36c73994", "event": "runner_on_start", "pid": 898221, "created": "2025-08-29T13:47:50.449938+00:00", "parent_uuid": "c403a83c-0a7d-9960-2310-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "99a98f6a-089c-42d6-a19e-ed425ba92078", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9960-2310-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-9960-2310-000000000003", "task_action": "debug", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "uuid": "0f3383fe-bf86-410a-944d-8de2101092b7"}}
@@ -0,0 +1,15 @@
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Simple playbook] *********************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Print message] ***********************************************************
ok: [localhost] => {
 "msg": "Hello Ansible World"
}
PLAY RECAP *********************************************************************
localhost : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "77db1dac-c490-443d-9989-4d28a08eb31c", "counter": 1, "stdout": "\u001b[1;35m[WARNING]: No inventory was parsed, only implicit localhost is available\u001b[0m", "start_line": 0, "end_line": 1, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "created": "2025-08-29T13:50:22.957726+00:00"}
@@ -0,0 +1 @@
{"uuid": "29708dd0-33ec-45da-85ba-527cf1954cf9", "counter": 10, "stdout": "\u001b[0;32mok: [localhost] => {\u001b[0m\r\n\u001b[0;32m \"msg\": \"Hello Ansible World\"\u001b[0m\r\n\u001b[0;32m}\u001b[0m", "start_line": 9, "end_line": 12, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "runner_on_ok", "pid": 898727, "created": "2025-08-29T13:50:24.484808+00:00", "parent_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000003", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "play_pattern": "localhost", "task": "Print message", "task_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000003", "task_action": "debug", "resolved_action": "ansible.builtin.debug", "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:6", "host": "localhost", "remote_addr": "127.0.0.1", "res": {"msg": "Hello Ansible World", "_ansible_verbose_always": true, "changed": false, "_ansible_no_log": false}, "start": "2025-08-29T13:50:24.475492+00:00", "end": "2025-08-29T13:50:24.484539+00:00", "duration": 0.009047, "event_loop": null, "uuid": "29708dd0-33ec-45da-85ba-527cf1954cf9"}}
@@ -0,0 +1 @@
{"uuid": "9dd0d18a-9c0e-4015-a537-8a0bbb486522", "counter": 11, "stdout": "\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=2 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ", "start_line": 12, "end_line": 16, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "playbook_on_stats", "pid": 898727, "created": "2025-08-29T13:50:24.486291+00:00", "parent_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "changed": {}, "dark": {}, "failures": {}, "ignored": {}, "ok": {"localhost": 2}, "processed": {"localhost": 1}, "rescued": {}, "skipped": {}, "artifact_data": {}, "uuid": "9dd0d18a-9c0e-4015-a537-8a0bbb486522"}}
@@ -0,0 +1 @@
{"event": "verbose", "uuid": "7dcf913c-abbf-4d17-84bc-ba873247d4f3", "counter": 2, "stdout": "\u001b[1;35m[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\u001b[0m", "start_line": 1, "end_line": 2, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "created": "2025-08-29T13:50:22.961598+00:00"}
@@ -0,0 +1 @@
{"uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "counter": 3, "stdout": "", "start_line": 2, "end_line": 2, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "playbook_on_start", "pid": 898727, "created": "2025-08-29T13:50:23.015512+00:00", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "counter": 4, "stdout": "\r\nPLAY [Simple playbook] *********************************************************", "start_line": 2, "end_line": 4, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "playbook_on_play_start", "pid": 898727, "created": "2025-08-29T13:50:23.016831+00:00", "parent_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "play_pattern": "localhost", "name": "Simple playbook", "pattern": "localhost", "uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001"}}
@@ -0,0 +1 @@
{"uuid": "c403a83c-0a7d-9b7b-9dbc-000000000008", "counter": 5, "stdout": "\r\nTASK [Gathering Facts] *********************************************************", "start_line": 4, "end_line": 6, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "playbook_on_task_start", "pid": 898727, "created": "2025-08-29T13:50:23.021982+00:00", "parent_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "name": "Gathering Facts", "is_conditional": false, "uuid": "c403a83c-0a7d-9b7b-9dbc-000000000008"}}
@@ -0,0 +1 @@
{"uuid": "f852c0c3-f1e2-4170-ad79-1447b59273a7", "counter": 6, "stdout": "", "start_line": 6, "end_line": 6, "runner_ident": "d5fdc75b-c25d-418f-982d-3a0872a2792f", "event": "runner_on_start", "pid": 898727, "created": "2025-08-29T13:50:23.022451+00:00", "parent_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000008", "event_data": {"playbook": "playbook.yml", "playbook_uuid": "48db46ba-55d1-4954-8db6-37caef1fd04f", "play": "Simple playbook", "play_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000001", "play_pattern": "localhost", "task": "Gathering Facts", "task_uuid": "c403a83c-0a7d-9b7b-9dbc-000000000008", "task_action": "gather_facts", "resolved_action": null, "task_args": "", "task_path": "/home/jonnybravo/Projekte/Python_Skripte/play_ansible/run_play/play/playbook.yml:2", "host": "localhost", "uuid": "f852c0c3-f1e2-4170-ad79-1447b59273a7"}}

Some files were not shown because too many files have changed in this diff Show More