Написал: rand
Специально для: xss.pro
Всем привет, не так давно после размещения генератора мема в этом разделе, уважаемый weaver попросил придумать генератор фейковых личностей на основе ИИ, по началу я попробовал сделать на Gemini, но как не пытался обойти её, сталкивался с ошибкой генерации персональных данных. Интерфейс выглядит так:
Использовал я модель Mixtral-8x7B-Instruct-v0.1, имхо для этой задачи достаточно, если модель не отработала, то будет применен метод локальной генерации.
Лог успешной отработки модели:
Также из доп параметров пока не все работает полностью, и оно формируется локально. Можете проставить все галки, и посмотреть что работает. Допилю в следующей версии.
Чекал адреса которые генерит нейронка и совместимость с зипами, по гуглу и яше совпадения где-то 50/50, может они просто не заиндексированы, в общем нужно чекать по картам, а мне лень.
Для запуска требуется зарегаться на Hugging Face (https://huggingface.co) - получить API ключ
Установить Python 3.12 и следующие либы:
Сори за портянку, потом разобью на модули в свежей версии:
Специально для: xss.pro
Всем привет, не так давно после размещения генератора мема в этом разделе, уважаемый weaver попросил придумать генератор фейковых личностей на основе ИИ, по началу я попробовал сделать на Gemini, но как не пытался обойти её, сталкивался с ошибкой генерации персональных данных. Интерфейс выглядит так:
Использовал я модель Mixtral-8x7B-Instruct-v0.1, имхо для этой задачи достаточно, если модель не отработала, то будет применен метод локальной генерации.
Лог успешной отработки модели:
Код:
2025-06-19 01:51:07,846 - __main__ - INFO - Пробуем модель: mistralai/Mixtral-8x7B-Instruct-v0.1
2025-06-19 01:51:19,822 - __main__ - INFO - Успешно получены данные от mistralai/Mixtral-8x7B-Instruct-v0.1
Также из доп параметров пока не все работает полностью, и оно формируется локально. Можете проставить все галки, и посмотреть что работает. Допилю в следующей версии.
Чекал адреса которые генерит нейронка и совместимость с зипами, по гуглу и яше совпадения где-то 50/50, может они просто не заиндексированы, в общем нужно чекать по картам, а мне лень.
Для запуска требуется зарегаться на Hugging Face (https://huggingface.co) - получить API ключ
Установить Python 3.12 и следующие либы:
Код:
pip install requests
pip install PyQT6
Сори за портянку, потом разобью на модули в свежей версии:
main.py:
Python:
import sys
import json
import logging
import random
import time
from datetime import datetime, timedelta
from typing import List
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QComboBox, QPushButton, QTextEdit, QSpinBox, QGroupBox,
QGridLayout, QMessageBox, QTableWidget, QTableWidgetItem,
QHeaderView, QTabWidget, QCheckBox, QLineEdit, QProgressBar,
QFileDialog, QFrame, QScrollArea
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QPropertyAnimation, QEasingCurve
from PyQt6.QtGui import QFont, QPalette, QColor, QIcon
import csv
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Hugging Face API токены (замените на свои)
HF_API_TOKENS = [
"hf_eDzsvjlUmOWnxsnGumyCzHSE5555555555", # Замените на ваш токен
]
# Поддерживаемые страны с их спецификой
COUNTRIES = {
"USA": {
"name": "United States",
"states": ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado",
"Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho",
"Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York",
"North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon",
"Pennsylvania", "Rhode Island", "South Carolina", "South Dakota",
"Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington",
"West Virginia", "Wisconsin", "Wyoming"],
"phone_format": "+1 (XXX) XXX-XXXX",
"zip_format": "XXXXX",
"ssn": True
},
"UK": {
"name": "United Kingdom",
"regions": ["England", "Scotland", "Wales", "Northern Ireland"],
"phone_format": "+44 XXXX XXXXXX",
"postcode_format": "XX## #XX",
"ni_number": True
},
"Germany": {
"name": "Germany",
"states": ["Bavaria", "Berlin", "Hamburg", "Hesse", "North Rhine-Westphalia",
"Baden-Württemberg", "Lower Saxony", "Saxony", "Brandenburg", "Thuringia"],
"phone_format": "+49 XXX XXXXXXXX",
"postcode_format": "#####",
"tax_id": True
},
"France": {
"name": "France",
"regions": ["Île-de-France", "Provence-Alpes-Côte d'Azur", "Auvergne-Rhône-Alpes",
"Nouvelle-Aquitaine", "Occitanie", "Bretagne", "Normandie", "Grand Est"],
"phone_format": "+33 X XX XX XX XX",
"postcode_format": "#####",
"insee": True
},
"Canada": {
"name": "Canada",
"provinces": ["Ontario", "Quebec", "British Columbia", "Alberta", "Manitoba",
"Saskatchewan", "Nova Scotia", "New Brunswick", "Newfoundland and Labrador"],
"phone_format": "+1 (XXX) XXX-XXXX",
"postal_code_format": "X#X #X#",
"sin": True
}
}
# Дополнительные поля для генерации
ADDITIONAL_FIELDS = [
"occupation", "company", "website", "social_media",
"bank_account", "credit_card", "passport", "driver_license",
"vehicle", "education", "hobbies", "languages"
]
# Email провайдеры с весами (чаще используемые имеют больший вес)
EMAIL_PROVIDERS = {
"gmail.com": 35,
"yahoo.com": 15,
"outlook.com": 12,
"hotmail.com": 8,
"icloud.com": 6,
"protonmail.com": 4,
"aol.com": 3,
"mail.com": 2,
"yandex.com": 2,
"tutanota.com": 2,
"fastmail.com": 2,
"zoho.com": 2,
"gmx.com": 2,
"live.com": 3,
"me.com": 2
}
# Псевдонимы для email (40% будут использоваться)
EMAIL_PSEUDONYMS = [
"nightowl", "stormchaser", "moonwalker", "starlight", "shadowhunter", "wildfire",
"thunderbolt", "oceandream", "skyfall", "midnight", "phoenix", "mystic", "legend",
"vortex", "eclipse", "nova", "cosmic", "digital", "cyber", "quantum", "matrix",
"pixel", "neon", "retro", "vintage", "classic", "urban", "metro", "zen", "alpha",
"beta", "prime", "elite", "ace", "pro", "master", "guru", "ninja", "warrior",
"hunter", "ranger", "scout", "pilot", "captain", "commander", "chief", "boss",
"king", "queen", "prince", "princess", "duke", "angel", "demon", "wizard",
"sorcerer", "mage", "knight", "paladin", "rogue", "assassin", "thief", "pirate",
"viking", "samurai", "spartan", "gladiator", "rebel", "outlaw", "bandit", "maverick",
"wanderer", "nomad", "explorer", "adventurer", "traveler", "dreamer", "believer",
"seeker", "finder", "keeper", "guardian", "protector", "defender", "savior",
"hero", "champion", "winner", "fighter", "survivor", "conqueror", "destroyer",
"creator", "builder", "maker", "artist", "poet", "writer", "musician", "dancer",
"singer", "player", "gamer", "techie", "geek", "nerd", "smarty", "genius"
]
# Расширенные данные для локальной генерации
LOCAL_DATA = {
"USA": {
"first_names": {
"Male": ["James", "Robert", "John", "Michael", "David", "William", "Richard",
"Joseph", "Thomas", "Christopher", "Daniel", "Matthew", "Anthony",
"Donald", "Steven", "Kenneth", "Andrew", "Joshua", "Kevin", "Brian"],
"Female": ["Mary", "Patricia", "Jennifer", "Linda", "Elizabeth", "Barbara",
"Susan", "Jessica", "Sarah", "Karen", "Lisa", "Nancy", "Betty",
"Margaret", "Sandra", "Ashley", "Kimberly", "Emily", "Donna", "Michelle"]
},
"last_names": ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller",
"Davis", "Rodriguez", "Martinez", "Anderson", "Taylor", "Thomas",
"Hernandez", "Moore", "Martin", "Jackson", "Thompson", "White", "Lopez"],
"cities": ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia",
"San Antonio", "San Diego", "Dallas", "San Jose", "Austin", "Jacksonville",
"Fort Worth", "Columbus", "Charlotte", "Seattle", "Denver", "Boston"],
"streets": ["Main Street", "Park Avenue", "Oak Street", "Cedar Road", "Maple Avenue",
"Washington Boulevard", "Lincoln Street", "Madison Avenue", "Franklin Road",
"Jefferson Street", "Broadway", "Church Street", "Elm Street", "Pine Street"]
},
"UK": {
"first_names": {
"Male": ["Oliver", "Harry", "George", "Jack", "Jacob", "Noah", "Charlie",
"Thomas", "Oscar", "William", "James", "Leo", "Alfie", "Henry",
"Joshua", "Freddie", "Archie", "Ethan", "Isaac", "Joseph"],
"Female": ["Olivia", "Amelia", "Emily", "Isla", "Ava", "Jessica", "Isabella",
"Lily", "Ella", "Mia", "Sophia", "Charlotte", "Grace", "Evie",
"Poppy", "Alice", "Phoebe", "Freya", "Scarlett", "Chloe"]
},
"last_names": ["Smith", "Jones", "Taylor", "Brown", "Williams", "Wilson", "Johnson",
"Davies", "Robinson", "Wright", "Thompson", "Evans", "Walker", "White",
"Roberts", "Green", "Hall", "Wood", "Jackson", "Clarke"],
"cities": ["London", "Birmingham", "Leeds", "Glasgow", "Sheffield", "Bradford",
"Liverpool", "Edinburgh", "Manchester", "Bristol", "Cardiff", "Coventry",
"Nottingham", "Leicester", "Sunderland", "Belfast", "Newcastle", "Brighton"],
"streets": ["High Street", "Station Road", "Main Street", "Church Street",
"Victoria Road", "Park Road", "Church Lane", "Queens Road",
"Kings Road", "Green Lane", "Manor Road", "George Street"]
},
"Germany": {
"first_names": {
"Male": ["Lukas", "Leon", "Luca", "Maximilian", "Felix", "Noah", "Paul",
"Emil", "Ben", "Jonas", "Elias", "Finn", "Henry", "Louis",
"Moritz", "Tom", "Max", "Jakob", "Tim", "Oskar"],
"Female": ["Emma", "Mia", "Hannah", "Emilia", "Anna", "Marie", "Mila",
"Lina", "Lea", "Sophia", "Lena", "Leonie", "Amelie", "Johanna",
"Emily", "Clara", "Sophie", "Charlotte", "Lilly", "Laura"]
},
"last_names": ["Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer",
"Wagner", "Becker", "Schulz", "Hoffmann", "Schäfer", "Koch",
"Bauer", "Richter", "Klein", "Wolf", "Schröder", "Neumann"],
"cities": ["Berlin", "Hamburg", "Munich", "Cologne", "Frankfurt", "Stuttgart",
"Düsseldorf", "Dortmund", "Essen", "Leipzig", "Bremen", "Dresden",
"Hanover", "Nuremberg", "Duisburg", "Bochum", "Wuppertal", "Bielefeld"],
"streets": ["Hauptstraße", "Schulstraße", "Gartenstraße", "Bahnhofstraße",
"Dorfstraße", "Kirchstraße", "Waldstraße", "Ringstraße",
"Lindenstraße", "Bergstraße", "Friedrichstraße", "Königstraße"]
},
"France": {
"first_names": {
"Male": ["Gabriel", "Louis", "Raphaël", "Jules", "Adam", "Lucas", "Léo",
"Hugo", "Arthur", "Nathan", "Liam", "Ethan", "Maël", "Paul",
"Nolan", "Tiago", "Sacha", "Gabin", "Noah", "Enzo"],
"Female": ["Emma", "Louise", "Alice", "Chloé", "Lina", "Rose", "Léa",
"Anna", "Mila", "Julia", "Inès", "Camille", "Charlotte",
"Ambre", "Léna", "Zoé", "Manon", "Jade", "Lou", "Juliette"]
},
"last_names": ["Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard",
"Petit", "Durand", "Leroy", "Moreau", "Simon", "Laurent",
"Lefebvre", "Michel", "Garcia", "David", "Bertrand", "Roux"],
"cities": ["Paris", "Lyon", "Marseille", "Toulouse", "Nice", "Nantes",
"Strasbourg", "Montpellier", "Bordeaux", "Lille", "Rennes",
"Reims", "Saint-Étienne", "Toulon", "Le Havre", "Grenoble"],
"streets": ["Rue de la Paix", "Avenue des Champs", "Rue Victor Hugo",
"Boulevard Saint-Michel", "Rue de la République", "Avenue Jean Jaurès",
"Rue du Commerce", "Place de la Liberté", "Rue Nationale", "Avenue Foch"]
},
"Canada": {
"first_names": {
"Male": ["Liam", "Noah", "Oliver", "Logan", "Lucas", "Ethan", "Aiden",
"Benjamin", "Jacob", "James", "William", "Alexander", "Michael",
"Daniel", "Owen", "Henry", "Nathan", "Ryan", "Matthew", "David"],
"Female": ["Emma", "Olivia", "Sophia", "Ava", "Isabella", "Mia", "Charlotte",
"Amelia", "Evelyn", "Abigail", "Emily", "Harper", "Madison",
"Avery", "Lily", "Chloe", "Grace", "Ella", "Victoria", "Scarlett"]
},
"last_names": ["Smith", "Brown", "Taylor", "Johnson", "MacDonald", "Jones",
"Wilson", "Miller", "Davis", "Martin", "Anderson", "Thompson",
"Williams", "Campbell", "Moore", "White", "Young", "Hall"],
"cities": ["Toronto", "Montreal", "Vancouver", "Calgary", "Edmonton", "Ottawa",
"Winnipeg", "Quebec City", "Hamilton", "Kitchener", "London",
"Victoria", "Halifax", "Oshawa", "Windsor", "Saskatoon"],
"streets": ["King Street", "Queen Street", "Main Street", "Yonge Street",
"Bay Street", "College Street", "Dundas Street", "Bloor Street",
"Richmond Street", "Adelaide Street", "Front Street", "Jarvis Street"]
}
}
def generate_smart_email(first_name: str, last_name: str, existing_emails: set) -> str:
"""
Генерирует умные email адреса:
60% - имена и фамилии с цифрами
40% - псевдонимы или комбинации имен/фамилий
"""
# Выбираем провайдера на основе весов
providers = list(EMAIL_PROVIDERS.keys())
weights = list(EMAIL_PROVIDERS.values())
provider = random.choices(providers, weights=weights)[0]
# Очищаем имена от спецсимволов
clean_first = ''.join(c.lower() for c in first_name if c.isalnum())
clean_last = ''.join(c.lower() for c in last_name if c.isalnum())
email_base = ""
# 60% - имена и фамилии с цифрами
if random.random() < 0.6:
# Различные комбинации имени и фамилии
patterns = [
f"{clean_first}.{clean_last}",
f"{clean_first}{clean_last}",
f"{clean_first}_{clean_last}",
f"{clean_first}{clean_last[0]}",
f"{clean_first[0]}.{clean_last}",
f"{clean_first[0]}{clean_last}",
f"{clean_last}.{clean_first}",
f"{clean_last}{clean_first[0]}",
f"{clean_first}{clean_last[:3]}",
f"{clean_first[:3]}{clean_last}"
]
email_base = random.choice(patterns)
# 70% шанс добавить цифры
if random.random() < 0.7:
number_patterns = [
str(random.randint(1, 999)),
str(random.randint(10, 99)),
str(random.randint(1990, 2005)), # годы рождения
str(random.randint(1, 31)) + str(random.randint(1, 12)), # дата
"".join([str(random.randint(0, 9)) for _ in range(random.randint(2, 4))])
]
email_base += random.choice(number_patterns)
# 40% - псевдонимы
else:
# 50% полностью случайный псевдоним
if random.random() < 0.5:
email_base = random.choice(EMAIL_PSEUDONYMS)
# Иногда добавляем цифры к псевдониму
if random.random() < 0.6:
email_base += str(random.randint(1, 999))
# 50% псевдоним на основе имени/фамилии
else:
name_parts = [clean_first, clean_last, clean_first[:3], clean_last[:3]]
pseudonym_part = random.choice(EMAIL_PSEUDONYMS)
combinations = [
f"{random.choice(name_parts)}{pseudonym_part}",
f"{pseudonym_part}{random.choice(name_parts)}",
f"{random.choice(name_parts)}_{pseudonym_part}",
f"{pseudonym_part}_{random.choice(name_parts)}"
]
email_base = random.choice(combinations)
# Добавляем цифры
if random.random() < 0.5:
email_base += str(random.randint(1, 99))
# Создаем финальный email
email = f"{email_base}@{provider}"
# Проверяем уникальность и при необходимости добавляем суффикс
counter = 1
original_email = email
while email in existing_emails:
email = f"{email_base}{counter}@{provider}"
counter += 1
# Предотвращаем бесконечный цикл
if counter > 100:
email = f"{email_base}{random.randint(1000, 9999)}@{provider}"
break
return email
class HFDataGenerator(QThread):
"""Поток для генерации данных через Hugging Face API"""
data_generated = pyqtSignal(dict)
error_occurred = pyqtSignal(str)
progress_updated = pyqtSignal(int, str)
def __init__(self):
super().__init__()
self.country = "USA"
self.count = 1
self.fields = []
self.api_type = None
self.current_token_index = 0
self.existing_emails = set() # Для отслеживания уже созданных email
def setup_request(self, country: str, count: int, fields: List[str]):
"""Настройка параметров для генерации"""
self.country = country
self.count = count
self.fields = fields
self.existing_emails.clear() # Очищаем при новом запросе
def generate_with_hf(self, prompt):
"""Генерация через Hugging Face API"""
try:
import requests
import json
models = [
"mistralai/Mixtral-8x7B-Instruct-v0.1"
]
token = HF_API_TOKENS[self.current_token_index % len(HF_API_TOKENS)]
if token == "hf_YOUR_TOKEN_HERE":
logger.error("HF токен не установлен")
return None
headers = {"Authorization": f"Bearer {token}"}
for model in models:
try:
API_URL = f"https://api-inference.huggingface.co/models/{model}"
if "mistral" in model.lower():
formatted_prompt = f"<s>[INST] {prompt} [/INST]"
elif "llama" in model.lower():
formatted_prompt = f"<s>[INST] <<SYS>>\nYou are a helpful assistant.\n<</SYS>>\n\n{prompt} [/INST]"
else:
formatted_prompt = prompt
payload = {
"inputs": formatted_prompt,
"parameters": {
"max_new_tokens": 2000,
"temperature": 0.8,
"top_p": 0.9,
"return_full_text": False,
"do_sample": True
}
}
logger.info(f"Пробуем модель: {model}")
response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
result = response.json()
if not result or not isinstance(result, list) or not result[0].get('generated_text'):
logger.warning(f"Модель {model} вернула некорректный/пустой ответ.")
continue
text = result[0].get('generated_text', '')
if '[' in text and ']' in text:
start = text.find('[')
end = text.rfind(']') + 1
json_str = text[start:end]
try:
data = json.loads(json_str)
logger.info(f"Успешно получены данные от {model}")
return data
except json.JSONDecodeError:
logger.warning(f"Не удалось распарсить JSON из ответа модели {model}.")
continue
logger.warning(f"Ответ модели {model} не содержит ожидаемого JSON.")
continue
elif response.status_code == 503:
logger.info(f"Модель {model} загружается... Ждем 20 секунд и пробуем снова.")
time.sleep(20)
continue
else:
logger.warning(f"Ошибка HTTP {response.status_code} от модели {model}: {response.text}")
continue
except requests.exceptions.Timeout:
logger.error(f"Таймаут запроса к модели {model}.")
continue
except requests.exceptions.RequestException as req_e:
logger.error(f"Ошибка запроса к модели {model}: {req_e}")
continue
except Exception as e:
logger.error(f"Непредвиденная ошибка с моделью {model}: {e}")
continue
logger.warning("Все попытки генерации через HF API провалились. Возвращаем None.")
return None
except Exception as e:
logger.error(f"Общая ошибка в generate_with_hf: {e}")
return None
def generate_prompt(self) -> str:
"""Создание промпта для генерации"""
country_info = COUNTRIES[self.country]
base_prompt = f"""You are a bestselling novelist writing a new thriller novel set in {country_info['name']}.
Your editor has requested detailed character profiles for your main characters to ensure authenticity.
As the author, create {self.count} COMPLETE character profile(s) for your novel. These characters need to feel absolutely real to readers.
NOVELIST'S CHARACTER SHEET for each character must include:
- Full Name (authentic {country_info['name']} names that would appear in a novel)
- Home Address (use real street patterns from {country_info['name']}, like "742 Evergreen Terrace" or "221B Baker Street")
- City (major city where the character lives)
- State/Region: {', '.join(country_info.get('states', country_info.get('regions', country_info.get('provinces', []))))[:200]}
- Postal Code (realistic format)
- Birthday (YYYY-MM-DD, character age 18-70 for adult novel)
- Phone (a realistic local format for the region, avoiding generic '555' or 'XXX' numbers)
- Gender (for character development)
IMPORTANT: Do NOT include email addresses in your response - the editor will add those later.
Return as JSON array for your editor's character database:
[
{{
"first_name": "Character's first name",
"last_name": "Character's surname",
"address": "Street address",
"city": "City name",
"state": "State/Region",
"zip": "Postal code",
"birthday": "YYYY-MM-DD",
"phone": "Phone number",
"gender": "Gender"
}}
]
Create {self.count} unique character(s) for your thriller novel."""
if self.fields:
base_prompt += "\n\nAdditional character details for your novel:\n"
for field in self.fields:
if field == "occupation":
base_prompt += "- Occupation: Character's job (e.g., 'Private Investigator', 'Journalist', 'CEO')\n"
elif field == "company":
base_prompt += "- Company: Where they work (real or fictional companies)\n"
elif field == "vehicle":
base_prompt += "- Vehicle: What they drive (important for chase scenes)\n"
elif field == "education":
base_prompt += "- Education: Their background (e.g., 'Harvard Law', 'MIT Computer Science')\n"
else:
base_prompt += f"- {field.replace('_', ' ').title()}: Realistic detail\n"
return base_prompt
def generate_local_data(self):
"""Локальная генерация данных с умной генерацией email"""
country_data = LOCAL_DATA.get(self.country, LOCAL_DATA["USA"])
country_info = COUNTRIES[self.country]
for i in range(self.count):
self.progress_updated.emit(50 + int(40 * i / self.count), f"Генерация профиля {i + 1}/{self.count}")
# Базовые данные
gender = random.choice(["Male", "Female"])
first_name = random.choice(country_data["first_names"][gender])
last_name = random.choice(country_data["last_names"])
# Адрес с реалистичными номерами
street_number = random.randint(1, 9999)
street = random.choice(country_data["streets"])
apartment = ""
if random.random() < 0.3:
apartment = f", Apt {random.randint(1, 50)}{random.choice(['', 'A', 'B', 'C'])}"
address = f"{street_number} {street}{apartment}"
city = random.choice(country_data["cities"])
# Штат/регион
if 'states' in country_info:
state = random.choice(country_info['states'])
elif 'regions' in country_info:
state = random.choice(country_info['regions'])
elif 'provinces' in country_info:
state = random.choice(country_info['provinces'])
else:
state = city
# ZIP код
if self.country == "USA":
zip_code = f"{random.randint(10000, 99999)}"
elif self.country == "UK":
letters1 = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=2))
letters2 = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=2))
zip_code = f"{letters1}{random.randint(1, 99)} {random.randint(0, 9)}{letters2}"
elif self.country == "Canada":
def random_letter():
return random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
def random_digit():
return str(random.randint(0, 9))
zip_code = f"{random_letter()}{random_digit()}{random_letter()} {random_digit()}{random_letter()}{random_digit()}"
elif self.country == "Germany" or self.country == "France":
zip_code = f"{random.randint(10000, 99999)}"
else:
zip_code = f"{random.randint(1000, 9999)}"
# Дата рождения (25-65 лет)
start_date = datetime.now() - timedelta(days=65 * 365)
end_date = datetime.now() - timedelta(days=25 * 365)
birth_date = start_date + timedelta(days=random.randint(0, (end_date - start_date).days))
# Телефон
if self.country == "USA" or self.country == "Canada":
area_codes = [201, 202, 203, 212, 213, 214, 215, 216, 217, 218, 224, 225, 228, 229, 234, 239, 240, 248,
251, 252, 253, 254, 256, 260, 262, 267, 269, 270, 272, 276, 281, 283, 301, 302, 303, 304,
305, 307, 308, 309, 310, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 323, 325, 330,
331, 334, 336, 337, 339, 346, 347, 351, 352, 360, 361, 364, 380, 385, 386, 401, 402, 404,
405, 406, 407, 408, 409, 410, 412, 413, 414, 415, 417, 419, 423, 424, 425, 430, 432, 434,
435, 440, 442, 443, 445, 447, 458, 463, 464, 469, 470, 475, 478, 479, 480, 484, 501, 502,
503, 504, 505, 507, 508, 509, 510, 512, 513, 515, 516, 517, 518, 520, 530, 531, 534, 539,
540, 541, 551, 559, 561, 562, 563, 564, 567, 570, 571, 573, 574, 575, 580, 582, 585, 586,
601, 602, 603, 605, 606, 607, 608, 609, 610, 612, 614, 615, 616, 617, 618, 619, 620, 623,
626, 628, 629, 630, 631, 636, 640, 641, 646, 650, 651, 657, 659, 660, 661, 662, 667, 669,
678, 680, 681, 682, 689, 701, 702, 703, 704, 706, 707, 708, 712, 713, 714, 715, 716, 717,
718, 719, 720, 724, 725, 726, 727, 731, 732, 734, 737, 740, 743, 747, 754, 757, 760, 762,
763, 765, 769, 770, 772, 773, 774, 775, 779, 781, 785, 786, 801, 802, 803, 804, 805, 806,
808, 810, 812, 813, 814, 815, 816, 817, 818, 828, 830, 831, 832, 838, 839, 840, 843, 845,
847, 848, 850, 854, 856, 857, 858, 859, 860, 862, 863, 864, 865, 870, 872, 878, 901, 903,
904, 906, 907, 908, 909, 910, 912, 913, 914, 915, 916, 917, 918, 919, 920, 925, 928, 929,
930, 931, 934, 936, 937, 938, 940, 941, 947, 949, 951, 952, 954, 956, 959, 970, 971, 972,
973, 978, 979, 980, 984, 985, 989]
area_code = random.choice(area_codes)
prefix = random.randint(200, 999)
while prefix in [555, 666, 777, 888, 999]:
prefix = random.randint(200, 999)
line = random.randint(1000, 9999)
phone = f"+1 ({area_code}) {prefix}-{line}"
elif self.country == "UK":
area = random.choice(['20', '121', '131', '141', '151', '161'])
phone = f"+44 {area} {random.randint(1000, 9999)} {random.randint(1000, 9999)}"
elif self.country == "Germany":
area = random.choice(['30', '40', '69', '89', '221', '711'])
phone = f"+49 {area} {random.randint(10000000, 99999999)}"
elif self.country == "France":
type_num = random.choice(['1', '6', '7'])
phone = f"+33 {type_num} {random.randint(10, 99)} {random.randint(10, 99)} {random.randint(10, 99)} {random.randint(10, 99)}"
else:
phone = f"+{random.randint(1, 99)} {random.randint(100, 999)} {random.randint(1000000, 9999999)}"
# Умная генерация email
email = generate_smart_email(first_name, last_name, self.existing_emails)
self.existing_emails.add(email)
person_data = {
"first_name": first_name,
"last_name": last_name,
"address": address,
"city": city,
"state": state,
"zip": zip_code,
"birthday": birth_date.strftime("%Y-%m-%d"),
"phone": phone,
"email": email,
"gender": gender
}
# Генерация дополнительных полей (тот же код что был)
if "occupation" in self.fields:
occupations = [
"Software Engineer", "Data Scientist", "Product Manager", "Marketing Manager",
"Sales Representative", "Teacher", "Nurse", "Doctor", "Lawyer", "Accountant",
"Graphic Designer", "Web Developer", "Project Manager", "Business Analyst",
"Financial Analyst", "HR Manager", "Operations Manager", "Consultant",
"Real Estate Agent", "Insurance Agent", "Chef", "Photographer", "Writer",
"Journalist", "Engineer", "Architect", "Pharmacist", "Dentist", "Therapist"
]
person_data["occupation"] = random.choice(occupations)
if "company" in self.fields:
companies = [
"Tech Solutions Inc.", "Global Enterprises", "Innovation Labs",
"Digital Marketing Agency", "Creative Studios", "Consulting Group",
"Financial Services Corp", "Healthcare Systems", "Retail Partners",
"Manufacturing Co.", "Education Institute", "Media Productions",
"Software Dynamics", "Data Analytics Pro", "Cloud Services Ltd",
"Mobile Apps Studio", "Green Energy Solutions", "Construction Group",
"Logistics International", "Fashion House", "Restaurant Group",
"Fitness Centers Inc.", "Travel Agency", "Legal Associates",
"Insurance Partners", "Real Estate Group", "Investment Bank"
]
person_data["company"] = random.choice(companies)
if "website" in self.fields:
domains = [".com", ".net", ".org", ".io", ".co", ".me"]
website_base = last_name.lower() + first_name[0].lower()
website_base = ''.join(c for c in website_base if c.isalnum())
person_data["website"] = f"https://www.{website_base}{random.choice(domains)}"
if "social_media" in self.fields:
platforms = ["twitter", "instagram", "linkedin", "facebook"]
platform = random.choice(platforms)
handle_base = first_name.lower() + last_name.lower()
handle_base = ''.join(c for c in handle_base if c.isalnum())
if random.random() < 0.5:
handle_base += str(random.randint(1, 999))
person_data["social_media"] = f"@{handle_base}"
if "bank_account" in self.fields:
last_digits = random.randint(1000, 9999)
person_data["bank_account"] = f"****-****-****-{last_digits}"
if "credit_card" in self.fields:
card_types = [
("4", "Visa"),
("5", "MasterCard"),
("3", "American Express"),
("6", "Discover")
]
card_type = random.choice(card_types)
last_digits = random.randint(1000, 9999)
person_data["credit_card"] = f"{card_type[0]}***-****-****-{last_digits}"
# Остальные дополнительные поля...
if "passport" in self.fields:
if self.country == "USA":
letters = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=2))
numbers = ''.join(random.choices('0123456789', k=7))
person_data["passport"] = f"{letters}{numbers}"
elif self.country == "UK":
numbers = ''.join(random.choices('0123456789', k=9))
person_data["passport"] = numbers
else:
letters = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=2))
numbers = ''.join(random.choices('0123456789', k=7))
person_data["passport"] = f"{letters}{numbers}"
if "driver_license" in self.fields:
if self.country == "USA":
if state == "California":
letter = random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
numbers = ''.join(random.choices('0123456789', k=7))
person_data["driver_license"] = f"{letter}{numbers}"
else:
numbers = ''.join(random.choices('0123456789', k=9))
person_data["driver_license"] = numbers
else:
letters = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=2))
numbers = ''.join(random.choices('0123456789', k=8))
person_data["driver_license"] = f"{letters}{numbers}"
if "vehicle" in self.fields:
makes = ["Toyota", "Honda", "Ford", "Chevrolet", "Nissan", "BMW", "Mercedes-Benz",
"Volkswagen", "Audi", "Hyundai", "Kia", "Mazda", "Subaru", "Lexus",
"Jeep", "Ram", "GMC", "Cadillac", "Volvo", "Tesla"]
models_by_make = {
"Toyota": ["Camry", "Corolla", "RAV4", "Highlander", "Prius"],
"Honda": ["Civic", "Accord", "CR-V", "Pilot", "Odyssey"],
"Ford": ["F-150", "Explorer", "Escape", "Mustang", "Edge"],
"Chevrolet": ["Silverado", "Equinox", "Malibu", "Tahoe", "Camaro"],
"Tesla": ["Model 3", "Model Y", "Model S", "Model X"],
"BMW": ["3 Series", "5 Series", "X3", "X5", "M3"],
"Mercedes-Benz": ["C-Class", "E-Class", "GLC", "GLE", "S-Class"]
}
make = random.choice(makes)
if make in models_by_make:
model = random.choice(models_by_make[make])
else:
model = "Model " + random.choice(['A', 'B', 'C', 'X', 'S'])
year = random.randint(2015, 2024)
person_data["vehicle"] = f"{year} {make} {model}"
if "education" in self.fields:
universities = [
"Harvard University", "Stanford University", "MIT", "Yale University",
"Princeton University", "Columbia University", "University of Chicago",
"University of Pennsylvania", "Cornell University", "Duke University",
"Northwestern University", "Johns Hopkins University", "UCLA",
"UC Berkeley", "University of Michigan", "NYU", "Boston University",
"University of Texas", "University of Washington", "Georgetown University"
]
degrees = [
"Bachelor of Science in Computer Science",
"Bachelor of Arts in Business Administration",
"Master of Business Administration (MBA)",
"Master of Science in Engineering",
"Bachelor of Arts in Psychology",
"Doctor of Medicine (MD)",
"Juris Doctor (JD)",
"Master of Science in Data Science",
"Bachelor of Science in Biology",
"Master of Arts in Education"
]
person_data["education"] = f"{random.choice(degrees)}, {random.choice(universities)}"
if "hobbies" in self.fields:
all_hobbies = [
"Photography", "Hiking", "Reading", "Cooking", "Gaming", "Traveling",
"Yoga", "Running", "Cycling", "Swimming", "Painting", "Music",
"Gardening", "Writing", "Dancing", "Fishing", "Golf", "Tennis",
"Basketball", "Soccer", "Woodworking", "Knitting", "Chess",
"Board games", "Meditation", "Volunteering", "Wine tasting"
]
num_hobbies = random.randint(2, 4)
selected_hobbies = random.sample(all_hobbies, num_hobbies)
person_data["hobbies"] = ", ".join(selected_hobbies)
if "languages" in self.fields:
all_languages = [
"English", "Spanish", "French", "German", "Italian", "Portuguese",
"Russian", "Chinese", "Japanese", "Korean", "Arabic", "Hindi",
"Dutch", "Swedish", "Polish", "Turkish", "Greek", "Hebrew"
]
languages = ["English"]
num_additional = random.randint(0, 2)
if num_additional > 0:
additional = random.sample([l for l in all_languages if l != "English"], num_additional)
languages.extend(additional)
person_data["languages"] = ", ".join(languages)
# Специфичные для страны поля
if country_info.get('ssn') and "ssn" in self.fields:
area = random.randint(100, 899)
if area == 666:
area = 667
group = random.randint(10, 99)
serial = random.randint(1000, 9999)
person_data["ssn"] = f"{area:03d}-{group:02d}-{serial:04d}"
if country_info.get('sin') and "sin" in self.fields:
numbers = ''.join(random.choices('0123456789', k=9))
person_data["sin"] = f"{numbers[:3]} {numbers[3:6]} {numbers[6:]}"
if country_info.get('ni_number') and "ni_number" in self.fields:
prefix = random.choice(['AB', 'BA', 'BB', 'CA', 'CB', 'CE', 'CH', 'CY'])
numbers = ''.join(random.choices('0123456789', k=6))
suffix = random.choice(['A', 'B', 'C', 'D'])
person_data["ni_number"] = f"{prefix} {numbers[:2]} {numbers[2:4]} {numbers[4:]} {suffix}"
self.data_generated.emit(person_data)
time.sleep(0.1)
self.progress_updated.emit(100, "Генерация завершена!")
def run(self):
"""Основной метод генерации"""
try:
self.progress_updated.emit(10, "Инициализация...")
# Сначала пробуем HF API
if HF_API_TOKENS[0] != "hf_YOUR_TOKEN_HERE":
self.progress_updated.emit(30, "Создание запроса для AI...")
prompt = self.generate_prompt()
self.progress_updated.emit(50, "Отправка запроса к AI...")
result = self.generate_with_hf(prompt)
if result and isinstance(result, list):
self.progress_updated.emit(70, "Обработка ответа AI...")
for i, person in enumerate(result):
self.progress_updated.emit(
70 + int(25 * i / len(result)),
f"Обработка профиля {i + 1}/{len(result)}"
)
if 'first_name' in person and 'last_name' in person:
# Заменяем email на умно сгенерированный
if 'email' not in person or not person['email'] or '@example.com' in person.get('email',
''):
person['email'] = generate_smart_email(
person['first_name'],
person['last_name'],
self.existing_emails
)
self.existing_emails.add(person['email'])
# Проверяем и дополняем телефон
if 'phone' in person and ('555' in person['phone'] or 'XXX' in person['phone']):
area_code = random.randint(201, 999)
prefix = random.randint(200, 999)
while prefix == 555:
prefix = random.randint(200, 999)
line = random.randint(1000, 9999)
person['phone'] = f"+1 ({area_code}) {prefix}-{line}"
self.data_generated.emit(person)
self.progress_updated.emit(100, "Готово!")
return
# Если HF не сработал, используем локальную генерацию
logger.info("Используется локальная генерация")
self.progress_updated.emit(40, "Использование локальной генерации...")
self.generate_local_data()
except Exception as e:
logger.error(f"Ошибка генерации: {e}", exc_info=True)
self.error_occurred.emit(f"Ошибка: {str(e)}")
class CharacterGeneratorApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("🤖 AI Character Generator Pro")
self.setGeometry(100, 100, 1400, 900)
# Применяем темную тему
self.apply_dark_theme()
self.generated_data = []
self.generator_thread = HFDataGenerator()
self.init_ui()
self.setup_connections()
self.update_statistics()
def apply_dark_theme(self):
"""Применение красивой темной темы"""
self.setStyleSheet("""
/* Основные стили */
QMainWindow {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #1a1a2e, stop:1 #16213e);
color: #ffffff;
}
QWidget {
background-color: transparent;
color: #ffffff;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 13px;
}
/* Группы */
QGroupBox {
font-weight: bold;
font-size: 14px;
border: 2px solid #0f3460;
border-radius: 12px;
margin-top: 15px;
padding-top: 10px;
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(15, 52, 96, 0.4), stop:1 rgba(26, 26, 46, 0.4));
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top center;
padding: 0 10px;
color: #00d4ff;
font-size: 15px;
font-weight: bold;
}
/* Кнопки */
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00d4ff, stop:1 #0f3460);
border: none;
border-radius: 8px;
color: white;
font-weight: bold;
font-size: 13px;
padding: 12px 22px;
min-height: 25px;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00e6ff, stop:1 #1a4480);
transform: translateY(-1px);
}
QPushButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #0099cc, stop:1 #0a2040);
}
QPushButton:disabled {
background: #2a2a3a;
color: #666666;
}
/* Специальные кнопки */
QPushButton#generateBtn {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00ff88, stop:1 #00cc66);
font-size: 14px;
font-weight: bold;
padding: 15px 30px;
}
QPushButton#generateBtn:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00ffaa, stop:1 #00dd77);
}
QPushButton#clearBtn {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #ff4757, stop:1 #cc2e3f);
}
QPushButton#clearBtn:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #ff6b7a, stop:1 #dd3850);
}
/* Поля ввода */
QLineEdit, QSpinBox, QComboBox {
background: rgba(255, 255, 255, 0.1);
border: 2px solid #0f3460;
border-radius: 8px;
padding: 10px 15px;
color: #ffffff;
font-size: 13px;
min-height: 25px;
}
QLineEdit:focus, QSpinBox:focus, QComboBox:focus {
border-color: #00d4ff;
background: rgba(0, 212, 255, 0.1);
}
QComboBox::drop-down {
border: none;
background: transparent;
width: 20px;
}
QComboBox::down-arrow {
image: none;
border-style: solid;
border-width: 4px 4px 0px 4px;
border-color: #00d4ff transparent transparent transparent;
}
QComboBox QAbstractItemView {
background: #1a1a2e;
border: 2px solid #0f3460;
border-radius: 8px;
color: #ffffff;
selection-background-color: #00d4ff;
padding: 4px;
}
/* Чекбоксы */
QCheckBox {
color: #ffffff;
font-size: 13px;
spacing: 10px;
padding: 3px;
}
QCheckBox::indicator {
width: 20px;
height: 20px;
border: 2px solid #0f3460;
border-radius: 4px;
background: rgba(255, 255, 255, 0.1);
}
QCheckBox::indicator:checked {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #00d4ff, stop:1 #0f3460);
border-color: #00d4ff;
}
QCheckBox::indicator:checked:hover {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #00e6ff, stop:1 #1a4480);
}
/* Таблица */
QTableWidget {
background: rgba(255, 255, 255, 0.05);
border: 2px solid #0f3460;
border-radius: 12px;
gridline-color: #0f3460;
color: #ffffff;
font-size: 12px;
alternate-background-color: rgba(15, 52, 96, 0.3);
}
QTableWidget::item {
background: transparent;
border-bottom: 1px solid #0f3460;
padding: 12px;
color: #ffffff;
}
QTableWidget::item:alternate {
background: rgba(15, 52, 96, 0.2);
color: #ffffff;
}
QTableWidget::item:selected {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 rgba(0, 212, 255, 0.4), stop:1 rgba(15, 52, 96, 0.4));
color: #ffffff;
}
QHeaderView::section {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #0f3460, stop:1 #1a1a2e);
color: #00d4ff;
padding: 12px;
border: none;
border-right: 1px solid #2a2a3a;
font-weight: bold;
font-size: 12px;
}
/* Вкладки */
QTabWidget::pane {
border: 2px solid #0f3460;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
}
QTabBar::tab {
background: rgba(15, 52, 96, 0.6);
color: #ffffff;
padding: 15px 25px;
margin-right: 2px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
font-weight: bold;
font-size: 13px;
}
QTabBar::tab:selected {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00d4ff, stop:1 #0f3460);
color: #ffffff;
}
QTabBar::tab:hover:!selected {
background: rgba(0, 212, 255, 0.3);
}
/* Текстовые области */
QTextEdit {
background: rgba(255, 255, 255, 0.05);
border: 2px solid #0f3460;
border-radius: 12px;
color: #ffffff;
font-family: 'Consolas', 'Courier New', monospace;
font-size: 12px;
padding: 12px;
}
/* Прогресс-бар */
QProgressBar {
border: 2px solid #0f3460;
border-radius: 8px;
background: rgba(255, 255, 255, 0.1);
text-align: center;
color: #ffffff;
font-weight: bold;
font-size: 13px;
min-height: 25px;
}
QProgressBar::chunk {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 #00ff88, stop:0.5 #00d4ff, stop:1 #0f3460);
border-radius: 6px;
}
/* Лейблы */
QLabel {
color: #ffffff;
font-size: 13px;
}
/* Статус */
QStatusBar {
background: rgba(15, 52, 96, 0.8);
color: #00d4ff;
border-top: 1px solid #0f3460;
}
/* Скроллбары */
QScrollBar:vertical {
background: rgba(255, 255, 255, 0.1);
width: 12px;
border-radius: 6px;
}
QScrollBar::handle:vertical {
background: #00d4ff;
border-radius: 6px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background: #00e6ff;
}
QScrollBar:horizontal {
background: rgba(255, 255, 255, 0.1);
height: 12px;
border-radius: 6px;
}
QScrollBar::handle:horizontal {
background: #00d4ff;
border-radius: 6px;
min-width: 20px;
}
QScrollBar::handle:horizontal:hover {
background: #00e6ff;
}
""")
def init_ui(self):
"""Создание красивого интерфейса"""
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(15)
main_layout.setContentsMargins(20, 20, 20, 20)
# Заголовок приложения
title_label = QLabel("🤖 AI Character Generator Pro")
title_label.setStyleSheet("""
QLabel {
font-size: 28px;
font-weight: bold;
color: #00d4ff;
text-align: center;
padding: 20px;
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 rgba(0, 212, 255, 0.1), stop:1 rgba(15, 52, 96, 0.1));
border-radius: 15px;
border: 2px solid #0f3460;
}
""")
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_layout.addWidget(title_label)
# Контейнер для основных настроек
settings_container = QWidget()
settings_layout = QHBoxLayout(settings_container)
settings_layout.setSpacing(20)
# Левая панель - основные настройки
main_settings_group = QGroupBox("⚙️ Основные настройки")
main_settings_layout = QGridLayout()
# Выбор страны
main_settings_layout.addWidget(QLabel("🌍 Страна:"), 0, 0)
self.country_combo = QComboBox()
self.country_combo.addItems(list(COUNTRIES.keys()))
main_settings_layout.addWidget(self.country_combo, 0, 1)
# Количество
main_settings_layout.addWidget(QLabel("🔢 Количество:"), 1, 0)
self.count_spin = QSpinBox()
self.count_spin.setRange(1, 50)
self.count_spin.setValue(5)
main_settings_layout.addWidget(self.count_spin, 1, 1)
# Кнопки управления
buttons_layout = QHBoxLayout()
self.generate_btn = QPushButton("🚀 Генерировать")
self.generate_btn.setObjectName("generateBtn")
self.clear_btn = QPushButton("🗑️ Очистить")
self.clear_btn.setObjectName("clearBtn")
buttons_layout.addWidget(self.generate_btn)
buttons_layout.addWidget(self.clear_btn)
main_settings_layout.addLayout(buttons_layout, 2, 0, 1, 2)
main_settings_group.setLayout(main_settings_layout)
settings_layout.addWidget(main_settings_group, 1)
# Правая панель - дополнительные поля
fields_group = QGroupBox("📋 Дополнительные поля")
fields_scroll = QScrollArea()
fields_widget = QWidget()
fields_layout = QVBoxLayout(fields_widget)
# Кнопка "Выбрать все"
select_all_layout = QHBoxLayout()
self.select_all_btn = QPushButton("✅ Выбрать все")
self.deselect_all_btn = QPushButton("❌ Снять все")
select_all_layout.addWidget(self.select_all_btn)
select_all_layout.addWidget(self.deselect_all_btn)
fields_layout.addLayout(select_all_layout)
# Чекбоксы для полей
self.field_checkboxes = {}
field_icons = {
"occupation": "💼", "company": "🏢", "website": "🌐", "social_media": "📱",
"bank_account": "🏦", "credit_card": "💳", "passport": "📔", "driver_license": "🚗",
"vehicle": "🚙", "education": "🎓", "hobbies": "🎯", "languages": "🗣️"
}
for field in ADDITIONAL_FIELDS:
icon = field_icons.get(field, "📄")
checkbox = QCheckBox(f"{icon} {field.replace('_', ' ').title()}")
self.field_checkboxes[field] = checkbox
fields_layout.addWidget(checkbox)
fields_widget.setLayout(fields_layout)
fields_scroll.setWidget(fields_widget)
fields_scroll.setWidgetResizable(True)
fields_scroll.setMaximumHeight(300)
fields_group_layout = QVBoxLayout()
fields_group_layout.addWidget(fields_scroll)
fields_group.setLayout(fields_group_layout)
settings_layout.addWidget(fields_group, 1)
main_layout.addWidget(settings_container)
# Прогресс-бар
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
main_layout.addWidget(self.progress_bar)
# Вкладки для отображения данных
self.tabs = QTabWidget()
self.tabs.setStyleSheet("""
QTabWidget::pane {
border: 2px solid #0f3460;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
}
""")
# Таблица
self.table_widget = QTableWidget()
self.setup_table()
# Карточки
self.cards_area = QTextEdit()
self.cards_area.setReadOnly(True)
# JSON
self.json_text = QTextEdit()
self.json_text.setReadOnly(True)
self.tabs.addTab(self.table_widget, "📊 Таблица")
self.tabs.addTab(self.cards_area, "🎴 Карточки")
self.tabs.addTab(self.json_text, "📄 JSON")
main_layout.addWidget(self.tabs)
# Нижняя панель
bottom_container = QWidget()
bottom_layout = QVBoxLayout(bottom_container)
# Статистика
self.stats_label = QLabel("📈 Сгенерировано персонажей: 0")
self.stats_label.setStyleSheet("""
QLabel {
font-size: 16px;
font-weight: bold;
color: #00d4ff;
padding: 12px;
background: rgba(0, 212, 255, 0.1);
border-radius: 8px;
border: 1px solid #0f3460;
}
""")
bottom_layout.addWidget(self.stats_label)
# API ключ и экспорт
controls_layout = QHBoxLayout()
# API секция
api_layout = QHBoxLayout()
api_layout.addWidget(QLabel("🔑 HF API Key:"))
self.api_key_input = QLineEdit("hf_YOUR_TOKEN_HERE")
self.api_key_input.setEchoMode(QLineEdit.EchoMode.Password)
api_layout.addWidget(self.api_key_input)
self.add_key_btn = QPushButton("➕ Добавить ключ")
api_layout.addWidget(self.add_key_btn)
controls_layout.addLayout(api_layout)
# Экспорт секция
export_layout = QHBoxLayout()
self.export_csv_btn = QPushButton("📄 CSV")
self.export_json_btn = QPushButton("📋 JSON")
self.export_txt_btn = QPushButton("📝 TXT")
self.copy_all_btn = QPushButton("📋 Копировать")
export_layout.addWidget(QLabel("💾 Экспорт:"))
export_layout.addWidget(self.export_csv_btn)
export_layout.addWidget(self.export_json_btn)
export_layout.addWidget(self.export_txt_btn)
export_layout.addWidget(self.copy_all_btn)
controls_layout.addLayout(export_layout)
bottom_layout.addLayout(controls_layout)
main_layout.addWidget(bottom_container)
# Статусбар
self.statusBar().showMessage("🎉 Готов к генерации персонажей!")
def setup_table(self):
"""Настройка таблицы"""
self.table_widget.setColumnCount(11)
headers = ["👤 Имя", "👥 Фамилия", "🏠 Адрес", "🏙️ Город", "🌏 Регион",
"📮 Индекс", "🎂 День рождения", "📞 Телефон", "📧 Email", "⚧ Пол", "🔍 Детали"]
self.table_widget.setHorizontalHeaderLabels(headers)
# Делаем колонки растягиваемыми мышкой
header = self.table_widget.horizontalHeader()
header.setSectionResizeMode(QHeaderView.ResizeMode.Interactive)
header.setStretchLastSection(True)
# Устанавливаем высоту строк
self.table_widget.verticalHeader().setDefaultSectionSize(50)
self.table_widget.verticalHeader().setMinimumSectionSize(45)
# Устанавливаем минимальные ширины для колонок
self.table_widget.setColumnWidth(0, 100) # Имя
self.table_widget.setColumnWidth(1, 100) # Фамилия
self.table_widget.setColumnWidth(2, 200) # Адрес
self.table_widget.setColumnWidth(3, 120) # Город
self.table_widget.setColumnWidth(4, 100) # Регион
self.table_widget.setColumnWidth(5, 80) # Индекс
self.table_widget.setColumnWidth(6, 120) # День рождения
self.table_widget.setColumnWidth(7, 150) # Телефон
self.table_widget.setColumnWidth(8, 200) # Email
self.table_widget.setColumnWidth(9, 80) # Пол
self.table_widget.setColumnWidth(10, 70) # Детали
self.table_widget.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self.table_widget.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
self.table_widget.setAlternatingRowColors(True)
def setup_connections(self):
"""Настройка сигналов и слотов"""
# Основные кнопки
self.generate_btn.clicked.connect(self.generate_data)
self.clear_btn.clicked.connect(self.clear_data)
self.add_key_btn.clicked.connect(self.add_api_key)
# Кнопки выбора полей
self.select_all_btn.clicked.connect(lambda: self.toggle_all_fields(True))
self.deselect_all_btn.clicked.connect(lambda: self.toggle_all_fields(False))
# Экспорт
self.export_csv_btn.clicked.connect(self.export_to_csv)
self.export_json_btn.clicked.connect(self.export_to_json)
self.export_txt_btn.clicked.connect(self.export_to_txt)
self.copy_all_btn.clicked.connect(self.copy_all_data)
# Генератор
self.generator_thread.data_generated.connect(self.add_generated_data)
self.generator_thread.error_occurred.connect(self.show_error)
self.generator_thread.progress_updated.connect(self.update_progress)
self.generator_thread.finished.connect(self.on_generation_complete)
# Таблица
self.table_widget.cellClicked.connect(self.show_details)
# Вкладки
self.tabs.currentChanged.connect(self.on_tab_changed)
def toggle_all_fields(self, checked: bool):
"""Переключение всех чекбоксов"""
for checkbox in self.field_checkboxes.values():
checkbox.setChecked(checked)
def generate_data(self):
"""Запуск генерации данных"""
selected_fields = [
field for field, checkbox in self.field_checkboxes.items()
if checkbox.isChecked()
]
country = self.country_combo.currentText()
country_info = COUNTRIES[country]
if country_info.get('ssn'):
selected_fields.append('ssn')
elif country_info.get('ni_number'):
selected_fields.append('ni_number')
elif country_info.get('sin'):
selected_fields.append('sin')
self.generator_thread.setup_request(
country,
self.count_spin.value(),
selected_fields
)
self.generate_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setValue(0)
self.statusBar().showMessage("🤖 ИИ генерирует персонажей...")
self.generator_thread.start()
def add_generated_data(self, data: dict):
"""Добавление сгенерированных данных"""
self.generated_data.append(data)
# Добавление в таблицу
row = self.table_widget.rowCount()
self.table_widget.insertRow(row)
# Заполнение ячеек с принудительным белым цветом
items = [
QTableWidgetItem(data.get('first_name', '')),
QTableWidgetItem(data.get('last_name', '')),
QTableWidgetItem(data.get('address', '')),
QTableWidgetItem(data.get('city', '')),
QTableWidgetItem(data.get('state', data.get('region', ''))),
QTableWidgetItem(data.get('zip', data.get('postcode', ''))),
QTableWidgetItem(data.get('birthday', '')),
QTableWidgetItem(data.get('phone', '')),
QTableWidgetItem(data.get('email', '')),
QTableWidgetItem(data.get('gender', ''))
]
for col, item in enumerate(items):
# Принудительно устанавливаем белый цвет текста
item.setForeground(QColor(255, 255, 255))
self.table_widget.setItem(row, col, item)
# Кнопка детальной информации
detail_btn = QPushButton("👁️")
detail_btn.setStyleSheet("""
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00d4ff, stop:1 #0f3460);
color: white;
border: none;
padding: 8px;
border-radius: 6px;
font-size: 16px;
font-weight: bold;
min-width: 15px;
max-width: 15px;
min-height: 15px;
max-height: 15px;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00e6ff, stop:1 #1a4480);
transform: scale(1.05);
}
QPushButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #0099cc, stop:1 #0a2040);
}
""")
detail_btn.clicked.connect(lambda: self.show_details(row, 0))
self.table_widget.setCellWidget(row, 10, detail_btn)
# Обновление других представлений
self.update_json_view()
self.update_cards_view()
self.update_statistics()
def show_details(self, row: int, column: int):
"""Показ детальной информации"""
if row < len(self.generated_data):
data = self.generated_data[row]
detail_dialog = QMessageBox(self)
detail_dialog.setWindowTitle(f"👤 {data.get('first_name', '')} {data.get('last_name', '')}")
detail_dialog.setTextFormat(Qt.TextFormat.RichText)
# Устанавливаем темную тему для диалога
detail_dialog.setStyleSheet("""
QMessageBox {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #1a1a2e, stop:1 #16213e);
color: #ffffff;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 13px;
}
QMessageBox QLabel {
color: #ffffff;
font-size: 14px;
}
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00d4ff, stop:1 #0f3460);
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
font-weight: bold;
font-size: 13px;
min-width: 80px;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #00e6ff, stop:1 #1a4480);
}
""")
detail_text = f"""
<div style='font-family: Segoe UI, Arial, sans-serif; color: #ffffff; background: transparent;'>
<h2 style='color: #00d4ff; text-align: center; margin-bottom: 20px; font-size: 20px;'>
🎭 Character Profile
</h2>
<table style='width: 100%; border-collapse: collapse; background: transparent;'>
"""
field_icons = {
'first_name': '👤', 'last_name': '👥', 'address': '🏠', 'city': '🏙️',
'state': '🌏', 'zip': '📮', 'birthday': '🎂', 'phone': '📞',
'email': '📧', 'gender': '⚧', 'occupation': '💼', 'company': '🏢',
'website': '🌐', 'social_media': '📱', 'bank_account': '🏦',
'credit_card': '💳', 'passport': '📔', 'driver_license': '🚗',
'vehicle': '🚙', 'education': '🎓', 'hobbies': '🎯', 'languages': '🗣️',
'ssn': '🔢', 'sin': '🔢', 'ni_number': '🔢'
}
for key, value in data.items():
icon = field_icons.get(key, '📄')
key_display = key.replace('_', ' ').title()
detail_text += f"""
<tr style='border-bottom: 1px solid rgba(255,255,255,0.1);'>
<td style='padding: 12px; font-weight: bold; color: #00d4ff; width: 35%; font-size: 14px;'>
{icon} {key_display}:
</td>
<td style='padding: 12px; color: #ffffff; font-size: 14px; font-weight: normal;'>{value}</td>
</tr>
"""
detail_text += "</table></div>"
detail_dialog.setText(detail_text)
# Увеличиваем размер диалога
detail_dialog.resize(500, 600)
detail_dialog.exec()
detail_text += "</table></div>"
detail_dialog.setText(detail_text)
detail_dialog.exec()
def update_json_view(self):
"""Обновление JSON представления"""
json_text = json.dumps(self.generated_data, indent=2, ensure_ascii=False)
self.json_text.setText(json_text)
def update_cards_view(self):
"""Обновление карточного представления"""
cards_html = """
<style>
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e, #16213e);
margin: 0;
padding: 20px;
}
.card {
border: 2px solid #0f3460;
border-radius: 15px;
padding: 20px;
margin: 15px 0;
background: linear-gradient(145deg, rgba(0,212,255,0.1), rgba(15,52,96,0.2));
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
backdrop-filter: blur(10px);
color: #ffffff;
}
.card-header {
color: #00d4ff;
font-size: 20px;
font-weight: bold;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px solid #00d4ff;
padding-bottom: 10px;
}
.card-row {
display: flex;
justify-content: space-between;
margin: 8px 0;
padding: 5px 0;
}
.card-label {
font-weight: bold;
color: #00d4ff;
min-width: 120px;
}
.card-value {
color: #ffffff;
flex: 1;
text-align: right;
}
.additional-fields {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #0f3460;
}
</style>
"""
for i, person in enumerate(self.generated_data):
cards_html += f"""
<div class="card">
<div class="card-header">
🎭 {person.get('first_name', '')} {person.get('last_name', '')}
</div>
<div class="card-row">
<span class="card-label">🏠 Address:</span>
<span class="card-value">{person.get('address', '')}</span>
</div>
<div class="card-row">
<span class="card-label">🏙️ City:</span>
<span class="card-value">{person.get('city', '')}, {person.get('state', '')} {person.get('zip', '')}</span>
</div>
<div class="card-row">
<span class="card-label">🎂 Birthday:</span>
<span class="card-value">{person.get('birthday', '')}</span>
</div>
<div class="card-row">
<span class="card-label">⚧ Gender:</span>
<span class="card-value">{person.get('gender', '')}</span>
</div>
<div class="card-row">
<span class="card-label">📞 Phone:</span>
<span class="card-value">{person.get('phone', '')}</span>
</div>
<div class="card-row">
<span class="card-label">📧 Email:</span>
<span class="card-value">{person.get('email', '')}</span>
</div>
"""
# Дополнительные поля
additional_fields = {k: v for k, v in person.items()
if k not in ['first_name', 'last_name', 'address', 'city', 'state', 'zip', 'birthday',
'phone', 'email', 'gender']}
if additional_fields:
cards_html += '<div class="additional-fields">'
field_icons = {
'occupation': '💼', 'company': '🏢', 'website': '🌐', 'social_media': '📱',
'bank_account': '🏦', 'credit_card': '💳', 'passport': '📔', 'driver_license': '🚗',
'vehicle': '🚙', 'education': '🎓', 'hobbies': '🎯', 'languages': '🗣️'
}
for key, value in additional_fields.items():
icon = field_icons.get(key, '📄')
label = key.replace('_', ' ').title()
cards_html += f"""
<div class="card-row">
<span class="card-label">{icon} {label}:</span>
<span class="card-value">{value}</span>
</div>
"""
cards_html += '</div>'
cards_html += "</div>"
self.cards_area.setHtml(cards_html)
def update_statistics(self):
"""Обновление статистики"""
self.stats_label.setText(f"📈 Сгенерировано персонажей: {len(self.generated_data)}")
def on_tab_changed(self, index: int):
"""Обработка смены вкладки"""
if index == 1: # Cards view
self.update_cards_view()
elif index == 2: # JSON view
self.update_json_view()
def clear_data(self):
"""Очистка всех данных"""
reply = QMessageBox.question(
self,
"🗑️ Подтверждение очистки",
"Вы уверены, что хотите очистить все сгенерированные данные?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
self.generated_data.clear()
self.table_widget.setRowCount(0)
self.json_text.clear()
self.cards_area.clear()
self.update_statistics()
self.statusBar().showMessage("🧹 Все данные очищены")
def add_api_key(self):
"""Добавление API ключа"""
key = self.api_key_input.text().strip()
if key and key not in HF_API_TOKENS and key != "hf_YOUR_TOKEN_HERE":
HF_API_TOKENS[0] = key
self.api_key_input.clear()
QMessageBox.information(
self,
"✅ Успех",
"Hugging Face API токен успешно обновлен!"
)
self.statusBar().showMessage("🔑 API ключ обновлен")
def copy_all_data(self):
"""Копирование всех данных в буфер обмена"""
if not self.generated_data:
QMessageBox.warning(self, "⚠️ Предупреждение", "Нет данных для копирования")
return
json_text = json.dumps(self.generated_data, indent=2, ensure_ascii=False)
QApplication.clipboard().setText(json_text)
self.statusBar().showMessage("📋 Данные скопированы в буфер обмена", 3000)
def export_to_csv(self):
"""Экспорт в CSV"""
if not self.generated_data:
QMessageBox.warning(self, "⚠️ Предупреждение", "Нет данных для экспорта")
return
file_path, _ = QFileDialog.getSaveFileName(
self,
"💾 Сохранить CSV файл",
"character_profiles.csv",
"CSV Files (*.csv)"
)
if file_path:
try:
with open(file_path, 'w', newline='', encoding='utf-8') as file:
if self.generated_data:
writer = csv.DictWriter(file, fieldnames=self.generated_data[0].keys())
writer.writeheader()
writer.writerows(self.generated_data)
QMessageBox.information(self, "✅ Успех", "Данные успешно экспортированы в CSV!")
self.statusBar().showMessage("📄 CSV файл сохранен", 3000)
except Exception as e:
QMessageBox.critical(self, "❌ Ошибка", f"Ошибка экспорта: {e}")
def export_to_json(self):
"""Экспорт в JSON"""
if not self.generated_data:
QMessageBox.warning(self, "⚠️ Предупреждение", "Нет данных для экспорта")
return
file_path, _ = QFileDialog.getSaveFileName(
self,
"💾 Сохранить JSON файл",
"character_profiles.json",
"JSON Files (*.json)"
)
if file_path:
try:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(self.generated_data, file, indent=2, ensure_ascii=False)
QMessageBox.information(self, "✅ Успех", "Данные успешно экспортированы в JSON!")
self.statusBar().showMessage("📋 JSON файл сохранен", 3000)
except Exception as e:
QMessageBox.critical(self, "❌ Ошибка", f"Ошибка экспорта: {e}")
def export_to_txt(self):
"""Экспорт в TXT"""
if not self.generated_data:
QMessageBox.warning(self, "⚠️ Предупреждение", "Нет данных для экспорта")
return
file_path, _ = QFileDialog.getSaveFileName(
self,
"💾 Сохранить текстовый файл",
"character_profiles.txt",
"Text Files (*.txt)"
)
if file_path:
try:
with open(file_path, 'w', encoding='utf-8') as file:
file.write("🎭 CHARACTER PROFILES FOR NOVEL\n")
file.write("=" * 70 + "\n\n")
for i, person in enumerate(self.generated_data, 1):
file.write(f"CHARACTER #{i}\n")
file.write("-" * 50 + "\n")
for key, value in person.items():
key_display = key.replace('_', ' ').title()
file.write(f"{key_display}: {value}\n")
file.write("\n")
QMessageBox.information(self, "✅ Успех", "Данные успешно экспортированы в TXT!")
self.statusBar().showMessage("📝 TXT файл сохранен", 3000)
except Exception as e:
QMessageBox.critical(self, "❌ Ошибка", f"Ошибка экспорта: {e}")
def show_error(self, message: str):
"""Отображение ошибок"""
QMessageBox.critical(self, "❌ Ошибка генерации", f"Произошла ошибка: {message}")
self.progress_bar.setVisible(False)
self.generate_btn.setEnabled(True)
self.statusBar().showMessage("❌ Ошибка генерации")
def update_progress(self, value: int, message: str):
"""Обновление прогресс-бара"""
self.progress_bar.setValue(value)
self.statusBar().showMessage(f"🤖 {message}")
if value >= 100:
self.progress_bar.setVisible(False)
def on_generation_complete(self):
"""Завершение генерации"""
self.generate_btn.setEnabled(True)
self.progress_bar.setVisible(False)
self.statusBar().showMessage("🎉 Генерация завершена!")
if __name__ == "__main__":
app = QApplication(sys.argv)
# Установка иконки приложения (если есть)
app.setApplicationName("AI Character Generator Pro")
app.setOrganizationName("AI Tools")
window = CharacterGeneratorApp()
window.show()
sys.exit(app.exec())
Последнее редактирование:
в каких методах применяется генерация личности? Генератор только по США как понимаю. В целом очень интересно