File size: 45,431 Bytes
4973465
f154467
641416a
c250f7e
641416a
 
 
 
3885885
542a444
ba58727
4973465
ba58727
 
edcf6fe
 
641416a
119e341
c250f7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235357d
da5f556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7aee395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6839970
19d03d6
 
9f22308
 
7aee395
 
 
6839970
7aee395
 
 
 
edcf6fe
7aee395
 
 
 
6839970
7aee395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9f22308
 
 
 
19d03d6
9f22308
7aee395
edcf6fe
6839970
c2fba02
7aee395
 
 
 
 
 
 
 
029a020
edcf6fe
 
 
 
 
 
 
 
 
 
9f22308
edcf6fe
 
9f22308
08918b8
edcf6fe
029a020
7aee395
08918b8
9f22308
dc5500f
925728c
9f22308
 
 
 
 
 
 
 
ac0dd59
9f22308
 
 
 
 
 
 
ac0dd59
9f22308
f69bcd9
ac0dd59
 
9f22308
dc5500f
9f4be58
08918b8
ac0dd59
08918b8
9f22308
 
 
 
 
 
 
08918b8
 
 
cc02279
029a020
33a02ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d369a94
 
 
 
 
 
 
33a02ce
d369a94
 
33a02ce
d369a94
 
 
 
 
 
 
33a02ce
d369a94
 
 
db923e7
d369a94
 
 
 
db923e7
 
d369a94
 
 
db923e7
d369a94
 
 
 
db923e7
 
d369a94
 
 
db923e7
d369a94
 
 
 
db923e7
 
 
d369a94
 
 
 
 
 
db923e7
d369a94
db923e7
d369a94
 
 
 
33a02ce
 
edcf6fe
6839970
75730ec
dfaf6c1
 
11d46da
5c41f19
593d3df
 
 
 
6839970
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593d3df
6839970
 
 
 
 
 
2ca7cf0
119e341
593d3df
 
 
 
6839970
 
 
 
593d3df
6839970
119e341
593d3df
 
 
edcf6fe
 
593d3df
 
6839970
 
593d3df
 
6839970
 
593d3df
 
6839970
 
 
7c8c711
 
 
 
 
af57cba
 
 
7aee395
af57cba
 
 
7aee395
af57cba
 
 
 
 
 
 
593d3df
2ca7cf0
 
 
 
 
 
 
 
dc5500f
75730ec
6839970
 
 
 
2ca7cf0
 
 
dc5500f
6839970
 
 
 
 
2ca7cf0
dc5500f
 
6839970
 
 
 
2ca7cf0
33a02ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6839970
ae090c3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
import re
import gradio as gr
import os
import pandas as pd
import time
from langchain.schema import SystemMessage
from langchain_community.chat_models.gigachat import GigaChat
from openpyxl import load_workbook
import plotly.graph_objects as go
import random
import pymorphy2
import string

morph = pymorphy2.MorphAnalyzer()

# Авторизация в GigaChat Pro
gc_key = os.getenv('GC_KEY')
chat_pro = GigaChat(credentials=gc_key, model='GigaChat-Pro', max_tokens=68, temperature=1, verify_ssl_certs=False)

# Загрузка данных из Excel-файла
try:
    data = pd.read_excel('Признаки.xlsx', sheet_name=None)
except Exception as e:
    print(f"Ошибка при загрузке Excel-файла: {e}")
    data = {}

# Создание списка признаков и их значений
features = {}
for sheet_name, df in data.items():
    try:
        if sheet_name == "Пол Поколение Психотип":
            features[sheet_name] = df.set_index(['Пол', 'Поколение', 'Психотип'])['Инструкция'].to_dict()
        else:
            features[sheet_name] = df.set_index(df.columns[0]).to_dict()[df.columns[1]]
    except Exception as e:
        print(f"Ошибка при обработке данных листа {sheet_name}: {e}")
        features[sheet_name] = {}

# Функция для создания спидометра
def create_gauge(value):
    fig = go.Figure(go.Indicator(
        mode="gauge+number",
        value=value,
        gauge={
            'axis': {'range': [0, 100]},
            'bar': {'color': "black"},  # Цвет стрелки
            'steps': [
                {'range': [0, 40], 'color': "#55efc4"},  # Мягкий зеленый
                {'range': [40, 70], 'color': "#ffeaa7"},  # Желтый
                {'range': [70, 100], 'color': "#ff7675"}  # Мягкий красный
            ],
            'threshold': {
                'line': {'color': "black", 'width': 4},
                'thickness': 0.75,
                'value': value
            }
        },
        number={'font': {'size': 48}}  # Размер шрифта числа
    ))
    fig.update_layout(paper_bgcolor="#f8f9fa", font={'color': "#2d3436", 'family': "Arial"}, width=250, height=150)
    return fig

