Spaces:
Sleeping
Sleeping
| # routes/feedback.py | |
| from flask import Blueprint, request, jsonify, g, Response | |
| from bson.objectid import ObjectId | |
| from db import db | |
| from utils.auth import token_required, admin_required | |
| import csv | |
| import io | |
| from datetime import datetime | |
| feedback_bp = Blueprint("feedback", __name__) | |
| # create feedback | |
| def create_feedback(): | |
| data = request.json or {} | |
| course_id = data.get("course_id") | |
| rating = int(data.get("rating", 0)) | |
| message = data.get("message", "").strip() | |
| if not course_id or not (1 <= rating <= 5): | |
| return jsonify({"error":"course_id and rating 1-5 required"}), 400 | |
| fb = { | |
| "course_id": course_id, | |
| "rating": rating, | |
| "message": message, | |
| "student_id": g.current_user["_id"], | |
| "student_name": g.current_user.get("name"), | |
| "created_at": datetime.utcnow() | |
| } | |
| res = db.feedback.insert_one(fb) | |
| return jsonify({"message":"Feedback submitted", "id": str(res.inserted_id)}), 201 | |
| # list feedbacks (students see their own, admins can filter) | |
| def list_feedbacks(): | |
| page = int(request.args.get("page", 1)) | |
| limit = int(request.args.get("limit", 10)) | |
| course = request.args.get("course") | |
| rating = request.args.get("rating") | |
| student_id = request.args.get("student_id") | |
| query = {} | |
| if g.current_user.get("role") != "admin": | |
| query["student_id"] = g.current_user["_id"] | |
| else: | |
| # admin filters | |
| if student_id: | |
| query["student_id"] = student_id | |
| if course: | |
| query["course_id"] = course | |
| if rating: | |
| try: | |
| query["rating"] = int(rating) | |
| except: | |
| pass | |
| total = db.feedback.count_documents(query) | |
| skip = (page-1)*limit | |
| docs = list(db.feedback.find(query).sort("created_at", -1).skip(skip).limit(limit)) | |
| for d in docs: | |
| d["_id"] = str(d["_id"]) | |
| if isinstance(d.get("created_at"), datetime): | |
| d["created_at"] = d["created_at"].isoformat() | |
| return jsonify({"total": total, "page": page, "limit": limit, "data": docs}) | |
| # edit feedback (only owner or admin) | |
| def edit_feedback(fid): | |
| data = request.json or {} | |
| fb = db.feedback.find_one({"_id": ObjectId(fid)}) | |
| if not fb: | |
| return jsonify({"error":"Not found"}), 404 | |
| if g.current_user.get("role") != "admin" and fb.get("student_id") != g.current_user["_id"]: | |
| return jsonify({"error":"Not allowed"}), 403 | |
| update = {} | |
| if "rating" in data: update["rating"] = int(data["rating"]) | |
| if "message" in data: update["message"] = data["message"] | |
| db.feedback.update_one({"_id": ObjectId(fid)}, {"$set": update}) | |
| return jsonify({"message":"Updated"}) | |
| # delete feedback (owner or admin) | |
| def delete_feedback(fid): | |
| fb = db.feedback.find_one({"_id": ObjectId(fid)}) | |
| if not fb: | |
| return jsonify({"error":"Not found"}), 404 | |
| if g.current_user.get("role") != "admin" and fb.get("student_id") != g.current_user["_id"]: | |
| return jsonify({"error":"Not allowed"}), 403 | |
| db.feedback.delete_one({"_id": ObjectId(fid)}) | |
| return jsonify({"message":"Deleted"}) | |
| # admin export CSV | |
| def export_csv(): | |
| # optional filters | |
| course = request.args.get("course") | |
| rating = request.args.get("rating") | |
| student_id = request.args.get("student_id") | |
| query = {} | |
| if course: query["course_id"] = course | |
| if rating: | |
| try: query["rating"] = int(rating) | |
| except: pass | |
| if student_id: query["student_id"] = student_id | |
| docs = list(db.feedback.find(query).sort("created_at", -1)) | |
| # CSV | |
| si = io.StringIO() | |
| cw = csv.writer(si) | |
| cw.writerow(["feedback_id","course_id","rating","message","student_id","student_name","created_at"]) | |
| for d in docs: | |
| cw.writerow([ | |
| str(d.get("_id")), | |
| d.get("course_id"), | |
| d.get("rating"), | |
| d.get("message"), | |
| d.get("student_id"), | |
| d.get("student_name"), | |
| d.get("created_at").isoformat() if d.get("created_at") else "" | |
| ]) | |
| output = si.getvalue() | |
| return Response(output, mimetype="text/csv", | |
| headers={"Content-Disposition":"attachment;filename=feedbacks.csv"}) | |