From c5131cb272b6f55dd7ef20523367e0c855974e8a Mon Sep 17 00:00:00 2001 From: Tetiana Mohorian Date: Tue, 22 Apr 2025 16:58:15 +0000 Subject: [PATCH] =?UTF-8?q?Nahr=C3=A1t=20soubory=20do=20=E2=80=9Ehatespeec?= =?UTF-8?q?happ=E2=80=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hatespeechapp/app.py | 84 ++++++++++++++++++ hatespeechapp/gui.py | 158 +++++++++++++++++++++++++++++++++ hatespeechapp/requirements.txt | Bin 0 -> 166 bytes hatespeechapp/run_all.py | 7 ++ hatespeechapp/run_all.spec | 38 ++++++++ 5 files changed, 287 insertions(+) create mode 100644 hatespeechapp/app.py create mode 100644 hatespeechapp/gui.py create mode 100644 hatespeechapp/requirements.txt create mode 100644 hatespeechapp/run_all.py create mode 100644 hatespeechapp/run_all.spec diff --git a/hatespeechapp/app.py b/hatespeechapp/app.py new file mode 100644 index 0000000..2164546 --- /dev/null +++ b/hatespeechapp/app.py @@ -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) diff --git a/hatespeechapp/gui.py b/hatespeechapp/gui.py new file mode 100644 index 0000000..a94f797 --- /dev/null +++ b/hatespeechapp/gui.py @@ -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("", 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() + + diff --git a/hatespeechapp/requirements.txt b/hatespeechapp/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e65bbe90b3a111dc2a1c2ac3667ed615a2a7e50a GIT binary patch literal 166 zcmZXNNeX~45CrQi_!J)%#ErOM62GtZjGzZEL#C^ybDcdiflQ>P;@cWpHj>Cc4`L$| qD}&SGre)?9T?MhMHfvbu-kz32x%rD`^p4`(E|XrnjLx0sXUlJk93CYA literal 0 HcmV?d00001 diff --git a/hatespeechapp/run_all.py b/hatespeechapp/run_all.py new file mode 100644 index 0000000..959ad97 --- /dev/null +++ b/hatespeechapp/run_all.py @@ -0,0 +1,7 @@ +import subprocess + +# Запуск Flask API (если у тебя есть backend) +subprocess.Popen(["python", "app.py"]) + +# Запуск GUI +subprocess.run(["python", "gui.py"]) diff --git a/hatespeechapp/run_all.spec b/hatespeechapp/run_all.spec new file mode 100644 index 0000000..51d9a19 --- /dev/null +++ b/hatespeechapp/run_all.spec @@ -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, +)