Adding Teacher Panel to start/end event recording
parent
6b79de7866
commit
8aac49c38c
Binary file not shown.
149
server.py
149
server.py
|
@ -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)
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue