From 63507c66bf05080bf87d97bef2b85a216c04d8d6 Mon Sep 17 00:00:00 2001 From: Martin Mares <mj@ucw.cz> Date: Fri, 11 Oct 2024 20:02:27 +0200 Subject: [PATCH] Students can leave courses if they have no posts Closes #98. --- owl/course.py | 36 +++++++++++++++++++++++++++++++++++- owl/templates/course.html | 5 +++++ owl/templates/leave.html | 21 +++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 owl/templates/leave.html diff --git a/owl/course.py b/owl/course.py index 43f333d..72deb1a 100644 --- a/owl/course.py +++ b/owl/course.py @@ -5,7 +5,7 @@ import datetime from decimal import Decimal from flask import Flask, render_template, request, make_response, g, request_tearing_down, redirect, url_for, flash from flask_wtf import FlaskForm -from sqlalchemy import select, and_, or_, false +from sqlalchemy import select, and_, or_, false, delete import sqlalchemy.sql.functions as func from sqlalchemy.orm import joinedload, aliased from typing import Optional, Tuple @@ -215,6 +215,40 @@ def enroll_commit(course: db.Course) -> None: sess.commit() +# Leaving + +class LeaveConfirmForm(FlaskForm): + pass + + +@app.route('/c/<sident>/<cident>/leave', methods=('GET', 'POST')) +def leave(sident: str, cident: str) -> str: + app.course_init(sident, cident) + sess = db.get_session() + + err = None + if g.is_teacher: + err = 'Teachers are not allowed to leave their courses.' + else: + if sess.scalar( + select(db.Post) + .join(db.Topic) + .filter(or_(db.Post.target_uid == g.uid, db.Post.author_uid == g.uid)) + .filter(db.Topic.course == g.course) + .limit(1) + ): + err = 'You cannot leave, because you already have posts in this course.' + + form = LeaveConfirmForm() + if err is None and form.validate_on_submit(): + sess.execute(delete(db.Enroll).where(and_(db.Enroll.uid == g.uid, db.Enroll.course == g.course))) + sess.commit() + flash(f'You left the course {g.course.name}.', 'info') + return redirect(url_for('index')) + + return render_template('leave.html', form=form, error=err) + + # Topics @app.route('/c/<sident>/<cident>/<tident>/', methods=('GET', 'POST')) diff --git a/owl/templates/course.html b/owl/templates/course.html index afd17f7..07e2168 100644 --- a/owl/templates/course.html +++ b/owl/templates/course.html @@ -84,4 +84,9 @@ {% else %} <p>The course is empty. Come back in a couple of millifortnights. {% endif %} + {% if not g.is_teacher %} + <nav class=buttons> + <a class=button href='{{ url_for('leave', sident=g.course.semester.ident, cident=g.course.ident) }}'>Leave the course</a> + </nav> + {% endif %} {% endblock %} diff --git a/owl/templates/leave.html b/owl/templates/leave.html new file mode 100644 index 0000000..6ad1719 --- /dev/null +++ b/owl/templates/leave.html @@ -0,0 +1,21 @@ +{% set title = g.course.name %} +{% extends "base.html" %} +{% block body %} + <h2>{{ g.course.name }}</h2> + + {% if error %} + <p class="flash flash-error">{{ error }} + {% else %} + <p>Please confirm that you want to leave this course. + {% endif %} + + <nav class=buttons> + {% if not error %} + <form method="POST" action="?"> + {{ form.csrf_token }} + <input class=danger type=submit value='Leave'> + </form> + {% endif %} + <a class=button href='{{ url_for('course_index', sident=g.course.semester.ident, cident=g.course.ident) }}'>Back to the course</a> + </nav> +{% endblock %} -- GitLab