# Функция для генерации случайных значений спидометров
def generate_random_gauges():
    return create_gauge(random.randint(60, 90)), create_gauge(random.randint(60, 90)), create_gauge(random.randint(60, 90))

# Функция для смены вкладки
def change_tab(id):
    return gr.Tabs(selected=id)

# Вспомогательная функция для добавления префиксов и суффиксов
def add_prefix_suffix(prompt, prefix, suffix):
    return f"{prefix}\n{prompt}\n{suffix}"

# Функция для обрезки сообщения до последнего знака препинания
def clean_message(message):
    if not message.endswith(('.', '!', '?')):
        last_period = max(message.rfind('.'), message.rfind('!'), message.rfind('?'))
        if last_period != -1:
            message = message[:last_period + 1]
    return message

# Функция для генерации сообщения с GigaChat Pro
def generate_message_gigachat_pro(prompt):
    try:
        messages = [SystemMessage(content=prompt)]
        res = chat_pro(messages)
        cleaned_message = clean_message(res.content.strip())
        return cleaned_message
    except Exception as e:
        return f"Ошибка при обращении к GigaChat-Pro: {e}"

# Функция для повторной генерации сообщения, пока оно не станет короче 250 знаков
def generate_message_gigachat_pro_with_retry(prompt):
    for _ in range(10):
        message = generate_message_gigachat_pro(prompt)
        if len(message) <= 250:
            return message
    return message

# Функция для создания задания для копирайтера
def generate_standard_prompt(description, advantages, key_message, *selected_values):
    prompt = (
        f"Сгенерируй смс-сообщение для клиента.\n"
        f"Описание предложения: {description}\n"
        f"Преимущества: {advantages}\n"
        "В тексте смс запрещено использование:\n"
        "- Запрещенные слова: № один, номер один, № 1, вкусный, дешёвый, продукт, спам, доступный, банкротство, долги, займ, срочно, сейчас, лучший, главный, номер 1, гарантия, успех, лидер;\n"
        "- Обращение к клиенту;\n"
        "- Приветствие клиента;\n"
        "- Обещания и гарантии;\n"
        "- Использовать составные конструкции из двух глаголов;\n"
        "- Причастия и причастные обороты;\n"
        "- Деепричастия и деепричастные обороты;\n"
        "- Превосходная степень прилагательных;\n"
        "- Страдательный залог;\n"
        "- Порядковые числительные от 10 прописью;\n"
        "- Цепочки с придаточными предложениями;\n"
        "- Разделительные повторяющиеся союзы;\n"
        "- Вводные конструкции;\n"
        "- Усилители;\n"
        "- Паразиты времени;\n"
        "- Несколько существительных подряд, в том числе отглагольных;\n"
        "- Производные предлоги;\n"
        "- Сложные предложения, в которых нет связи между частями;\n"
        "- Сложноподчинённые предложения;\n"
        "- Даты прописью;\n"
        "- Близкие по смыслу однородные члены предложения;\n"
        "- Шокирующие, экстравагантные, кликбейтные фразы;\n"
        "- Абстрактные заявления без поддержки фактами и отсутствие доказательства пользы для клиента;\n"
        "- Гарантирующие фразы;\n"
        "- Узкоспециализированные термины;\n"
        "- Фразы, способные создать двойственное ощущение, обидеть;\n"
        "- Речевые клише, рекламные штампы, канцеляризмы;\n"
        "Убедись, что в готовом тексте до 250 знаков с пробелами.\n"
    )        
    if key_message.strip():
        prompt += f"Убедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"
     
    return prompt


