Arghet6 commited on
Commit
33fa6ad
·
verified ·
1 Parent(s): 08e9339

Upload 12 files

Browse files
Files changed (6) hide show
  1. .gitattributes +1 -0
  2. admin.py +121 -0
  3. app.py +205 -27
  4. instance/chats.db +0 -0
  5. models.py +1 -0
  6. requirements.txt +16 -14
.gitattributes CHANGED
@@ -1,2 +1,3 @@
1
  *.bin filter=lfs diff=lfs merge=lfs -text
2
  *.pth filter=lfs diff=lfs merge=lfs -text
 
 
1
  *.bin filter=lfs diff=lfs merge=lfs -text
2
  *.pth filter=lfs diff=lfs merge=lfs -text
3
+ instance/chats.db filter=lfs diff=lfs merge=lfs -text
admin.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint, render_template, jsonify, request, flash, redirect, url_for
2
+ from flask_login import login_required, current_user
3
+ from models import User, Chat, AnalysisReport
4
+ from extensions import db
5
+ from datetime import datetime, timedelta
6
+ from sqlalchemy import func
7
+
8
+ admin_bp = Blueprint('admin_bp', __name__, template_folder='templates')
9
+
10
+
11
+ @admin_bp.before_request
12
+ @login_required
13
+ def restrict_admin_panel():
14
+ if not current_user.is_admin:
15
+ flash('Доступ запрещен', 'danger')
16
+ return redirect(url_for('index'))
17
+
18
+
19
+ @admin_bp.route('/')
20
+ def dashboard():
21
+ # Статистика за последние 30 дней
22
+ end_date = datetime.now()
23
+ start_date = end_date - timedelta(days=30)
24
+
25
+ # Основная статистика
26
+ users_count = User.query.count()
27
+ new_users = User.query.filter(User.created_at >= start_date).count()
28
+ reports_count = AnalysisReport.query.count()
29
+ active_users = db.session.query(Chat.user_id).distinct().count()
30
+
31
+ # Статистика по эмоциям
32
+ emotion_stats = db.session.query(
33
+ AnalysisReport.emotion,
34
+ func.count(AnalysisReport.id).label('count')
35
+ ).group_by(AnalysisReport.emotion).all()
36
+
37
+ # Активность пользователей
38
+ user_activity = db.session.query(
39
+ User.username,
40
+ func.count(AnalysisReport.id).label('report_count')
41
+ ).join(
42
+ AnalysisReport, User.id == AnalysisReport.user_id
43
+ ).group_by(
44
+ User.username
45
+ ).order_by(
46
+ func.count(AnalysisReport.id).desc()
47
+ ).limit(10).all()
48
+
49
+ return render_template('admin/dashboard.html',
50
+ users_count=users_count,
51
+ new_users=new_users,
52
+ reports_count=reports_count,
53
+ active_users=active_users,
54
+ emotion_stats=emotion_stats,
55
+ user_activity=user_activity)
56
+
57
+
58
+ @admin_bp.route('/users')
59
+ def manage_users():
60
+ search_query = request.args.get('search', '')
61
+ page = request.args.get('page', 1, type=int)
62
+
63
+ query = User.query.order_by(User.created_at.desc())
64
+
65
+ if search_query:
66
+ query = query.filter(User.username.ilike(f'%{search_query}%') |
67
+ User.email.ilike(f'%{search_query}%'))
68
+
69
+ users = query.paginate(page=page, per_page=20, error_out=False)
70
+
71
+ return render_template('admin/users.html',
72
+ users=users,
73
+ search_query=search_query)
74
+
75
+
76
+ @admin_bp.route('/reports')
77
+ def view_reports():
78
+ emotion_filter = request.args.get('emotion')
79
+ page = request.args.get('page', 1, type=int)
80
+
81
+ query = AnalysisReport.query.order_by(AnalysisReport.created_at.desc())
82
+
83
+ if emotion_filter:
84
+ query = query.filter(AnalysisReport.emotion == emotion_filter)
85
+
86
+ reports = query.paginate(page=page, per_page=50, error_out=False)
87
+
88
+ # Получаем список всех эмоций для фильтра
89
+ emotions = db.session.query(
90
+ AnalysisReport.emotion.distinct().label('emotion')
91
+ ).all()
92
+
93
+ return render_template('admin/reports.html',
94
+ reports=reports,
95
+ emotions=emotions,
96
+ current_emotion=emotion_filter)
97
+
98
+
99
+ @admin_bp.route('/toggle_admin/<int:user_id>', methods=['POST'])
100
+ def toggle_admin(user_id):
101
+ user = User.query.get_or_404(user_id)
102
+ user.is_admin = not user.is_admin
103
+ db.session.commit()
104
+ return jsonify({'status': 'success', 'is_admin': user.is_admin})
105
+
106
+
107
+ @admin_bp.route('/delete_user/<int:user_id>', methods=['POST'])
108
+ def delete_user(user_id):
109
+ if current_user.id == user_id:
110
+ return jsonify({'status': 'error', 'message': 'Нельзя удалить себя'}), 400
111
+
112
+ user = User.query.get_or_404(user_id)
113
+
114
+ # Удаляем все связанные данные пользователя
115
+ AnalysisReport.query.filter_by(user_id=user_id).delete()
116
+ Chat.query.filter_by(user_id=user_id).delete()
117
+
118
+ db.session.delete(user)
119
+ db.session.commit()
120
+
121
+ return jsonify({'status': 'success'})
app.py CHANGED
@@ -12,7 +12,10 @@ import sqlite3
12
  from pathlib import Path
