11 Commits
0.7.0 ... 1.0.0

Author SHA1 Message Date
6bac7839d3 release 1.0.0
- bump python-version to 3.14
- bump streamlit to 1.52.2
- bump numpy to 2.4.0
- extra-script for generating special-wb's
- rework of container to use a python-virtual-environment and create the special-wb's at first start of the container (no longer at first web-request)
- adding links to source-code and docker-registry's in welcome-message
- some more error-handling for ADFG(V)X
2025-12-25 18:52:23 +01:00
79c3cb83c7 trivy_image_scan 2025-12-13 22:56:43 +01:00
cb9fa61bc5 trivy_image_scan 2025-12-13 22:51:46 +01:00
9a1e438615 trivy_image_scan 2025-12-13 22:51:20 +01:00
787cb10e21 - pump streamlit to v1.52.1
- pump numpy to v2.3.5
- run container with
2025-12-09 23:23:39 +01:00
ad208f3403 fix compose-example in readme.md 2025-11-23 18:18:02 +01:00
b60316750a adding image-release on docker hub to ci-pipeline 2025-11-23 17:55:50 +01:00
013a6b298e Merge pull request 'pump to' (#4) from update_dependencies into main
Reviewed-on: #4
2025-11-16 11:55:27 +01:00
f1dce2417f pump to
streamlit==1.51.0
streamlit-folium==0.25.3
pandas==2.3.3
matplotlib~=3.10.7
numpy~=2.3.4
and automatic pip-upgrade while build container
2025-11-16 11:51:11 +01:00
9947c205c7 update versions of requirements 2025-09-28 16:43:33 +02:00
8510169495 erweiterung zeichenzählen 2025-08-30 13:37:00 +02:00
13 changed files with 204 additions and 193 deletions

View File

@@ -6,11 +6,11 @@ on:
image_tag: image_tag:
description: '2. Tag für das Docker-Image (außer latest) (z.B. v1.0.0)' description: '2. Tag für das Docker-Image (außer latest) (z.B. v1.0.0)'
required: true required: true
default: '0.7.0' default: '1.0.0'
env: env:
image_name: mysteryhelfer image_name: mysteryhelfer
registry: gitea.tebarius.duckdns.org registry_gitea: gitea.tebarius.duckdns.org
user: tebarius user: tebarius
jobs: jobs:
@@ -26,9 +26,15 @@ jobs:
- name: Login to Gitea - name: Login to Gitea
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ${{ env.registry }} registry: ${{ env.registry_gitea }}
username: ${{ env.user }} username: ${{ env.user }}
password: ${{ secrets.DOCKER_PULL_TOKEN }} password: ${{ secrets.IMAGE_REGISTRY_TOKEN_GITEA }}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ env.user }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build and push - name: Build and push
run: | run: |
@@ -39,6 +45,8 @@ jobs:
docker buildx build \ docker buildx build \
--file ./Dockerfile \ --file ./Dockerfile \
--platform linux/amd64,linux/arm64 \ --platform linux/amd64,linux/arm64 \
--tag ${{ env.registry }}/${{ env.user }}/${{ env.image_name }}:latest \ --tag ${{ env.registry_gitea }}/${{ env.user }}/${{ env.image_name }}:latest \
--tag ${{ env.registry }}/${{ env.user }}/${{ env.image_name }}:${{ github.event.inputs.image_tag }} \ --tag ${{ env.registry_gitea }}/${{ env.user }}/${{ env.image_name }}:${{ github.event.inputs.image_tag }} \
--tag ${{ env.user }}/${{ env.image_name }}:latest \
--tag ${{ env.user }}/${{ env.image_name }}:${{ github.event.inputs.image_tag }} \
--push ./ --push ./

View File