# Функция для создания задания для редактора с добавлением prefix и suffix
def generate_personalization_prompt(key_message, *selected_values, prefix, suffix):
    prompt = "Адаптируй, не превышая длину сообщения в 250 знаков с пробелами, текст с учетом следующих особенностей:\n"
    gender, generation, psychotype = selected_values[0], selected_values[1], selected_values[2]
    combined_instruction = ""
    additional_instructions = ""

    # Проверяем, выбраны ли все три параметра: Пол, Поколение, Психотип
    if gender and generation and psychotype:
        # Получаем данные с листа "Пол Поколение Психотип"
        sheet = features.get("Пол Поколение Психотип", {})

        # Ищем ключ, соответствующий комбинации "Пол", "Поколение", "Психотип"
        key = (gender, generation, psychotype)
        if key in sheet:
            combined_instruction = sheet[key]

    # Если не найдена комбинированная инструкция, добавляем индивидуальные инструкции
    if not combined_instruction:
        for i, feature in enumerate(["Пол", "Поколение", "Психотип"]):
            if selected_values[i]:
                try:
                    instruction = features[feature][selected_values[i]]
                    additional_instructions += f"{instruction}\n"
                except KeyError:
                    return f"Ошибка: выбранное значение {selected_values[i]} не найдено в данных."

    # Добавляем инструкции для остальных параметров (например, Отрасль)
    for i, feature in enumerate(features.keys()):
        if feature not in ["Пол", "Поколение", "Психотип", "Пол Поколение Психотип"]:
            if i < len(selected_values) and selected_values[i]:
                try:
                    instruction = features[feature][selected_values[i]]
                    additional_instructions += f"{instruction}\n"
                except KeyError:
                    return f"Ошибка: выбранное значение {selected_values[i]} не найдено в данных."

    # Формируем итоговый промпт
    if combined_instruction:
        prompt += combined_instruction  # Добавляем комбинированную инструкцию, если она есть
    if additional_instructions:
        prompt += additional_instructions  # Добавляем остальные инструкции

    # Добавляем префикс и суффикс для задания редактора
    prompt = f"{prefix}\n{prompt}\n{suffix}"

    # Добавляем ключевое сообщение
    prompt += f"\nУбедись, что в готовом тексте есть следующая ключевая информация: {key_message.strip()}"

    return prompt.strip()


# Функция для постепенной генерации всех сообщений через yield
def generate_all_messages(desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf):
    # Генерация задания для копирайтера
    standard_prompt = generate_standard_prompt(desc, benefits, key_message)
    yield standard_prompt, None, None, None, None, None, None, None

    # Небольшая пауза для демонстрации постепенной генерации
    time.sleep(1)

    # Варианты предложений для начала и конца
    prefixes = [
        "Начни сообщение с призыва к действию с продуктом.",
        "Начни сообщение с указания на пользу продукта. Используй глагол в побудительном наклонении.",
        "Начни сообщение с вопроса, который указывает на пользу продукта для клиента."
    ]
    suffixes = [
        "Убедись, что готовый текст начинается с призыва к действию с продуктом.",
        "Убедись, что готовый текст начинается с указания на пользу продукта и использования глагола в побудительном наклонении.",
        "Убедись, что готовый текст начинается с вопроса, который указывает на пользу продукта для клиента."
    ]

    non_personalized_messages = []
    personalized_messages = []

    # Генерация и постепенная подача каждого сообщения
    for i in range(3):
        # Генерация неперсонализированного сообщения
        prompt = add_prefix_suffix(standard_prompt, prefixes[i], suffixes[i])
        non_personalized_message = generate_message_gigachat_pro_with_retry(prompt)
        non_personalized_length = len(non_personalized_message)
        non_personalized_display = f"{non_personalized_message}\n------\nКоличество знаков: {non_personalized_length}"
        non_personalized_messages.append(non_personalized_display)

        # Генерация задания для редактора
        personalization_prompt = generate_personalization_prompt(
            key_message, gender, generation, psychotype, business_stage, industry, opf,
            prefix=prefixes[i], suffix=suffixes[i]
        )

        # Выводим неперсонализированное сообщение и задание для редактора
        yield (
            standard_prompt, personalization_prompt,
            non_personalized_messages[0] if i >= 0 else None,
            personalized_messages[0] if len(personalized_messages) > 0 else None,
            non_personalized_messages[1] if i >= 1 else None,
            personalized_messages[1] if len(personalized_messages) > 1 else None,
            non_personalized_messages[2] if i >= 2 else None,
            personalized_messages[2] if len(personalized_messages) > 2 else None
        )

        # Генерация персонализированного сообщения
        full_personalized_prompt = f"{personalization_prompt}\n\nТекст для адаптации: {non_personalized_message}"
        personalized_message = generate_message_gigachat_pro_with_retry(full_personalized_prompt)
        personalized_length = len(personalized_message)
        personalized_display = f"{personalized_message}\n------\nКоличество знаков: {personalized_length}"
        personalized_messages.append(personalized_display)

        # Выводим персонализированное сообщение
        yield (
            standard_prompt, personalization_prompt,
            non_personalized_messages[0] if len(non_personalized_messages) > 0 else None,
            personalized_messages[0] if len(personalized_messages) > 0 else None,
            non_personalized_messages[1] if len(non_personalized_messages) > 1 else None,
            personalized_messages[1] if len(personalized_messages) > 1 else None,
            non_personalized_messages[2] if len(non_personalized_messages) > 2 else None,
            personalized_messages[2] if len(personalized_messages) > 2 else None
        )

        time.sleep(1)