13
  import whisper
14
  from extensions import db, login_manager
15
-
 
 
 
16
 
17
  instance_path = Path(__file__).parent / 'instance'
18
  instance_path.mkdir(exist_ok=True, mode=0o755)
@@ -27,7 +30,7 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
27
  db.init_app(app)
28
  login_manager.init_app(app)
29
  login_manager.login_view = 'auth_bp.login'
30
-
31
  # Инициализация моделей
32
  def init_models():
33
  try:
@@ -63,12 +66,38 @@ models = init_models()
63
  if not models:
64
  raise RuntimeError("Не удалось загрузить модели")
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  # Импорт Blueprint
67
  from auth import auth_bp
68
  from profile import profile_bp
69
 
70
  app.register_blueprint(auth_bp)
71
  app.register_blueprint(profile_bp)
 
72
 
73
  # Делаем переменные доступными
74
  emotion_map = models['emotion_map']
@@ -76,6 +105,21 @@ speech_to_text_model = models['speech_to_text_model']
76
  text_classifier = models['text_classifier']
77
  audio_classifier = models['audio_classifier']
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  def transcribe_audio(audio_path):
81
  """Преобразование аудио в текст с помощью Whisper"""
@@ -94,29 +138,12 @@ login_manager = LoginManager(app)
94
  login_manager.login_view = 'login'
95
 
96
 
97
- # Модель пользователя для Flask-Login
98
- class User(UserMixin):
99
- def __init__(self, id, username, email, password_hash):
100
- self.id = id
101
- self.username = username
102
- self.email = email
103
- self.password_hash = password_hash
104
-
105
- def check_password(self, password):
106
- return check_password_hash(self.password_hash, password)
107
 
108
 
109
  @login_manager.user_loader
110
  def load_user(user_id):
111
- conn = get_db_connection()
112
- user = conn.execute(
113
- "SELECT id, username, email, password_hash FROM users WHERE id = ?",
114
- (user_id,)
115
- ).fetchone()
116
- conn.close()
117
- if user:
118
- return User(id=user['id'], username=user['username'], email=user['email'], password_hash=user['password_hash'])
119
- return None
120
 
121
 
122
  # Инициализация БД
@@ -256,6 +283,46 @@ def index():
256
  conn.close()
257
 
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  @app.route("/analyze", methods=["POST"])
260
  @login_required
261
  def analyze_text():
@@ -356,13 +423,80 @@ def analyze_audio():
356
  return jsonify({'error': str(e)}), 500
357
 
358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  @app.route('/get_chats')
360
  @login_required
361
  def get_chats():
362
  conn = get_db_connection()
363
  try:
364
  chats = conn.execute(
365
- "SELECT chat_id, title FROM chats WHERE user_id = ? ORDER BY created_at DESC",
366
  (current_user.id,)
367
  ).fetchall()
368
  return jsonify([dict(chat) for chat in chats])
@@ -373,21 +507,60 @@ def get_chats():
373
  @app.route('/start_chat', methods=['POST'])
374
  @login_required
375
  def start_chat():
376
- conn = get_db_connection()
377
  try:
378
  chat_id = str(uuid.uuid4())
 
 
 