@@ -0,0 +1,28 @@
name: release-tag
on:
workflow_dispatch: # Manuelles Auslösen des Workflows
inputs:
image_tag:
description: 'Tag für das zu scannende Docker-Image z.B. latest'
required: true
default: 'latest'
env:
image_name: mysteryhelfer
registry_gitea: gitea.tebarius.duckdns.org
user: tebarius
jobs:
trivy_image_scan:
runs-on: ubuntu-latest
container: aquasec/trivy:latest
steps:
- name: Scan image with trivy
run: |
trivy image \
--exit-code 1 \
--scanners vuln,misconfig,secret \
--severity MEDIUM,HIGH,CRITICAL \
--ignore-unfixed \
${{ env.registry_gitea }}/${{ env.user }}/${{ env.image_name }}:${{ github.event.inputs.image_tag }}

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@
/.idea/ /.idea/
/app/data/morse-de.dic /app/data/morse-de.dic
/app/data/t9-de.dic /app/data/t9-de.dic
/.venv/

View File

@@ -1,30 +1,40 @@
FROM python:3.13-slim # Copyright (c) 2025 Martin Kayser (tebarius)
# Licensed under the MIT License. See LICENSE file in the project root.
ARG PYTHON_VERSION="3.14"
FROM python:${PYTHON_VERSION}-slim
LABEL authors="tebarius" LABEL authors="tebarius"
LABEL description="tebarius Mysteryhelfer web" LABEL description="tebarius Mysteryhelfer web"
WORKDIR /app ARG PYTHON_VERSION
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/myst-venv/bin:$PATH"
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y curl \ && apt-get install -y --no-install-recommends curl \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN python -m venv /myst-venv \
&& python -m pip install --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt
COPY ./app /app COPY ./app /app
# ein bisschen Patchen um auch beim Bookmarken oder Versenden der Webadresse per Messenger den richtigen Titel und das # ein bisschen Patchen um auch beim Bookmarken oder Versenden der Webadresse per Messenger den richtigen Titel und das
# richtige Favicon zu verwenden/sehen # richtige Favicon zu verwenden/sehen
COPY ./app/images/favicon.ico /usr/local/lib/python3.13/site-packages/streamlit/static/favicon.ico COPY ./app/images/favicon.ico /myst-venv/lib/python${PYTHON_VERSION}/site-packages/streamlit/static/favicon.ico
RUN sed -i -e 's|favicon\.png|favicon.ico|' \ RUN sed -i -e 's|favicon\.png|favicon.ico|' \
-e 's|<title>.*</title>|<title>tebarius Mysteryhelfer (web)</title>|' \ -e 's|<title>.*</title>|<title>tebarius Mysteryhelfer (web)</title>|' \
/usr/local/lib/python3.13/site-packages/streamlit/static/index.html /myst-venv/lib/python${PYTHON_VERSION}/site-packages/streamlit/static/index.html \
&& useradd -m -u 1000 myst \
&& chown -R myst:myst /app
USER myst
EXPOSE 8501 EXPOSE 8501
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"] ENTRYPOINT ["/bin/bash", "entrypoint.sh"]

View File