# ФУНКЦИИ ПРОВЕРОК (НАЧАЛО)

# 1. Запрещенные слова

def check_forbidden_words(message):   
    morph = pymorphy2.MorphAnalyzer()
    
    # Перечень запрещённых слов и фраз
    forbidden_patterns = [
        r'№\s?1\b', r'номер\sодин\b', r'номер\s1\b',
        r'вкусный', r'дешёвый', r'продукт', 
        r'спам', r'доступный', r'банкротство', r'долг[и]?', r'займ',
        r'срочный', r'сейчас', r'главный',
        r'гарантия', r'успех', r'лидер'
    ]
    
    # Удаляем знаки препинания для корректного анализа
    message_without_punctuation = message.translate(str.maketrans('', '', string.punctuation))
    
    # Проверка на наличие подстроки "лучш" (без учета регистра)
    if re.search(r'лучш', message_without_punctuation, re.IGNORECASE):
        return False
    
    # Лемматизация слов сообщения
    words = message_without_punctuation.split()
    lemmas = [morph.parse(word)[0].normal_form for word in words]
    normalized_message = ' '.join(lemmas)

    # Проверка на запрещённые фразы и леммы
    for pattern in forbidden_patterns:
        if re.search(pattern, normalized_message, re.IGNORECASE):
            return False

    return True


# 2 и #3. Обращение к клиенту и приветствие клиента

def check_no_greeting(message):
    morph = pymorphy2.MorphAnalyzer()
    # Список типичных обращений и приветствий
    greeting_patterns = [
        r"привет\b", r"здравствуй", r"добрый\s(день|вечер|утро)",
        r"дорогой\b", r"уважаемый\b", r"дорогая\b", r"уважаемая\b",
        r"господин\b", r"госпожа\b", r"друг\b", r"коллега\b",
        r"товарищ\b", r"приятель\b", r"друг\b", r"подруга\b"
    ]
    
    # Компилируем все шаблоны в один регулярное выражение
    greeting_regex = re.compile('|'.join(greeting_patterns), re.IGNORECASE)
    
    # Проверяем, начинается ли сообщение с шаблона приветствия или обращения
    if greeting_regex.search(message.strip()):
        return False
    return True

# 4. Обещания и гарантии

def check_no_promises(message):
    morph = pymorphy2.MorphAnalyzer()
    promise_patterns = [
        "обещать", "гарантировать", "обязаться"
    ]
    
    words = message.split()
    lemmas = [morph.parse(word)[0].normal_form for word in words]
    
    for pattern in promise_patterns:
        if pattern in lemmas:
            return False
    return True

# 5. Составные конструкции из двух глаголов

def check_no_double_verbs(message):
    morph = pymorphy2.MorphAnalyzer()
    # Разделяем текст по пробелам и знакам препинания
    words = re.split(r'\s+|[.!?]', message)
    morphs = [morph.parse(word)[0] for word in words]
    
    for i in range(len(morphs) - 1):
        # Проверяем, что оба слова являются глаголами (в любой форме, включая инфинитивы)
        if (morphs[i].tag.POS in {'VERB', 'INFN'}) and (morphs[i+1].tag.POS in {'VERB', 'INFN'}):
            return False
    return True

# 6. Причастия и причастные обороты

def check_no_participles(message):
    morph = pymorphy2.MorphAnalyzer()
    words = message.split()
    morphs = [morph.parse(word)[0] for word in words]
    
    for morph in morphs:
        if 'PRTF' in morph.tag:
            return False
    return True

# 7. Деепричастия и деепричастные обороты

def check_no_adverbial_participles(message):
    morph = pymorphy2.MorphAnalyzer()
    words = message.split()
    morphs = [morph.parse(word)[0] for word in words]
    
    for morph in morphs:
        if 'GRND' in morph.tag:
            return False
    return True

# 8. Превосходная степень прилагательных

def check_no_superlative_adjectives(message):
    morph = pymorphy2.MorphAnalyzer()
    words = message.split()
    morphs = [morph.parse(word)[0] for word in words]
    
    for morph in morphs:
        if 'COMP' in morph.tag or 'Supr' in morph.tag:
            return False
    return True

# 9. Страдательный залог

def check_no_passive_voice(message):
    morph = pymorphy2.MorphAnalyzer()
    words = message.split()
    morphs = [morph.parse(word)[0] for word in words]
    
    for morph in morphs:
        if 'PRTF' in morph.tag and ('passive' in morph.tag or 'в' in morph.tag):
            return False
    return True

# 10. Порядковые числительные от 10 прописью

def check_no_written_out_ordinals(message):
    morph = pymorphy2.MorphAnalyzer()
    ordinal_words = [
        "десятый", "одиннадцатый", "двенадцатый", "тринадцатый", "четырнадцатый", "пятнадцатый",
        "шестнадцатый", "семнадцатый", "восемнадцатый", "девятнадцатый", "двадцатый"
    ]
    
    words = message.split()
    lemmas = [morph.parse(word)[0].normal_form for word in words]
    
    for word in ordinal_words:
        if word in lemmas:
            return False
    return True

# 11. Цепочки с придаточными предложениями

def check_no_subordinate_clauses_chain(message):
    # Регулярное выражение, которое ищет последовательности придаточных предложений
    subordinate_clause_patterns = [
        r'\b(который|которая|которое|которые)\b',
        r'\b(если|потому что|так как|что|когда)\b',
        r'\b(хотя|несмотря на то что)\b'
    ]
    
    count = 0
    for pattern in subordinate_clause_patterns:
        if re.search(pattern, message):
            count += 1
    
    # Если в предложении найдено более одного придаточного предложения подряд, возвращаем False
    return count < 2

# 12. Разделительные повторяющиеся союзы

def check_no_repeating_conjunctions(message):
    # Регулярное выражение для поиска разделительных повторяющихся союзов с запятой перед вторым союзом
    repeating_conjunctions_patterns = r'\b(и|ни|то|не то|или|либо)\b\s*(.*?)\s*,\s*\b\1\b'
    
    # Разделяем сообщение на предложения по точке, вопросительному и восклицательному знакам
    sentences = re.split(r'[.!?]\s*', message)
    
    # Проверяем каждое предложение отдельно
    for sentence in sentences:
        if re.search(repeating_conjunctions_patterns, sentence, re.IGNORECASE):
            return False
    return True

# 13. Вводные конструкции

def check_no_introductory_phrases(message):
    introductory_phrases = [
        r'\b(во-первых|во-вторых|с одной стороны|по сути|по правде говоря)\b',
        r'\b(может быть|кстати|конечно|естественно|безусловно|возможно)\b'
    ]
    
    for pattern in introductory_phrases:
        if re.search(pattern, message, re.IGNORECASE):
            return False
    return True

# 14. Усилители

def check_no_amplifiers(message):
    amplifiers = [
        r'\b(очень|крайне|чрезвычайно|совсем|абсолютно|полностью|чисто)\b'
    ]
    
    for pattern in amplifiers:
        if re.search(pattern, message, re.IGNORECASE):
            return False
    return True

# 15. Паразиты времени

def check_no_time_parasites(message):
    time_parasites = [
        r'\b(сейчас|немедленно|срочно|в данный момент|теперь)\b'
    ]
    
    for pattern in time_parasites:
        if re.search(pattern, message, re.IGNORECASE):
            return False
    return True

# 16. Несколько существительных подряд

def check_no_multiple_nouns(message):
    noun_count = 0
    words = re.split(r'\s+|[.!?]', message)  # Разбиваем по пробелам и знакам препинания
    morph = pymorphy2.MorphAnalyzer()
    
    for word in words:
        parsed_word = morph.parse(word)[0]
        
        # Если слово — существительное
        if 'NOUN' in parsed_word.tag:
            noun_count += 1
        # Если встречен конец предложения (точка, вопросительный знак, восклицательный знак)
        elif re.match(r'[.!?]', word):
            noun_count = 0
        else:
            noun_count = 0
        
        if noun_count > 2:
            return False
    return True

# 17. Производные предлоги

def check_no_derived_prepositions(message):
    derived_prepositions = [
        r'\b(в течение|в ходе|вследствие|в связи с|по мере|при помощи|согласно|вопреки|на основании|на случай|в продолжение|по причине|вблизи|вдалеке|вокруг|внутри|вдоль|посередине|вне|снаружи|благодаря|невзирая на|исходя из)\b'
    ]
    
    for pattern in derived_prepositions:
        if re.search(pattern, message, re.IGNORECASE):
            return False
    return True

# 19. Сложноподчиненные предложения

def check_no_compound_sentences(message):
    subordinating_conjunctions = [
        r'\bкогда\b', r'\bкак только\b', r'\bпока\b', r'\bпосле того как\b',
        r'\bпотому что\b', r'\bтак как\b', r'\bоттого что\b', r'\bблагодаря тому что\b',
        r'\bчтобы\b', r'\bдля того чтобы\b', r'\bесли\b', r'\bкогда бы\b', r'\bесли бы\b',
        r'\bхотя\b', r'\bнесмотря на то что\b', r'\bкак\b', r'\bбудто\b', r'\bсловно\b', r'\bкак будто\b',
        r'\bчто\b'
    ]
    
    # Убедимся, что слово "как" используется не в вопросе
    for pattern in subordinating_conjunctions:
        if re.search(pattern, message) and not re.search(r'\?', message):
            return False
    return True

