Nahrát soubory do „hatespeechapp“
This commit is contained in:
parent
469cde70aa
commit
c5131cb272
84
hatespeechapp/app.py
Normal file
84
hatespeechapp/app.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS
|
||||||
|
import json
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
||||||
|
from flask_caching import Cache
|
||||||
|
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app)
|
||||||
|
|
||||||
|
app.config['CACHE_TYPE'] = 'SimpleCache'
|
||||||
|
cache = Cache(app)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
model_path = "./hate_speech_model/final_model"
|
||||||
|
|
||||||
|
|
||||||
|
tokenizer = AutoTokenizer.from_pretrained(model_path)
|
||||||
|
model = AutoModelForSequenceClassification.from_pretrained(model_path)
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_text_hash(text):
|
||||||
|
return hashlib.md5(text.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
@app.route("/api/predict", methods=["POST"])
|
||||||
|
def predict():
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
text = data.get("text", "")
|
||||||
|
|
||||||
|
text_hash = generate_text_hash(text)
|
||||||
|
cached_result = cache.get(text_hash)
|
||||||
|
if cached_result:
|
||||||
|
return jsonify({"prediction": cached_result}), 200
|
||||||
|
|
||||||
|
|
||||||
|
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
|
||||||
|
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
outputs = model(**inputs)
|
||||||
|
predictions = torch.argmax(outputs.logits, dim=1).item()
|
||||||
|
|
||||||
|
prediction_label = "Pravdepodobne toxický" if predictions == 1 else "Neutrálny text"
|
||||||
|
|
||||||
|
cache.set(text_hash, prediction_label)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
response = app.response_class(
|
||||||
|
response=json.dumps({"prediction": prediction_label}, ensure_ascii=False),
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
app.run(host="127.0.0.1", port=5000, threaded=True)
|
158
hatespeechapp/gui.py
Normal file
158
hatespeechapp/gui.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import customtkinter as ctk
|
||||||
|
from PIL import ImageEnhance, ImageFilter,Image
|
||||||
|
import requests
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- APP CONFIG ----------
|
||||||
|
ctk.set_appearance_mode("dark")
|
||||||
|
ctk.set_default_color_theme("dark-blue")
|
||||||
|
|
||||||
|
app = ctk.CTk()
|
||||||
|
|
||||||
|
app.geometry("1000x650")
|
||||||
|
app.minsize(400, 400)
|
||||||
|
app.title("Detektor nenávistného jazyka")
|
||||||
|
app.configure(bg="transparent")
|
||||||
|
|
||||||
|
# ---------- BACKGROUND ----------
|
||||||
|
bg_image_pil = Image.open("images/image.png").convert("RGB")
|
||||||
|
bg_image_pil = bg_image_pil.filter(ImageFilter.GaussianBlur(radius=3)) # мягкий блюр
|
||||||
|
bg_image_pil = ImageEnhance.Brightness(bg_image_pil).enhance(0.6)
|
||||||
|
|
||||||
|
bg_image = ctk.CTkImage(light_image=bg_image_pil, dark_image=bg_image_pil, size=(1920, 1080))
|
||||||
|
|
||||||
|
|
||||||
|
bg_label = ctk.CTkLabel(app, image=bg_image, text="")
|
||||||
|
bg_label.place(relx=0, rely=0, relwidth=1, relheight=1)
|
||||||
|
|
||||||
|
# ---------- HEADER ----------#1a1a1a
|
||||||
|
header_frame = ctk.CTkFrame(app, fg_color="transparent")
|
||||||
|
header_frame.pack(side="top", fill="x")
|
||||||
|
|
||||||
|
title = ctk.CTkLabel(
|
||||||
|
header_frame,
|
||||||
|
text="Detektor nenávistného jazyka",
|
||||||
|
font=ctk.CTkFont(size=26, weight="bold"),
|
||||||
|
text_color="white"
|
||||||
|
)
|
||||||
|
title.pack(pady=10)
|
||||||
|
|
||||||
|
# ---------- MAIN CARD ----------
|
||||||
|
#1a1a1a
|
||||||
|
|
||||||
|
#main_card_frame = ctk.CTkFrame(app, fg_color="transparent")
|
||||||
|
main_card_frame = ctk.CTkFrame(app, fg_color="#1a1a1a", corner_radius=25)
|
||||||
|
main_card_frame.place(relx=0.5, rely=0.5, anchor="center", relwidth=0.9)
|
||||||
|
|
||||||
|
|
||||||
|
subtitle = tk.Label(
|
||||||
|
master=main_card_frame,
|
||||||
|
text="Analyzujte text na nenávistný jazyk",
|
||||||
|
font=("Arial", 28, "bold"),
|
||||||
|
bg="#1a1a1a",
|
||||||
|
fg="white",
|
||||||
|
wraplength=700,
|
||||||
|
justify="center"
|
||||||
|
)
|
||||||
|
subtitle.pack(fill="x", padx=20)
|
||||||
|
|
||||||
|
description = tk.Label(
|
||||||
|
master=main_card_frame,
|
||||||
|
text="Tento nástroj využíva umelú inteligenciu na identifikáciu toxického obsahu v textoch.\n"
|
||||||
|
"Stačí zadať text a zistiť, či obsahuje nenávistný jazyk.",
|
||||||
|
font=("Arial", 14),
|
||||||
|
bg="#1a1a1a",
|
||||||
|
fg="white",
|
||||||
|
justify="center",
|
||||||
|
wraplength=600
|
||||||
|
)
|
||||||
|
description.pack(fill="x", padx=20)
|
||||||
|
|
||||||
|
input_box = ctk.CTkTextbox(main_card_frame, height=100, corner_radius=15)
|
||||||
|
input_box.pack(fill="x", padx=20)
|
||||||
|
|
||||||
|
send_btn = ctk.CTkButton(main_card_frame, text="📤 Analyzovať", command=lambda: send_request(), width=150, height=40)
|
||||||
|
send_btn.pack(pady=(10, 30))
|
||||||
|
|
||||||
|
# ---------- FOOTER ----------
|
||||||
|
footer_frame = ctk.CTkFrame(app, fg_color="transparent", corner_radius=20)
|
||||||
|
footer_frame.pack(side="bottom", fill="x")
|
||||||
|
|
||||||
|
footer = ctk.CTkLabel(footer_frame, text="Created by Tetiana Mohorian", font=ctk.CTkFont(size=12), text_color="white")
|
||||||
|
footer.pack(pady=8)
|
||||||
|
|
||||||
|
# ---------- ADAPTIVE PADDING ----------
|
||||||
|
def adapt_padding(event=None):
|
||||||
|
h = app.winfo_height()
|
||||||
|
w = app.winfo_width()
|
||||||
|
scale = h / 650
|
||||||
|
|
||||||
|
# Размеры шрифтов в зависимости от ширины окна
|
||||||
|
if w >= 900:
|
||||||
|
desc_font_size = 30
|
||||||
|
subtitle_font_size = 38
|
||||||
|
elif w >= 700:
|
||||||
|
subtitle_font_size = 32
|
||||||
|
desc_font_size = 20
|
||||||
|
elif w >= 500:
|
||||||
|
subtitle_font_size = 26
|
||||||
|
desc_font_size = 20
|
||||||
|
else:
|
||||||
|
subtitle_font_size = 18
|
||||||
|
desc_font_size = 16
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
subtitle.configure(font=("Arial", subtitle_font_size, "bold"))
|
||||||
|
description.configure(font=("Arial", desc_font_size))
|
||||||
|
|
||||||
|
# Отступы
|
||||||
|
subtitle.pack_configure(pady=(int(20 * scale), int(10 * scale)))
|
||||||
|
description.pack_configure(pady=(0, int(20 * scale)))
|
||||||
|
input_box.pack_configure(pady=(0, int(20 * scale)))
|
||||||
|
send_btn.pack_configure(pady=(int(10 * scale), int(30 * scale)))
|
||||||
|
|
||||||
|
def update_wrap():
|
||||||
|
wrap = max(int(w * 0.9), 250)
|
||||||
|
subtitle.configure(wraplength=wrap)
|
||||||
|
description.configure(wraplength=wrap)
|
||||||
|
|
||||||
|
app.after(30, update_wrap)
|
||||||
|
|
||||||
|
app.bind("<Configure>", adapt_padding)
|
||||||
|
|
||||||
|
# ---------- SEND REQUEST ----------
|
||||||
|
def send_request():
|
||||||
|
text = input_box.get("1.0", "end-1c").strip()
|
||||||
|
if not text:
|
||||||
|
subtitle.configure(text="Zadajte text", fg="red")
|
||||||
|
description.configure(text="")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
"http://localhost:5000/api/predict",
|
||||||
|
json={"text": text}
|
||||||
|
)
|
||||||
|
if response.status_code == 200:
|
||||||
|
result = response.json()["prediction"]
|
||||||
|
subtitle.configure(
|
||||||
|
text=result,
|
||||||
|
fg=("orange" if "toxický" in result.lower() else "white")
|
||||||
|
)
|
||||||
|
description.configure(text=f'Váš text bol: "{text}"')
|
||||||
|
else:
|
||||||
|
subtitle.configure(text="Chyba na serveri", fg="orange")
|
||||||
|
description.configure(text="")
|
||||||
|
except Exception as e:
|
||||||
|
subtitle.configure(text=f"Chyba: {e}", fg="red")
|
||||||
|
description.configure(text="")
|
||||||
|
|
||||||
|
# ---------- START ----------
|
||||||
|
app.mainloop()
|
||||||
|
|
||||||
|
|
BIN
hatespeechapp/requirements.txt
Normal file
BIN
hatespeechapp/requirements.txt
Normal file
Binary file not shown.
7
hatespeechapp/run_all.py
Normal file
7
hatespeechapp/run_all.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Запуск Flask API (если у тебя есть backend)
|
||||||
|
subprocess.Popen(["python", "app.py"])
|
||||||
|
|
||||||
|
# Запуск GUI
|
||||||
|
subprocess.run(["python", "gui.py"])
|
38
hatespeechapp/run_all.spec
Normal file
38
hatespeechapp/run_all.spec
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['run_all.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='run_all',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user