379
  conn.execute(
380
- "INSERT INTO chats (chat_id, user_id, title, created_at) VALUES (?, ?, ?, ?)",
381
- (chat_id, current_user.id, f"Новый чат {datetime.now().strftime('%d.%m')}", datetime.now())
382
  )
383
  conn.commit()
384
- return jsonify({"chat_id": chat_id, "title": f"Новый чат {datetime.now().strftime('%d.%m')}"})
 
 
 
 
 
385
  except Exception as e:
386
  return jsonify({"error": str(e)}), 500
387
  finally:
388
  conn.close()
389
 
390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  @app.route('/load_chat/<chat_id>')
392
  @login_required
393
  def load_chat(chat_id):
@@ -471,5 +644,10 @@ def save_message():
471
  finally:
472
  conn.close()
473
 
 
 
 
474
  if __name__ == "__main__":
475
- app.run(debug=True)
 
 
 
12
  from pathlib import Path
13
  import whisper
14
  from extensions import db, login_manager
15
+ import json
16
+ from admin import admin_bp
17
+ from flask_migrate import Migrate
18
+ from models import User
19
 
20
  instance_path = Path(__file__).parent / 'instance'
21
  instance_path.mkdir(exist_ok=True, mode=0o755)
 
30
  db.init_app(app)
31
  login_manager.init_app(app)
32
  login_manager.login_view = 'auth_bp.login'
33
+ migrate = Migrate(app, db)
34
  # Инициализация моделей
35
  def init_models():
36
  try:
 
66
  if not models:
67
  raise RuntimeError("Не удалось загрузить модели")
68
 
69
+
70
+ @app.template_filter('datetimeformat')
71
+ def datetimeformat(value, format='%d.%m.%Y %H:%M'):
72
+ if value is None:
73
+ return ""
74
+ return value.strftime(format)
75
+
76
+ @app.context_processor
77
+ def utility_processor():
78
+ return {
79
+ 'emotion_map': {
80
+ 'joy': '😊 Радость',
81
+ 'neutral': '😐 Нейтрально',
82
+ 'anger': '😠 Злость',
83
+ 'sadness': '😢 Грусть',
84
+ 'surprise': '😲 Удивление'
85
+ },
86
+ 'get_emotion_color': lambda emotion: {
87
+ 'joy': '#00b894',
88
+ 'neutral': '#636e72',
89
+ 'anger': '#d63031',
90
+ 'sadness': '#0984e3',
91
+ 'surprise': '#fdcb6e'
92
+ }.get(emotion, '#4a4ae8')
93
+ }
94
  # Импорт Blueprint
95
  from auth import auth_bp
96
  from profile import profile_bp
97
 
98
  app.register_blueprint(auth_bp)
99
  app.register_blueprint(profile_bp)
100
+ app.register_blueprint(admin_bp, url_prefix='/admin')
101
 
102
  # Делаем переменные доступными
103
  emotion_map = models['emotion_map']
 
105
  text_classifier = models['text_classifier']
106
  audio_classifier = models['audio_classifier']
107
 
108
+ @app.cli.command('create-admin')
109
+ def create_admin():
110
+ """Создание администратора"""
111
+ email = input("Введите email: ")
112
+ password = input("Введите пароль: ")
113
+ user = User.query.filter_by(email=email).first()
114
+ if user:
115
+ user.is_admin = True
116
+ user.set_password(password)
117
+ else:
118
+ user = User(email=email, username=email, is_admin=True)
119
+ user.set_password(password)
120
+ db.session.add(user)
121
+ db.session.commit()
122
+ print(f"Администратор {email} создан")
123
 
124
  def transcribe_audio(audio_path):
125
  """Преобразование аудио в текст с помощью Whisper"""
 
138
  login_manager.login_view = 'login'
139
 
140
 
 
 
 
 
 
 
 
 
 
 
141
 
142
 
143
  @login_manager.user_loader
144
  def load_user(user_id):
145
+ from models import User
146
+ return User.query.get(int(user_id))
 
 
 
 
 
 
 
147
 
148
 
149
  # Инициализация БД
 
283
  conn.close()
284
 
285
 
286
+ @app.route('/profile')
287
+ @login_required
288
+ def profile():
289
+ conn = get_db_connection()
290
+ try:
291
+ # Запрашиваем все анализы пользователя
292
+ reports = conn.execute(
293
+ "SELECT * FROM analysis_reports WHERE user_id = ? ORDER BY created_at DESC",
294
+ (current_user.id,)
295
+ ).fetchall()
296
+
297
+ # Статистика: общее количество анализов
298
+ total_reports = len(reports)
299
+
300
+ # Статистика: самая частая эмоция
301
+ emotion_counts = {}
302
+ for r in reports:
303
+ emotion_counts[r['emotion']] = emotion_counts.get(r['emotion'], 0) + 1
304
+
305
+ most_common_emotion = max(emotion_counts, key=emotion_counts.get) if emotion_counts else None
306
+
307
+ return render_template(
308
+ "profile.html",
309
+ reports=reports,
310
+ total_reports=total_reports,
311
+ most_common_emotion=most_common_emotion,
312
+ emotion_map={
313
+ 'joy': '😊 Радость',
314
+ 'neutral': '😐 Нейтрально',
315
+ 'anger': '😠 Злость',
316
+ 'sadness': '😢 Грусть',
317
+ 'surprise': '😲 Удивление'
318
+ }
319
+ )
320
+ except Exception as e:
321
+ flash(f"Ошибка загрузки данных: {e}", "danger")
322
+ return redirect(url_for('index'))
323
+ finally:
324
+ conn.close()
325
+
326
  @app.route("/analyze", methods=["POST"])
327
  @login_required
328
  def analyze_text():
 
423
  return jsonify({'error': str(e)}), 500
424
 
425
 
426
+
427
+
428
+
429
+ @app.route('/analyze_telegram_chat', methods=['POST'])
430
+ @login_required
431
+ def analyze_telegram_chat():
432
+ if 'file' not in request.files:
433
+ return jsonify({'error': 'No file uploaded'}), 400
434
+ file = request.files['file']
435
+ if file.filename.split('.')[-1].lower() != 'json':
436
+ return jsonify({'error': 'Invalid file format. Only JSON allowed'}), 400
437
+ try:
438
+ data = json.load(file)
439
+
440
+ messages = []
441
+ for msg in data.get('messages', []):
442
+ text = msg.get('text')
443
+ sender = msg.get('from') or msg.get('sender') or 'Неизвестный пользователь'
444
+ if isinstance(text, str) and len(text.strip()) > 5:
445
+ messages.append({
446
+ 'text': text,
447
+ 'timestamp': msg.get('date', datetime.now().isoformat()),
448
+ 'from': sender # <-- сохраняем имя отправителя
449
+ })
450
+
451
+ if not messages:
452
+ return jsonify({'error': 'No valid text messages found'}), 400
453
+
454
+ results = []
455
+ for msg in messages[:500]:
456
+ prediction = text_classifier(msg['text'])[0]
457
+ results.append({
458
+ 'text': msg['text'],
459
+ 'emotion': prediction['label'],
460
+ 'confidence': prediction['score'],
461
+ 'timestamp': msg['timestamp'],
462
+ 'from': msg['from'] # <-- передаем дальше
463
+ })
464
+
465
+ conn = get_db_connection()
466
+ conn.execute('''
467
+ CREATE TABLE IF NOT EXISTS telegram_analysis (
468
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
469
+ user_id INTEGER,
470
+ data TEXT,
471
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
472
+ FOREIGN KEY(user_id) REFERENCES users(id)
473
+ )
474
+ ''')
475
+ conn.execute(
476
+ "INSERT INTO telegram_analysis (user_id, data) VALUES (?, ?)",
477
+ (current_user.id, json.dumps(results))
478
+ )
479
+ conn.commit()
480
+
481
+ return jsonify({
482
+ 'status': 'success',
483
+ 'message_count': len(results),
484
+ })
485
+
486
+ except Exception as e:
487
+ print(f"Error during analysis: {e}")
488
+ return jsonify({'error': f'Server error: {str(e)}'}), 500
489
+ finally:
490
+ conn.close()
491
+
492
+
493
  @app.route('/get_chats')
494
  @login_required
495
  def get_chats():
496
  conn = get_db_connection()
497
  try:
498
  chats = conn.execute(
499
+ "SELECT chat_id, title, created_at FROM chats WHERE user_id = ? ORDER BY created_at DESC",
500
  (current_user.id,)
501
  ).fetchall()
502
  return jsonify([dict(chat) for chat in chats])
 
507
  @app.route('/start_chat', methods=['POST'])
508
  @login_required
509
  def start_chat():
 
510
  try:
511
  chat_id = str(uuid.uuid4())