# 20. Даты прописью

def check_no_dates_written_out(message):
    # Ищем упоминания месяцев или слов, связанных с датами
    months = [
        "января", "февраля", "марта", "апреля", "мая", "июня", 
        "июля", "августа", "сентября", "октября", "ноября", "декабря"
    ]
    
    # Слова для проверки чисел прописью
    date_written_out_patterns = [
        r'\b(первого|второго|третьего|четвертого|пятого|шестого|седьмого|восьмого|девятого|десятого|одиннадцатого|двенадцатого|тринадцатого|четырнадцатого|пятнадцатого|шестнадцатого|семнадцатого|восемнадцатого|девятнадцатого|двадцатого|двадцать первого|двадцать второго|двадцать третьего|двадцать четвертого|двадцать пятого|двадцать шестого|двадцать седьмого|двадцать восьмого|двадцать девятого|тридцатого|тридцать первого)\b'
    ]
    
    for month in months:
        for pattern in date_written_out_patterns:
            if re.search(f'{pattern}\\s{month}', message, re.IGNORECASE):
                return False
    
    return True

# ФУНКЦИИ ПРОВЕРОК (КОНЕЦ)

def perform_checks(message):
    checks = {
        "forbidden_words": check_forbidden_words(message),
        "client_addressing": check_no_greeting(message),
        "promises": check_no_promises(message),
        "double_verbs": check_no_double_verbs(message),
        "participles": check_no_participles(message),
        "adverbial_participles": check_no_adverbial_participles(message),
        "superlative_adjectives": check_no_superlative_adjectives(message),
        "passive_voice": check_no_passive_voice(message),
        "written_out_ordinals": check_no_written_out_ordinals(message),
        "subordinate_clauses_chain": check_no_subordinate_clauses_chain(message),
        "repeating_conjunctions": check_no_repeating_conjunctions(message),
        "introductory_phrases": check_no_introductory_phrases(message),
        "amplifiers": check_no_amplifiers(message),
        "time_parasites": check_no_time_parasites(message),
        "multiple_nouns": check_no_multiple_nouns(message),
        "derived_prepositions": check_no_derived_prepositions(message),
        "compound_sentences": check_no_compound_sentences(message),
        "dates_written_out": check_no_dates_written_out(message)
    }
    return checks


def format_checks(checks):
    translation = {
        "forbidden_words": "Запрещенные слова",
        "client_addressing": "Обращение к клиенту",
        "promises": "Обещания и гарантии",
        "double_verbs": "Два глагола подряд",
        "participles": "Причастия",
        "adverbial_participles": "Деепричастия",
        "superlative_adjectives": "Превосходная степень",
        "passive_voice": "Страдательный залог",
        "written_out_ordinals": "Порядковые числительные",
        "subordinate_clauses_chain": "Цепочки с придаточными предложениями",
        "repeating_conjunctions": "Разделительные повторяющиеся союзы",
        "introductory_phrases": "Вводные конструкции",
        "amplifiers": "Усилители",
        "time_parasites": "Паразиты времени",
        "multiple_nouns": "Несколько существительных подряд",
        "derived_prepositions": "Производные предлоги",
        "compound_sentences": "Сложноподчиненные предложения",
        "dates_written_out": "Даты прописью"
    }
    return "  \n".join([f"{translation[rule]}: {'✔️' if result else '❌'}" for rule, result in checks.items()])