@@ -7,11 +7,16 @@ import matplotlib.pyplot as plt
import helper import helper
standard_output = ('#### Um den HILFE-Text zu einzelnen Funktionen aufzurufen bitte die Funktion mit leerem' standard_output = ('#### Um den HILFE-Text zu einzelnen Funktionen aufzurufen bitte die Funktion mit leerem'
' Eingabefeld aufrufen.') ' Eingabefeld aufrufen.\n'
'__Diese APP ist OpenSource:__ \n'
'- Sourcecode: https://gitea.tebarius.duckdns.org/tebarius/Mysteryhelfer-web\n'
'- Docker-Images sind verfügbar via:\n'
' - https://hub.docker.com/r/tebarius/mysteryhelfer\n'
' - https://gitea.tebarius.duckdns.org/tebarius/-/packages/container/mysteryhelfer/latest')
st.set_page_config( st.set_page_config(
# we do also patching static-files of streamlit in the docker-container so bookmarks will have # we do also patching static-files of streamlit in the docker-container so bookmarks will have
# the same favicon and if posting links for example in whatsapp they will have the same title # the same favicon and if posting links for example in WhatsApp they will have the same title
page_title="tebarius Mysteryhelfer (web)", page_title="tebarius Mysteryhelfer (web)",
page_icon="images/favicon.ico", page_icon="images/favicon.ico",
layout="wide", layout="wide",
@@ -224,7 +229,6 @@ if 'input_text' not in st.session_state:
st.session_state.input_text = "" st.session_state.input_text = ""
if 'output_text' not in st.session_state: if 'output_text' not in st.session_state:
st.session_state.output_text = standard_output st.session_state.output_text = standard_output
st.session_state.output_text += helper.generate_special_files()
if 'map_data' not in st.session_state: if 'map_data' not in st.session_state:
st.session_state.map_data = None st.session_state.map_data = None
if 'graph_data' not in st.session_state: if 'graph_data' not in st.session_state:

13
app/entrypoint.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Copyright (c) 2025 Martin Kayser (tebarius)
# Licensed under the MIT License. See LICENSE file in the project root.
set -e # Script bei Fehler abbrechen
# 1) einmalig bei Containerstart ausführen
echo "Generiere Special-WBs..."
python generate_special_wbs.py
# 2) Streamlit-App starten
echo "Starte Hauptprogramm..."
streamlit run app.py --server.port=8501 --server.address=0.0.0.0

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# Copyright (c) 2025 Martin Kayser (tebarius)
# Licensed under the MIT License. See LICENSE file in the project root.
import os
def t9_generate_t9de():
alphabet = {'A': '2', 'B': '2', 'C': '2', 'D': '3', 'E': '3', 'F': '3', 'G': '4',
'H': '4', 'I': '4', 'J': '5', 'K': '5', 'L': '5', 'M': '6', 'N': '6',
'O': '6', 'P': '7', 'Q': '7', 'R': '7', 'S': '7', 'T': '8', 'U': '8',
'V': '8', 'W': '9', 'X': '9', 'Y': '9', 'Z': '9', '1': '1', '2': '2',
'3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9',
'0': '0', 'Ñ': '6', 'É': '3', 'È': '3', 'À': '2', 'Ü': '8', 'Ö': '6',
'Ä': '2', '@': '1', '?': '1', '=': '0', ':': '1', '/': '1', '.': '1',
'-': '1', ',': '1', '+': '0', ')': '1', '(': '1', 'SS': '7'}
ofile = open("./data/t9-de.dic", "a", encoding="iso-8859-15")
file = open("./data/german.dic", "r", encoding="iso-8859-15")
for zeile in file:
omsg = ""
zeile = zeile.rstrip()
try:
for char in zeile:
omsg = omsg + alphabet[char.upper()]
except KeyError:
continue
else:
ofile.write(omsg + "," + zeile + "\n")
file.close()
ofile.close()
def remorse_generate_morsede():
alphabet = {'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.',
'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.',
'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-',
'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '1': '.----',
'2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.', '0': '-----', 'Ñ': '--.--', 'É': '..-..', 'È': '.-..-',
'À': '.--.-', 'Ü': '..--', 'Ö': '---.', 'Ä': '.-.-', '_': '..--.-', '@': '.--.-.',
'?': '..--..', '=': '-...-', ';': '-.-.-.', ':': '---...', '/': '-..-.',
'.': '.-.-.-', '-': '-....-', ',': '--..--', '+': '.-.-.', ')': '-.--.-',
'(': '-.--.', "'": '.----.', 'SS': '...--..'}
with open("./data/morse-de.dic", "w", encoding="iso-8859-1") as ofile:
for symbol, morse in alphabet.items():
ofile.write(f"{morse},{symbol}\n")
with open("./data/german.dic", "r", encoding="iso-8859-1") as infile:
for line in infile:
word = line.strip()
try:
morse_word = ''.join(alphabet[char.upper()] for char in word)
ofile.write(f"{morse_word},{word}\n")
except KeyError:
# Überspringe Wörter mit nicht-unterstützten Zeichen
continue
infile.close()
ofile.close()
def generate_special_files():
if os.path.exists("./data/morse-de.dic"):
print("Übersprungen...[deutsches Re-Morse-Wörterbuch existiert bereits]")
else:
remorse_generate_morsede()
print("[deutsches Re-Morse-Wörterbuch wurde generiert]")
if os.path.exists("./data/t9-de.dic"):
print("Übersprungen...[deutsches T9-Wörterbuch wurde existiert bereits]")
else:
t9_generate_t9de()
print("[deutsches T9-Wörterbuch wurde generiert]")
if __name__ == "__main__":
generate_special_files()

View File

@@ -2,10 +2,10 @@
# Licensed under the MIT License. See LICENSE file in the project root. # Licensed under the MIT License. See LICENSE file in the project root.
import math import math
from re import match # für unkennify from re import match # für unkennify
import folium
import streamlit as st import streamlit as st
from streamlit_folium import st_folium from streamlit_folium import st_folium
import folium
import os
# ***recursive quersummenfunktion*** # ***recursive quersummenfunktion***
@@ -176,70 +176,6 @@ def unkennify(text):
decoded = decoded + text[i:] decoded = decoded + text[i:]
return decoded return decoded
def generate_special_files():
out="\n\n\n"
if os.path.exists("./data/morse-de.dic"):
pass
else:
remorse_generate_morsede()
out+=":blue[deutsches Re-Morse-Wörterbuch wurde generiert] \n"
if os.path.exists("./data/t9-de.dic"):
pass
else:
t9_generate_t9de()
out+=":blue[deutsches T9-Wörterbuch wurde generiert] \n"
return out
def remorse_generate_morsede():
alphabet = {'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.',
'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.',
'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-',
'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '1': '.----',
'2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.', '0': '-----', 'Ñ': '--.--', 'É': '..-..', 'È': '.-..-',
'À': '.--.-', 'Ü': '..--', 'Ö': '---.', 'Ä': '.-.-', '_': '..--.-', '@': '.--.-.',
'?': '..--..', '=': '-...-', ';': '-.-.-.', ':': '---...', '/': '-..-.',
'.': '.-.-.-', '-': '-....-', ',': '--..--', '+': '.-.-.', ')': '-.--.-',
'(': '-.--.', "'": '.----.', 'SS': '...--..'}
with open("./data/morse-de.dic", "w", encoding="iso-8859-1") as ofile:
for symbol, morse in alphabet.items():
ofile.write(f"{morse},{symbol}\n")
with open("./data/german.dic", "r", encoding="iso-8859-1") as infile:
for line in infile:
word = line.strip()
try:
morse_word = ''.join(alphabet[char.upper()] for char in word)
ofile.write(f"{morse_word},{word}\n")
except KeyError:
# Überspringe Wörter mit nicht-unterstützten Zeichen
continue
infile.close()
ofile.close()
def t9_generate_t9de():
alphabet = {'A': '2', 'B': '2', 'C': '2', 'D': '3', 'E': '3', 'F': '3', 'G': '4',
'H': '4', 'I': '4', 'J': '5', 'K': '5', 'L': '5', 'M': '6', 'N': '6',
'O': '6', 'P': '7', 'Q': '7', 'R': '7', 'S': '7', 'T': '8', 'U': '8',
'V': '8', 'W': '9', 'X': '9', 'Y': '9', 'Z': '9', '1': '1', '2': '2',
'3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9',
'0': '0', 'Ñ': '6', 'É': '3', 'È': '3', 'À': '2', 'Ü': '8', 'Ö': '6',
'Ä': '2', '@': '1', '?': '1', '=': '0', ':': '1', '/': '1', '.': '1',
'-': '1', ',': '1', '+': '0', ')': '1', '(': '1', 'SS': '7'}
ofile = open("./data/t9-de.dic", "a", encoding="iso-8859-15")
file = open("./data/german.dic", "r", encoding="iso-8859-15")
for zeile in file:
omsg = ""
zeile = zeile.rstrip()
try:
for char in zeile:
omsg = omsg + alphabet[char.upper()]
except KeyError:
continue
else:
ofile.write(omsg + "," + zeile + "\n")
file.close()
ofile.close()
def rail_encrypt(plain_text: str, rails: int): def rail_encrypt(plain_text: str, rails: int):
arr = [["" for _ in range(len(plain_text))] for _ in range(rails)] arr = [["" for _ in range(len(plain_text))] for _ in range(rails)]
r = 0 r = 0
@@ -293,10 +229,10 @@ def rail_decrypt(cipher: str, rails: int):
y -= 1 y -= 1
elif down: elif down:
y += 1 y += 1
elif down is False and y == 0: elif not down and y == 0:
down = True down = True
y += 1 y += 1
elif down is False: elif not down:
y -= 1 y -= 1
return out return out
#from folium.map import DivIcon #from folium.map import DivIcon

View File

@@ -227,7 +227,7 @@ naknak_to_text = ("### NakNak to Text\n"
"Diese Funktion übersetzt das Geschnatter in verständliche Buchstaben/Zeichen. !!Bitte unbedingt " "Diese Funktion übersetzt das Geschnatter in verständliche Buchstaben/Zeichen. !!Bitte unbedingt "
"die Groß- und Kleinschreibung beibehalten!! Wer diesen Quatsch unbedingt umgekehrt übersetzten " "die Groß- und Kleinschreibung beibehalten!! Wer diesen Quatsch unbedingt umgekehrt übersetzten "
"will, wird wohl etwas suchen müssen, da der Original-Übersetzer, der unter " "will, wird wohl etwas suchen müssen, da der Original-Übersetzer, der unter "
"http://uebersetzer.schnatterente.net erreichbar war, nicht mehr online ist und ich ganz bestimmt " "https://uebersetzer.schnatterente.net erreichbar war, nicht mehr online ist und ich ganz bestimmt "
"keinen hier integrieren werde.") "keinen hier integrieren werde.")
navajo_to_text = ("### Navajo-ABC to Text\n" navajo_to_text = ("### Navajo-ABC to Text\n"
@@ -240,7 +240,7 @@ navajo_to_text = ("### Navajo-ABC to Text\n"
"Wörter bzw. deren Schreibweise angeht im Netz, mit welchen codiert wird, weshalb ich hier nur die " "Wörter bzw. deren Schreibweise angeht im Netz, mit welchen codiert wird, weshalb ich hier nur die "
"Dekodierung anbiete. weitere Codewörter und Erklärungen gibt es z.B. hier: " "Dekodierung anbiete. weitere Codewörter und Erklärungen gibt es z.B. hier: "
"https://ww2db.com/other.php?other_id=29 oder hier: " "https://ww2db.com/other.php?other_id=29 oder hier: "
"http://math.ucsd.edu/~crypto/Projects/RobertoSandoval/NavajoWindtalkers.pdf") "https://math.ucsd.edu/~crypto/Projects/RobertoSandoval/NavajoWindtalkers.pdf")
pi_suche = ("### PI Nachkommastellensuche\n" pi_suche = ("### PI Nachkommastellensuche\n"
"Für die eingegebene Zahl/Zahlenreihe X wird versucht die entsprechende X. Nachkommastelle auszugeben " "Für die eingegebene Zahl/Zahlenreihe X wird versucht die entsprechende X. Nachkommastelle auszugeben "
@@ -498,7 +498,7 @@ url_decode = ("### URL-decode\n"
"Listing erscheinen wird es manchmal schwer die ursprüngliche URL und/oder den Dateinamen des " "Listing erscheinen wird es manchmal schwer die ursprüngliche URL und/oder den Dateinamen des "
"Bildes noch lesen zu können. Die URL hat dort nun jeweils folgendes Muster: " "Bildes noch lesen zu können. Die URL hat dort nun jeweils folgendes Muster: "
"`https://imgproxy.geocaching.com/......?url=.....` wobei nach dem `?url=` dann die ursprüngliche " "`https://imgproxy.geocaching.com/......?url=.....` wobei nach dem `?url=` dann die ursprüngliche "
"Bild-URL folgt, allerdings wird dabei dann aus `http://` folgendes `http%3A%2F%2F` und um genau " "Bild-URL folgt, allerdings wird dabei dann aus `https://` folgendes `https%3A%2F%2F` und um genau "
"dieses wieder normal lesbar zu machen dient diese Funktion") "dieses wieder normal lesbar zu machen dient diese Funktion")
reversewig = ('### Reverse Wherigo zu Koordinaten\n' reversewig = ('### Reverse Wherigo zu Koordinaten\n'

View File

@@ -172,11 +172,14 @@ def zeichenanzahl(eingabetext):
else: else:
anzahl[b] = 1 anzahl[b] = 1
s = [] s = []
zeichensumme = 0
for key in anzahl: for key in anzahl:
s.append(key) s.append(key)
zeichensumme += anzahl[key]
s.sort() s.sort()
ausgabetext += f"Es wurden __:orange[{len(anzahl)}]__ unterschiedliche Zeichen gefunden. \n" ausgabetext += (f"Es wurden __:orange[{len(anzahl)}]__ unterschiedliche Zeichen in insgesamt "
ausgabetext += "| :blue[Zeichen] | :blue[Anzahl] |\n|----|----|\n" f"__:orange[{zeichensumme}]__ Zeichen gefunden. \n"
f"| :blue[Zeichen] | :blue[Anzahl] |\n|----|----|\n")
for i in s: for i in s:
if ord(i) == 9: if ord(i) == 9:
ausgabetext += f"|TAB|{anzahl[i]}|\n" ausgabetext += f"|TAB|{anzahl[i]}|\n"
@@ -2306,6 +2309,8 @@ def adfgx_kodieren(eingabetext, pw):
return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]" return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]"
pw[0] = pw[0].strip() pw[0] = pw[0].strip()
pw[1] = pw[1].strip() pw[1] = pw[1].strip()
if not pw[0].isalpha() or not pw[1].isalpha():
return ":red[Passwörter dürfen nur aus Buchstaben bestehen!]"
pw1az = "" pw1az = ""
for b in pw[0] + alpha_az: for b in pw[0] + alpha_az:
if b in alpha_az and b not in pw1az: if b in alpha_az and b not in pw1az:
@@ -2331,7 +2336,6 @@ def adfgx_kodieren(eingabetext, pw):
ctext1az = "" ctext1az = ""
for b in klartext: for b in klartext:
ctext1az += w_baz[b] ctext1az += w_baz[b]
print(ctext1az)
ctext1za = "" ctext1za = ""
for b in klartext: for b in klartext:
ctext1za += w_bza[b] ctext1za += w_bza[b]
@@ -2367,7 +2371,8 @@ def adfgx_kodieren(eingabetext, pw):
else: else:
ausgabe_za += i[j + 1] ausgabe_za += i[j + 1]
z += 1 z += 1
ausgabetext = f":blue[Passwort 1:] {pw[0]} \n" ausgabetext = f":blue[verwendeter Eingabetext:] {klartext} \n"
ausgabetext += f":blue[Passwort 1:] {pw[0]} \n"
ausgabetext += f":blue[Passwort 2:] {pw2} \n \n" ausgabetext += f":blue[Passwort 2:] {pw2} \n \n"
ausgabetext += f":blue[kodiert mit Variante A-Z:] \n{ausgabe_az} \n \n" ausgabetext += f":blue[kodiert mit Variante A-Z:] \n{ausgabe_az} \n \n"
ausgabetext += f":blue[kodiert mit Variante Z-A:] \n{ausgabe_za}" ausgabetext += f":blue[kodiert mit Variante Z-A:] \n{ausgabe_za}"
@@ -2387,6 +2392,7 @@ def adfgx_dekodieren(eingabetext, pw):
alpha_za = "ZYXWVUTSRQPONMLKIHGFEDCBA" alpha_za = "ZYXWVUTSRQPONMLKIHGFEDCBA"
text = text.upper() text = text.upper()
text = text.replace("J", "I") text = text.replace("J", "I")
text = text.replace(" ", "")
pw = pw.upper() pw = pw.upper()
pw = pw.replace("J", "I") pw = pw.replace("J", "I")
pw = pw.split(",") pw = pw.split(",")
@@ -2394,6 +2400,11 @@ def adfgx_dekodieren(eingabetext, pw):
return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]" return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]"
pw[0] = pw[0].strip() pw[0] = pw[0].strip()
pw[1] = pw[1].strip() pw[1] = pw[1].strip()
if not pw[0].isalpha() or not pw[1].isalpha():
return ":red[Passwörter dürfen nur aus Buchstaben bestehen!]"
for zeichen in text:
if zeichen not in "ADFGX":
return ":red[Im Eingabetext dürfen nur Leerzeichen und die Buchstaben ADFGX vorkommen!]"
pw1az = "" pw1az = ""
for b in pw[0] + alpha_az: for b in pw[0] + alpha_az:
if b in alpha_az and b not in pw1az: if b in alpha_az and b not in pw1az:
@@ -2463,6 +2474,8 @@ def adfgvx_kodieren(eingabetext, pw):
return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]" return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]"
pw[0] = pw[0].strip() pw[0] = pw[0].strip()
pw[1] = pw[1].strip() pw[1] = pw[1].strip()
if not pw[0].isalnum() or not pw[1].isalnum():
return ":red[Passwörter dürfen nur aus Buchstaben und Ziffern bestehen!]"
pw1az09 = "" pw1az09 = ""
for b in pw[0] + alpha_az09: for b in pw[0] + alpha_az09:
if b in alpha_az09 and b not in pw1az09: if b in alpha_az09 and b not in pw1az09:
@@ -2523,7 +2536,8 @@ def adfgvx_kodieren(eingabetext, pw):
else: else:
ausgabe_90za += i[j + 1] ausgabe_90za += i[j + 1]
z += 1 z += 1
ausgabetext = f":blue[Passwort 1:] {pw[0]} \n" ausgabetext = f":blue[verwendeter Eingabetext:] {klartext} \n"
ausgabetext += f":blue[Passwort 1:] {pw[0]} \n"
ausgabetext += f":blue[Passwort 2:] {pw2} \n \n" ausgabetext += f":blue[Passwort 2:] {pw2} \n \n"
ausgabetext += f":blue[kodiert mit Variante A-Z,0-9:] \n{ausgabe_az09} \n \n" ausgabetext += f":blue[kodiert mit Variante A-Z,0-9:] \n{ausgabe_az09} \n \n"
ausgabetext += f":blue[kodiert mit Variante 9-0,Z-A:] \n{ausgabe_90za}" ausgabetext += f":blue[kodiert mit Variante 9-0,Z-A:] \n{ausgabe_90za}"
@@ -2543,12 +2557,18 @@ def adfgvx_dekodieren(eingabetext, pw):
alpha_az09 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" alpha_az09 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
alpha_90za = "9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA" alpha_90za = "9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA"
text = text.upper() text = text.upper()
text = text.replace(" ", "")
pw = pw.upper() pw = pw.upper()
pw = pw.split(",") pw = pw.split(",")
if len(pw) != 2: if len(pw) != 2:
return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]" return ":red[Es es werden genau zwei durch Komma getrennte Passwörter benötigt!]"
pw[0] = pw[0].strip() pw[0] = pw[0].strip()
pw[1] = pw[1].strip() pw[1] = pw[1].strip()
if not pw[0].isalnum() or not pw[1].isalnum():
return ":red[Passwörter dürfen nur aus Buchstaben und Ziffern bestehen!]"
for zeichen in text:
if zeichen not in "ADFGVX":
return ":red[Im Eingabetext dürfen nur Leerzeichen und die Buchstaben ADFGVX vorkommen!]"
pw1az09 = "" pw1az09 = ""
for b in pw[0] + alpha_az09: for b in pw[0] + alpha_az09:
if b in alpha_az09 and b not in pw1az09: if b in alpha_az09 and b not in pw1az09:

View File

@@ -1,84 +0,0 @@
# Copyright (c) 2025 Martin Kayser (tebarius)
# Licensed under the MIT License. See LICENSE file in the project root.
# start with: streamlit run examples.py
import streamlit as st
st.write("# Hello World !")
import pandas as pd
df = pd.DataFrame({
'first column': [1, 2, 3, 4],
'second column': [10, 20, 30, 40]
})
df
st.table(df)
import pandas as pd
st.map(pd.DataFrame({'lat':[52.3642], 'lon':[13.0906]}), zoom=17, size=5)
import streamlit as st
x = st.slider('x', 32, 233) # 👈 this is a widget
st.write(x, 'squared is', x * x)
st.text_input("Your name", key="name")
# You can access the value at any point with:
st.session_state.name
import numpy as np
import pandas as pd
if st.checkbox('Show dataframe'):
chart_data = pd.DataFrame(
np.random.randn(20, 3),
columns=['a', 'b', 'c'])
chart_data
import pandas as pd
df = pd.DataFrame({
'first column': [1, 2, 3, 4],
'second column': [10, 20, 30, 40]
})
option = st.selectbox(
'Which number do you like best?',
df['first column'])
'You selected: ', option
add_selectbox = st.sidebar.selectbox(
'How would you like to be contacted?',
('Email', 'Home phone', 'Mobile phone')
)
# Add a slider to the sidebar:
add_slider = st.sidebar.slider(
'Select a range of values',
0.0, 100.0, (25.0, 75.0)
)
add_selectbox
add_slider
left_column, right_column = st.columns(2)
# You can use a column just like st.sidebar:
left_column.button('Press me!')
# Or even better, call Streamlit functions inside a "with" block:
with right_column:
chosen = st.radio(
'Sorting hat',
("Gryffindor", "Ravenclaw", "Hufflepuff", "Slytherin"))
st.write(f"You are in {chosen} house!")
st.progress(80)
st.text_area("Your text", key="te")
st.session_state.te

View File

@@ -7,25 +7,31 @@ Für die Umsetzung kommt das Python-Framework [Streamlit](https://streamlit.io/)
Der einfachst Weg, um die App lokal laufen zu lassen, ist mit Docker, wobei ich hier mal 3 Möglichkeiten aufzeigen (_das vorgebaute Image ist als Multi-Arch-Image für linux/amd64,linux/arm64 gebaut und sollte somit z.B. auch auf Mac's und neueren Raspberry's mit 64 bit Betriebssystem laufen_): Der einfachst Weg, um die App lokal laufen zu lassen, ist mit Docker, wobei ich hier mal 3 Möglichkeiten aufzeigen (_das vorgebaute Image ist als Multi-Arch-Image für linux/amd64,linux/arm64 gebaut und sollte somit z.B. auch auf Mac's und neueren Raspberry's mit 64 bit Betriebssystem laufen_):
1. mit vorgebautem Image: 1. mit vorgebautem Image:
- `docker run --rm -d -p 8501:8501 gitea.tebarius.duckdns.org/tebarius/mysteryhelfer` - `docker run --rm -d -p 8501:8501 gitea.tebarius.duckdns.org/tebarius/mysteryhelfer`
- alternativ (via Docker Hub): `docker run --rm -d -p 8501:8501 tebarius/mysteryhelfer`
2. mit vorgebautem Image und "docker compose" 2. mit vorgebautem Image und "docker compose"
- docker-compose.yml erstellen mit folgendem Inhalt: - docker-compose.yml erstellen mit folgendem Inhalt:
``` ```
services: services:
mysteryhelfer-web: mysteryhelfer-web:
image: gitea.tebarius.duckdns.org/tebarius/mysteryhelfer image: gitea.tebarius.duckdns.org/tebarius/mysteryhelfer
ports: ports:
- "8501:8501" - "8501:8501"
restart: unless-stopped restart: unless-stopped
``` ```
- `docker compose up` alternativ (via Docker Hub):
```
services:
mysteryhelfer-web:
image: tebarius/mysteryhelfer
ports:
- "8501:8501"
restart: unless-stopped
```
- `docker compose up -d`
3. mit selbst gebautem Image aus dem Quellcode 3. mit selbst gebautem Image aus dem Quellcode
- `git clone https://gitea.tebarius.duckdns.org/tebarius/Mysteryhelfer-web.git` - `git clone https://gitea.tebarius.duckdns.org/tebarius/Mysteryhelfer-web.git`
- `cd Mysteryhelfer-web` - `cd Mysteryhelfer-web`
- `docker compose up` - `docker compose up -d`
bei allen 3 Varianten ist die App anschließend im Browser unter http://127.0.0.1:8501/ aufrufbar bei allen 3 Varianten ist die App anschließend im Browser unter http://127.0.0.1:8501/ aufrufbar
__HINWEIS: Beim ersten Aufruf der Adresse werden Special-Wörterbücher für RE-Morse und T9 generiert,
was ca. 15-30s in Anspruch nimmt und dadurch das Laden der Seite einmalig verzögert.__

Binary file not shown.