Adding Teacher Panel to start/end event recording

main
Amēlija I 2025-04-29 20:11:39 +03:00
parent 6b79de7866
commit 8aac49c38c
4 changed files with 242 additions and 26 deletions

Binary file not shown.

149
server.py
View File

@ -1,53 +1,150 @@
from flask import Flask, request, jsonify
from flask import Flask, request, jsonify, session, render_template
from pymongo import MongoClient
from datetime import datetime
import os
app = Flask(__name__)
application = app
app.secret_key = os.urandom(24)
try:
from local_settings import *
except ImportError:
print("Can't import from localsettings, terminating")
exit()
#try:
# from local_settings import *
#except ImportError:
# print("Can't import from localsettings, terminating")
# exit()
EXPECTED_PASSWORD = APPLICATION_PASSWORD
uri = MONGO_URI
EXPECTED_PASSWORD = "8V33zfvkImxBZcigtQ"
#APPLICATION_PASSWORD
uri = "mongodb://zpdai.rkg.lv/submit"
#MONGO_URI
TEACHER_PASSWORD = "teacher_password_123"
mongo_client = MongoClient(uri)
db = mongo_client["user_interactions"]
collection = db["user_events"]
events_col = db["user_events"]
sessions_col = db["monitoring_sessions"]
current_session = {
'active': False,
'class_name': None,
'start_time': None,
'session_id': None
}
def validate_event(event):
required_keys = ["@version", "type", "TimeStamp", "data", "@timestamp"]
return all(key in event for key in required_keys)
@app.after_request
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, X-Password'
return response
@app.route('/api/session_status', methods=['OPTIONS'])
@app.route('/teacher/control', methods=['OPTIONS'])
def handle_options():
return jsonify(), 200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
@app.route('/submit', methods=['POST', 'OPTIONS'])
def submit_event():
if request.method == 'OPTIONS':
resp = jsonify()
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
resp.headers['Access-Control-Allow-Headers'] = 'Content-Type, X-Password'
return resp
return jsonify(), 204
try:
password = request.headers.get('X-Password')
if password != EXPECTED_PASSWORD:
return jsonify({"error": "Неверный пароль"}), 403
event = request.get_json()
if not event or not validate_event(event):
resp = jsonify({"error": "Неверный формат"})
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp, 400
return jsonify({"error": "Неверный формат"}), 400
collection.insert_one(event)
if current_session['active']:
event['session_id'] = current_session['session_id']
events_col.insert_one(event)
return jsonify({"message": "Действие сохранено"}), 200
else:
return jsonify({"error": "Сессия не активна"}), 400
resp = jsonify({"message": "Действие успешно сохранено"})
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp, 200
except Exception as e:
resp = jsonify({"error": str(e)})
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp, 500
return jsonify({"error": str(e)}), 500
@app.route('/api/session_status', methods=['GET', 'OPTIONS'])
def session_status():
if request.method == 'OPTIONS':
return jsonify(), 204
return jsonify({
'active': current_session['active'],
'class_name': current_session['class_name'],
'session_id': current_session['session_id']
}), 200
@app.route('/teacher', methods=['GET'])
def teacher_panel():
if not session.get('teacher_logged_in'):
return render_template('login.html')
return render_template('teacher_panel.html')
@app.route('/teacher/login', methods=['POST', 'OPTIONS'])
def teacher_login():
if request.method == 'OPTIONS':
return jsonify(), 204
password = request.json.get('password')
if password == TEACHER_PASSWORD:
session['teacher_logged_in'] = True
return jsonify({'status': 'success'}), 200
return jsonify({'error': 'Invalid password'}), 401
@app.route('/teacher/control', methods=['POST', 'OPTIONS'])
def session_control():
if request.method == 'OPTIONS':
return jsonify(), 200
if not session.get('teacher_logged_in'):
return jsonify({'error': 'Unauthorized'}), 401
action = request.json.get('action')
class_name = request.json.get('class_name')
if action == 'start' and class_name:
current_session.update({
'active': True,
'class_name': class_name,
'start_time': datetime.now(),
'session_id': os.urandom(16).hex()
})
sessions_col.insert_one({
'session_id': current_session['session_id'],
'class': class_name,
'start_time': current_session['start_time'],
'end_time': None,
'active': True
})
return jsonify({'status': 'session_started'}), 200
elif action == 'stop':
sessions_col.update_one(
{'session_id': current_session['session_id']},
{'$set': {'active': False, 'end_time': datetime.now()}}
)
current_session.update({
'active': False,
'class_name': None,
'start_time': None,
'session_id': None
})
return jsonify({'status': 'session_stopped'}), 200
return jsonify({'error': 'Invalid request'}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Авторизация учителя</title>
<style>
body { font-family: Arial, sans-serif; padding: 40px; }
form {
max-width: 300px;
margin: 0 auto;
}
input, button {
width: 100%;
padding: 10px;
margin: 10px 0;
}
</style>
</head>
<body>
<form onsubmit="login(event)">
<h2>Вход для учителя</h2>
<input type="password" id="password" placeholder="Пароль" required>
<button type="submit">Войти</button>
<p id="error" style="color: red;"></p>
</form>
<script>
async function login(event) {
event.preventDefault();
const password = document.getElementById('password').value;
const response = await fetch('/teacher/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ password })
});
if (response.ok) {
window.location.href = '/teacher';
} else {
document.getElementById('error').textContent = 'Неверный пароль';
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<title>Панель учителя</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.status { padding: 10px; margin: 10px 0; }
.active { background: #dff0d8; }
.inactive { background: #f2dede; }
</style>
</head>
<body>
<div id="status" class="status"></div>
<input type="text" id="className" placeholder="Название класса">
<button onclick="controlSession('start')">Начать сессию</button>
<button onclick="controlSession('stop')">Остановить сессию</button>
<script>
async function controlSession(action) {
try {
const className = action === 'start'
? document.getElementById('className').value.trim()
: null;
if (action === 'start' && !className) {
alert('Введите название класса!');
return;
}
const response = await fetch('/teacher/control', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Password': 'teacher_password_123'
},
body: JSON.stringify({
action: action,
class_name: className
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
alert(result.status || (action === 'start' ? 'Сессия начата' : 'Сессия остановлена'));
updateStatus();
} catch (error) {
console.error('Ошибка управления сессией:', error);
alert(`Ошибка: ${error.message}`);
}
}
async function updateStatus() {
const response = await fetch('/api/session_status');
const status = await response.json();
const statusDiv = document.getElementById('status');
statusDiv.className = `status ${status.active ? 'active' : 'inactive'}`;
statusDiv.textContent = status.active
? `Активная сессия: ${status.class_name}`
: 'Сессия не активна';
}
setInterval(updateStatus, 3000);
updateStatus();
</script>
</body>
</html>