# Функция для обработки нажатия кнопки "Проверить"
def perform_all_checks_and_show_results(personalized_message_1, personalized_message_2, personalized_message_3):
    # Моментально показываем все персонализированные сообщения
    yield (
        personalized_message_1, None,  # Первое сообщение без проверки
        personalized_message_2, None,  # Второе сообщение без проверки
        personalized_message_3, None,  # Третье сообщение без проверки
        None, None, None  # Пустые графики для спидометров
    )
    
    # Выполняем и показываем проверки с задержкой 1 секунда
    checks_1 = perform_checks(personalized_message_1)
    formatted_checks_1 = format_checks(checks_1)
    time.sleep(1)  # Задержка 1 секунда перед выводом первого результата проверки
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, None,  # Второе сообщение без проверки
        personalized_message_3, None,  # Третье сообщение без проверки
        None, None, None  # Пустые графики для спидометров
    )
    
    checks_2 = perform_checks(personalized_message_2)
    formatted_checks_2 = format_checks(checks_2)
    time.sleep(1)  # Задержка 1 секунда перед выводом второго результата проверки
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, formatted_checks_2,  # Проверка для второго сообщения
        personalized_message_3, None,  # Третье сообщение без проверки
        None, None, None  # Пустые графики для спидометров
    )
    
    checks_3 = perform_checks(personalized_message_3)
    formatted_checks_3 = format_checks(checks_3)
    time.sleep(1)  # Задержка 1 секунда перед выводом третьего результата проверки
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, formatted_checks_2,  # Проверка для второго сообщения
        personalized_message_3, formatted_checks_3,  # Проверка для третьего сообщения
        None, None, None  # Пустые графики для спидометров
    )

    # Генерация и показ графиков спидометров с задержкой 2 секунды
    time.sleep(2)
    gauges = generate_random_gauges()
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, formatted_checks_2,  # Проверка для второго сообщения
        personalized_message_3, formatted_checks_3,  # Проверка для третьего сообщения
        gauges[0], None, None  # Первый график спидометра
    )
    
    time.sleep(2)
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, formatted_checks_2,  # Проверка для второго сообщения
        personalized_message_3, formatted_checks_3,  # Проверка для третьего сообщения
        gauges[0], gauges[1], None  # Первый и второй графики спидометра
    )
    
    time.sleep(2)
    yield (
        personalized_message_1, formatted_checks_1,  # Проверка для первого сообщения
        personalized_message_2, formatted_checks_2,  # Проверка для второго сообщения
        personalized_message_3, formatted_checks_3,  # Проверка для третьего сообщения
        gauges[0], gauges[1], gauges[2]  # Все три графика спидометра
    )