512
+ title = f"Чат от {datetime.now().strftime('%d.%m.%Y %H:%M')}"
513
+
514
+ conn = get_db_connection()
515
  conn.execute(
516
+ "INSERT INTO chats (chat_id, user_id, created_at, title) VALUES (?, ?, ?, ?)",
517
+ (chat_id, current_user.id, datetime.now(), title)
518
  )
519
  conn.commit()
520
+
521
+ return jsonify({
522
+ "success": True,
523
+ "chat_id": chat_id,
524
+ "title": title
525
+ })
526
  except Exception as e:
527
  return jsonify({"error": str(e)}), 500
528
  finally:
529
  conn.close()
530
 
531
 
532
+ @app.route('/delete_chat/<chat_id>', methods=['DELETE'])
533
+ @login_required
534
+ def delete_chat(chat_id):
535
+ conn = get_db_connection()
536
+ try:
537
+ # Удаляем связанные сообщения
538
+ conn.execute("DELETE FROM messages WHERE chat_id = ?", (chat_id,))
539
+ # Удаляем сам чат
540
+ conn.execute("DELETE FROM chats WHERE chat_id = ? AND user_id = ?", (chat_id, current_user.id))
541
+ conn.commit()
542
+ return jsonify({"success": True})
543
+ except Exception as e:
544
+ return jsonify({"error": str(e)}), 500
545
+ finally:
546
+ conn.close()
547
+
548
+
549
+ @app.route('/get_telegram_analysis')
550
+ @login_required
551
+ def get_telegram_analysis():
552
+ conn = get_db_connection()
553
+ try:
554
+ analyses = conn.execute(
555
+ "SELECT id, data, created_at FROM telegram_analysis WHERE user_id = ?",
556
+ (current_user.id,)
557
+ ).fetchall()
558
+ return jsonify([dict(analysis) for analysis in analyses])
559
+ except Exception as e:
560
+ return jsonify({"error": str(e)}), 500
561
+ finally:
562
+ conn.close()
563
+
564
  @app.route('/load_chat/<chat_id>')
565
  @login_required
566
  def load_chat(chat_id):
 
644
  finally:
645
  conn.close()
646
 
647
+
648
+
649
+
650
  if __name__ == "__main__":
651
+ app.run(debug=True)
652
+
653
+
instance/chats.db CHANGED
Binary files a/instance/chats.db and b/instance/chats.db differ
 
models.py CHANGED
@@ -9,6 +9,7 @@ class User(UserMixin, db.Model):
9
  username = db.Column(db.String(50), unique=True, nullable=False)
10
  email = db.Column(db.String(100), unique=True, nullable=False)
11
  password_hash = db.Column(db.String(200), nullable=False)
 
12
  created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
13
  chats = db.relationship('Chat', backref='user', lazy=True)
14
  reports = db.relationship('AnalysisReport', backref='user', lazy=True)
 
9
  username = db.Column(db.String(50), unique=True, nullable=False)
10
  email = db.Column(db.String(100), unique=True, nullable=False)
11
  password_hash = db.Column(db.String(200), nullable=False)
12
+ is_admin = db.Column(db.Boolean, default=False)
13
  created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
14
  chats = db.relationship('Chat', backref='user', lazy=True)
15
  reports = db.relationship('AnalysisReport', backref='user', lazy=True)
requirements.txt CHANGED
@@ -1,14 +1,16 @@
1
- flask>=2.0.0
2
- flask-login>=0.5.0
3
- flask-sqlalchemy>=2.5.1
4
- flask-wtf>=1.0.0
5
- transformers>=4.15.0
6
- torch>=1.9.0
7
- torchaudio>=0.9.0
8
- pydub>=0.25.1
9
- librosa>=0.8.0
10
- numpy>=1.21.0
11
- gunicorn
12
- openai-whisper
13
- python-dotenv>=0.19.0
14
- email-validator>=1.1.3
 
 
 
1
+ flask==2.3.2
2
+ werkzeug==2.3.7
3
+ flask-login==0.6.2
4
+ flask-sqlalchemy==2.5.1
5
+ sqlalchemy==1.4.46
6
+ flask-wtf==1.1.1
7
+ transformers==4.33.3
8
+ torch==2.1.0
9
+ torchaudio==2.1.0
10
+ pydub==0.25.1
11
+ librosa==0.10.1
12
+ numpy==1.24.3
13
+ gunicorn==21.2.0
14
+ openai-whisper==20231117
15
+ python-dotenv==1.0.0
16
+ email-validator==2.0.0.post2