# Интерфейс Gradio
with gr.Blocks() as demo:
    # Твой интерфейс

    with gr.Tabs() as tabs:
        
        # Вкладка 1: Исходные данные
        with gr.TabItem("Исходные данные", id=0):
            with gr.Row():
                with gr.Column():
                    desc = gr.Textbox(
                        label="Описание предложения (предзаполненный пример можно поменять на свой)", 
                        lines=7,
                        value=(
                            "Необходимо предложить клиенту оформить дебетовую премиальную бизнес-карту Mastercard Preffered. "
                            "Обслуживание карты стоит 700 рублей в месяц, но клиент может пользоваться ей бесплатно. "
                            "Что необходимо сделать, чтобы воспользоваться предложением:\n"
                            "1. Оформить премиальную бизнес-карту в офисе банка или онлайн в интернет-банке СберБизнес.\n"
                            "2. Забрать карту.\n"
                            "3. В течение календарного месяца совершить по ней покупки на сумму от 100 000 рублей.\n"
                            "4. В течение следующего месяца пользоваться ей бесплатно."
                        )
                    )
                    benefits = gr.Textbox(
                        label="Преимущества (предзаполненный пример можно поменять на свой)", 
                        lines=5,
                        value=(
                            "Предложение по бесплатному обслуживанию — бессрочное.\n"
                            "Оплата покупок без отчётов и платёжных поручений.\n"
                            "Платёжные документы без комиссии.\n"
                            "Лимиты на расходы сотрудников.\n"
                            "Мгновенные переводы на карты любых банков."
                        )
                    )
                    
                    key_message = gr.Textbox(
                        label="Ключевое сообщение (предзаполненный пример можно поменять на свой)",
                        lines=3,
                        value="Бесплатное обслуживание при покупках от 100 000 рублей в месяц."
                    )

                with gr.Column():
                    gender = gr.Dropdown(label="Пол", choices=[None] + list(features.get('Пол', {}).keys()))
                    generation = gr.Dropdown(label="Поколение", choices=[None] + list(features.get('Поколение', {}).keys()))
                    psychotype = gr.Dropdown(label="Психотип", choices=[None] + list(features.get('Психотип', {}).keys()))
                    business_stage = gr.Dropdown(label="Стадия бизнеса", choices=[None] + list(features.get('Стадия бизнеса', {}).keys()))
                    industry = gr.Dropdown(label="Отрасль", choices=[None] + list(features.get('Отрасль', {}).keys()))
                    opf = gr.Dropdown(label="ОПФ", choices=[None] + list(features.get('ОПФ', {}).keys()))
            btn_to_prompts = gr.Button("Создать")
            
        # Вкладка 2: Промпты
        with gr.TabItem("Ассистент", id=1):
            with gr.Row():
                with gr.Column():
                    non_personalized_prompt = gr.Textbox(
                        label="Задание для копирайтера", 
                        lines=25,
                        interactive=False)
                with gr.Column():
                    personalized_prompt = gr.Textbox(label="Задание для редактора", lines=25)  # Увеличенная высота
                    
        # Вкладка 3: Сообщения
        with gr.TabItem("Сообщения", id=2):
            with gr.Row():
                gr.Markdown("### Копирайтер")
                gr.Markdown("### Редактор")
                
            with gr.Row():
                non_personalized_1 = gr.Textbox(label="Стандартное сообщение 1", lines=4, interactive=False)
                personalized_1 = gr.Textbox(label="Персонализированное сообщение 1", lines=4, interactive=False)

            with gr.Row():
                non_personalized_2 = gr.Textbox(label="Стандартное сообщение 2", lines=4, interactive=False)
                personalized_2 = gr.Textbox(label="Персонализированное сообщение 2", lines=4, interactive=False)

            with gr.Row():
                non_personalized_3 = gr.Textbox(label="Стандартное сообщение 3", lines=4, interactive=False)
                personalized_3 = gr.Textbox(label="Персонализированное сообщение 3", lines=4, interactive=False)

            # Четвертый ряд
            with gr.Row():
                btn_check = gr.Button("Проверить", elem_id="check3")
                btn_check.click(fn=change_tab, inputs=[gr.Number(value=3, visible=False)], outputs=tabs)

            # Сначала переключаем вкладку, потом запускаем генерацию сообщений
            btn_to_prompts.click(
                fn=change_tab, 
                inputs=[gr.Number(value=1, visible=False)],  # Переключение на вкладку "Ассистент" (id=1)
                outputs=tabs  # Обновляем вкладку
            ).then(
                fn=generate_all_messages, 
                inputs=[desc, benefits, key_message, gender, generation, psychotype, business_stage, industry, opf],  # Входные текстовые поля
                outputs=[
                    non_personalized_prompt, personalized_prompt,  # Поля для задания копирайтера и редактора (на вкладке "Ассистент")
                    non_personalized_1, personalized_1,  # Сообщения на вкладке "Сообщения"
                    non_personalized_2, personalized_2,
                    non_personalized_3, personalized_3
                ]
            )

        # Вкладка 4: Проверка
        with gr.TabItem("Проверка", id=3):
            with gr.Row():
                gr.Markdown("### Редактор")
                gr.Markdown("### Корректор")
                gr.Markdown("### Аналитик")
                
            with gr.Row():
                personalized_message_1 = gr.Textbox(label="Персонализированное сообщение 1", lines=5, interactive=False)
                check_message_1 = gr.Textbox(label="Проверка сообщения 1", lines=5, interactive=False)
                with gr.Column():
                    gr.HTML("<div style='display:flex; justify-content:center; width:100%;'>")
                    success_forecast_1 = gr.Plot(label="Прогноз успешности сообщения 1")
                    gr.HTML("</div>")
            
            with gr.Row():
                personalized_message_2 = gr.Textbox(label="Персонализированное сообщение 2", lines=5)
                check_message_2 = gr.Textbox(label="Проверка сообщения 2", lines=5, interactive=False)
                with gr.Column():
                    gr.HTML("<div style='display:flex; justify-content:center; width:100%;'>")
                    success_forecast_2 = gr.Plot(label="Прогноз успешности сообщения 2")
                    gr.HTML("</div>")
            
            with gr.Row():
                personalized_message_3 = gr.Textbox(label="Персонализированное сообщение 3", lines=5, interactive=False)
                check_message_3 = gr.Textbox(label="Проверка сообщения 3", lines=5, interactive=False)              
                with gr.Column():
                    gr.HTML("<div style='display:flex; justify-content:center; width:100%;'>")
                    success_forecast_3 = gr.Plot(label="Прогноз успешности сообщения 3")
                    gr.HTML("</div>")
                
            # Модифицируем нажатие кнопки "Проверить"
            btn_check.click(
                fn=change_tab, 
                inputs=[gr.Number(value=3, visible=False)],  # Переключение на вкладку "Проверка"
                outputs=tabs  # Обновляем вкладку
            ).then(
                fn=perform_all_checks_and_show_results,
                inputs=[personalized_1, personalized_2, personalized_3],  # Входные персонализированные сообщения
                outputs=[
                    personalized_message_1, check_message_1,  # Результаты проверок для первого сообщения
                    personalized_message_2, check_message_2,  # Результаты проверок для второго сообщения
                    personalized_message_3, check_message_3   # Результаты проверок для третьего сообщения
                ]
            ).then(
                fn=generate_random_gauges, 
                inputs=[],  # Нет входных данных для спидометров
                outputs=[success_forecast_1, success_forecast_2, success_forecast_3]  # Вывод значений спидометров
            )

demo.launch()