Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
16bec2f
Resolving issues filtering with no organizations
SamuelVch98 Jul 27, 2024
585ace3
use toast to display errors
SamuelVch98 Jul 29, 2024
0da87f0
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Jul 29, 2024
eada3ca
Fix small issues
SamuelVch98 Jul 30, 2024
7cae218
create unique file for toast and use it
SamuelVch98 Jul 30, 2024
4a633f6
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Jul 30, 2024
99f16d6
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Aug 20, 2024
cf3938d
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Sep 12, 2024
ad8658c
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 1, 2024
1177b11
indicate the type of user to be displayed
SamuelVch98 Oct 9, 2024
9f8b4cc
Fix the bug that prevents you from changing organisation if none was …
SamuelVch98 Oct 9, 2024
ec2bdc1
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 13, 2024
97023c8
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 15, 2024
af8da85
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 15, 2024
4db087a
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 16, 2024
d40c27b
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Oct 17, 2024
7d55991
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Nov 28, 2024
341b73d
Add handsontable to the app
SamuelVch98 Jul 16, 2024
ddd2e01
Finish integrating the handsontable panel
SamuelVch98 Jul 16, 2024
bae06dc
update README
SamuelVch98 Jul 16, 2024
e8784f6
Fix PR request
SamuelVch98 Jul 26, 2024
00c821e
rabse + modifying the code to display several promoters
SamuelVch98 Oct 21, 2024
0b38221
disable cells that contains user's preferences
SamuelVch98 Oct 21, 2024
fe1497f
Creation of an additional button for publishing assignments for teach…
SamuelVch98 Oct 11, 2024
1c4b06e
create a new table to store more easily whether a publication is for …
SamuelVch98 Oct 15, 2024
d36d018
continue to develop the publication just for teachers
SamuelVch98 Oct 15, 2024
bafc49f
display of courses assigned in the researcher and teacher view
SamuelVch98 Oct 16, 2024
7b4fe3a
updating the table to match requirements + displaying assignments on …
SamuelVch98 Nov 29, 2024
cce7b20
Merge branch 'UCL-INGI:main' into main
SamuelVch98 Dec 3, 2024
47cc8f5
merge
SamuelVch98 Dec 4, 2024
0387e73
fix select requested year
SamuelVch98 Dec 4, 2024
36c21ca
improve the display of information on the home page for researchers a…
SamuelVch98 Dec 4, 2024
9255b74
small fix
SamuelVch98 Dec 8, 2024
795ef4a
fix requested changes
SamuelVch98 Dec 10, 2024
f95b056
fix the other category in user, which displays researchers
SamuelVch98 Dec 10, 2024
836ca2e
remove publication status logic in assignment
SamuelVch98 Dec 10, 2024
45a4038
refactor variables and use dict to group variables
SamuelVch98 Dec 11, 2024
14cd30c
change if/elif conditions
SamuelVch98 Dec 11, 2024
5fb6c80
display assistants in the price history table + small fix
SamuelVch98 Dec 11, 2024
4ee136e
Create item in drop down menu to correctly remove filter on column
SamuelVch98 Dec 13, 2024
967c3d6
Redirect to course info when creating a course
SamuelVch98 Dec 13, 2024
991e533
modify the filter function to allow several filters to be applied to …
SamuelVch98 Dec 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import datetime

import jinja2.defaults
from sqlalchemy import and_
from sqlalchemy.orm import joinedload

from auth import auth_bp
from user import user_bp
from course import course_bp
from config import config_bp
from course_preference import course_preference_bp
from assignment import assignment_bp
from db import db, Year, Organization, User, Course, Teacher, Researcher, Evaluation
from db import db, Year, Organization, User, Course, Teacher, Researcher, Evaluation, AssignmentPublished
from decorators import *
from flask import Flask, render_template, session, request
from enums import *
Expand Down Expand Up @@ -53,11 +55,51 @@ def inject_configurations():
# Routes
@app.route('/')
@login_required
def index(): # put application's code here
def index():
current_year = get_current_year()
year = db.session.query(Year).filter_by(year=current_year).first()
user = db.session.query(User).filter_by(email=session['email']).first()
courses_teacher = db.session.query(Course).filter_by(year=current_year).join(Teacher).filter(Teacher.user_id == user.id).all()
return render_template("home.html", user=user, courses=courses_teacher, evaluations=user.evaluations)
teacher = db.session.query(Teacher).filter_by(user_id=user.id).first() if user.is_teacher else None

data = {}

if user.is_teacher:
# Populate teacher-specific data
teacher_courses = db.session.query(Course).join(Teacher).filter(
and_(Teacher.user_id == user.id, Course.year == current_year)
).all()

supervised_researchers = []
for researcher_supervisor in teacher.user.researchers:
researcher = researcher_supervisor.researcher
current_year_courses = [
course for course in researcher.assigned_courses if course.year == current_year
]
researcher.current_assigned_courses = current_year_courses
supervised_researchers.append(researcher)

data.update({
"teacher_courses": teacher_courses,
"supervised_researchers": supervised_researchers
})

# To change if we decide to use session to store roles
elif is_researcher():
researcher = db.session.query(Researcher).filter_by(user_id=user.id).first()
# Populate researcher-specific data
researcher_courses = researcher.assigned_courses
researcher_current_courses = [
course for course in researcher_courses if course.year == current_year
]
evaluations = researcher.user.evaluations

data.update({
"researcher_courses": researcher_courses,
"researcher_current_courses": researcher_current_courses,
"researcher_evaluations": evaluations
})

return render_template("home.html", user=user, data=data)


if __name__ == '__main__':
Expand Down
6 changes: 3 additions & 3 deletions assignment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from decorators import login_required, check_access_level
from db import db, User, Course, PreferenceAssignment, Teacher, Researcher, Organization, \
ResearcherSupervisor, Role, AssignmentDraft, AssignmentPublished
ResearcherSupervisor, Role, AssignmentDraft, AssignmentPublished, Year
from flask import Blueprint, render_template, flash, current_app, url_for, request, make_response, redirect, session, \
Flask, jsonify
from util import get_current_year
Expand Down Expand Up @@ -79,6 +79,7 @@ def publish_assignments():

current_year = get_current_year()
is_draft = data.get('isDraft')
is_teacher_publication = data.get('isTeacherPublication')

try:
# Clear existing assignments for the current year
Expand Down Expand Up @@ -113,8 +114,7 @@ def publish_assignments():
if not is_draft:
assignments_to_add.append(AssignmentPublished(
course_id=course_id, course_year=current_year, researcher_id=researcher_id,
load_q1=load_q1, load_q2=load_q2, position=position, comment=comment
))
load_q1=load_q1, load_q2=load_q2, position=position, comment=comment))
except (ValueError, TypeError) as e:
course = db.session.query(Course).filter_by(id=course_id, year=current_year).first()
return jsonify({
Expand Down
20 changes: 14 additions & 6 deletions course.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqlalchemy import func

from decorators import login_required, check_access_level
from db import db, User, Course, Teacher, Organization, Evaluation, Year, Role
from db import db, User, Course, Teacher, Organization, Evaluation, Year, Role, Researcher, AssignmentPublished
from flask import Blueprint, render_template, flash, url_for, request, make_response, redirect, \
Flask, jsonify, session
from util import get_current_year
Expand Down Expand Up @@ -97,7 +97,8 @@ def add_course():
is_course = db.session.query(Course).filter(Course.code == code,
Course.year == year).first()
if is_course is not None:
return make_response("Course already exists", 500)
flash("Course already exists", "danger")
return redirect(url_for('course.add_course'))

new_course = Course(year=year, code=code, title=title, quadri=quadri, language=language)
# Fetch organizations and add them to the course
Expand All @@ -106,7 +107,7 @@ def add_course():

db.session.add(new_course)
db.session.commit()
return redirect(url_for("course.courses", year=year))
return redirect(url_for("course.course_info", course_id=new_course.id, year=year))
except Exception as e:
db.session.rollback()
raise e
Expand All @@ -117,7 +118,7 @@ def add_course():
@check_access_level(Role.ADMIN)
def courses(year):
courses = db.session.query(Course).filter_by(year=year).all()
return render_template('courses.html', courses=courses, current_year=year)
return render_template('courses.html', courses=courses, year=year)


@course_bp.route('/search_teachers')
Expand All @@ -141,16 +142,23 @@ def search_teachers():
@check_access_level(Role.ADMIN)
def course_info(course_id, year):
course = db.session.query(Course).filter(Course.id == course_id, Course.year == year).first()

if not course:
return make_response("Course not found", 404)

all_years = db.session.query(Course).filter_by(id=course.id).distinct(Course.year).order_by(
Course.year.desc()).all()

query_all_assistants = db.session.query(User, Course.year).join(Researcher).join(
AssignmentPublished).join(Course).filter(
AssignmentPublished.course_id == course_id
).order_by(Course.year.desc()).all()
all_assistants = [{"user": user, "year": year} for user, year in query_all_assistants]

evaluations = db.session.query(Evaluation).filter_by(course_id=course_id).all() or []

return render_template('course_info.html', course=course, all_years=all_years, current_year=year,
evaluations=evaluations)
evaluations=evaluations, all_assistants=all_assistants)


@course_bp.route('/update_course_info', methods=['POST'])
Expand Down Expand Up @@ -276,7 +284,7 @@ def course_evaluation(evaluation_id):

course = evaluation.course
user = db.session.query(User).filter_by(id=session['user_id']).first()
teachers = course.course_teacher
teachers = course.teachers

# Accessible only to the admin, the evaluation creator and the course teachers
if (not user.is_admin) and (user.id != evaluation.user_id) and (user.id not in [teacher.user_id for teacher in teachers]):
Expand Down
15 changes: 14 additions & 1 deletion db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Enum
import json
from sqlalchemy.orm import validates

Expand Down Expand Up @@ -60,13 +61,23 @@ class Course(db.Model):

__table_args__ = (db.UniqueConstraint('id', 'year', name='uq_course_id_year'),)

teachers = db.relationship('Teacher', backref=db.backref('course_teacher', lazy=True))
organizations = db.relationship('Organization',
secondary='course_organization',
back_populates='courses',
primaryjoin="and_(Course.id == CourseOrganization.course_id, Course.year == "
"CourseOrganization.course_year)",
secondaryjoin="Organization.id == CourseOrganization.organization_id")

assistants = db.relationship(
'User',
secondary='assignment_published',
primaryjoin="and_(Course.id == AssignmentPublished.course_id, Course.year == AssignmentPublished.course_year)",
secondaryjoin="and_(Researcher.id == AssignmentPublished.researcher_id, User.id == Researcher.user_id)",
backref='assigned_courses',
viewonly=True
)


class Researcher(db.Model):
__tablename__ = 'researcher'
Expand All @@ -77,6 +88,8 @@ class Researcher(db.Model):
researcher_type = db.Column(db.String(30))

user = db.relationship('User', backref=db.backref('researcher_profile', uselist=False))
assigned_courses = db.relationship('Course', secondary='assignment_published',
backref=db.backref('courses', lazy=True), order_by='Course.year.desc()')


class ResearcherSupervisor(db.Model):
Expand Down Expand Up @@ -104,11 +117,11 @@ class Teacher(db.Model):
)

user = db.relationship('User', backref=db.backref('user_teacher', uselist=False))
course = db.relationship('Course', backref=db.backref('course_teacher', lazy=True))


class PreferenceAssignment(db.Model):
__tablename__ = 'preference_assignment'
rank = db.Column(db.Integer, nullable=False)
id = db.Column(db.Integer, primary_key=True)
rank = db.Column(db.Integer, nullable=False)
course_id = db.Column(db.Integer, nullable=False)
Expand Down
2 changes: 1 addition & 1 deletion enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
TASK = ["Exercise sessions", "Project(s)", "Supervisor of monitoring students", "Q&A sessions/restructuring",
"Corrections/jury/oral", "Creation of new content (new exercises, new project statement, ...)"]
EVALUATION_HOUR = ["< 2h", "2h - 4h", "4h - 6h", "6h - 8h", "> 8h"]
WORKLOAD = ["Very light", "Light", "Reasonable", "High", "Very high"]
WORKLOAD = ["Very light", "Light", "Reasonable", "High", "Very high"]
69 changes: 43 additions & 26 deletions static/scripts/assignment_table.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ fetch('/assignment/load_data')
const columns = getCourseColumns();

let data = fixedRows.concat(userRows);

const nbrLines = data.length - 1;
const nbrCols = columns.length - 1;

Expand Down Expand Up @@ -203,7 +202,23 @@ fetch('/assignment/load_data')
contextMenu: ['commentsAddEdit', 'commentsRemove', 'hidden_columns_hide', 'hidden_rows_hide', 'hidden_columns_show', 'hidden_rows_show'],
comments: true,
filters: true,
dropdownMenu: ['filter_by_value', 'filter_action_bar', 'undo'],
dropdownMenu: {
items: {
'filter_by_value': {},
'filter_action_bar': {},
'---------': {},
'clear_all_filters': {
name: 'Reset filters',
callback: function () {
const filtersPlugin = this.getPlugin('filters');
if (filtersPlugin) {
filtersPlugin.clearConditions();
filtersPlugin.filter();
}
},
},
},
},
className: 'controlsQuickFilter htCenter htMiddle',
colHeaders: allHeaders,
columns: columns,
Expand Down Expand Up @@ -336,32 +351,29 @@ fetch('/assignment/load_data')
],
beforeFilter(conditionsStack) {
const filtersPlugin = this.getPlugin('filters');
//Get the user data without the fixed rows
// Get the user data without the fixed rows
const tab = this.getData().slice(lenFixedRowsText);

let values = [];
const filteredResults = [];
//Get the number of the column to filter

if (conditionsStack && conditionsStack.length > 0) {
const col = conditionsStack[0].column;

if (conditionsStack && conditionsStack.length > 0) {
for (let i = 0; i < conditionsStack.length; i++) {
//Get the matching values to filter
values = conditionsStack[i].conditions[0].args.flat();

//Verify if the row value for the specific column is in the filter
for (const row of tab) {
if (values.includes(row[col])) {
//Push id to the filteredResults array
filteredResults.push(row[0]);
}
}
}
// Start with all row IDs as potentially valid
let filteredResults = tab.map(row => row[0]); // Array of IDs

// Process each condition and refine the filteredResults
for (const condition of conditionsStack) {
const col = condition.column; // Column to filter
const values = condition.conditions.flatMap(c => c.args).flat(); // Values to match

// Filter rows that match the current condition
filteredResults = filteredResults.filter(id =>
tab.some(row => row[0] === id && values.includes(row[col]))
);

// Exit early if no rows match any condition
if (filteredResults.length === 0) break;
}

// Apply the final filtered IDs to the Handsontable filter
filtersPlugin.clearConditions();
//Create a new condition to filter the data based on the id
filtersPlugin.addCondition(0, 'by_value', [filteredResults]);
}
},
Expand Down Expand Up @@ -398,7 +410,7 @@ fetch('/assignment/load_data')
toastNotification.show();
});

async function saveAssignment(isDraft = false) {
async function saveAssignment(isDraft = false, isTeacherPublication = false) {
const slicedData = data.slice(lenFixedRowsText);
const savedData = [];
const commentsPlugin = table.getPlugin('comments');
Expand Down Expand Up @@ -436,7 +448,8 @@ fetch('/assignment/load_data')

const tableData = {
data: savedData,
isDraft: isDraft
isDraft: isDraft,
isTeacherPublication: isTeacherPublication,
};

try {
Expand Down Expand Up @@ -479,7 +492,11 @@ fetch('/assignment/load_data')
toastNotification.show();
});

$('#button-publish-assignments').click(async function () {
$('#button-publish-teachers').click(async function () {
await saveAssignment(false, true);
});

$('#button-publish-everyone').click(async function () {
await saveAssignment();
});
});
Expand Down
15 changes: 12 additions & 3 deletions templates/assignment.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,18 @@ <h2 class="accordion-header" id="controlsHeading">
</button>
</div>
</div>
<button type="button" class="btn btn-outline-dark btn-sm"
id="button-publish-assignments">Publish assignments
</button>
<div class="row g-2">
<div class="col-6">
<button type="button" class="btn btn-outline-dark btn-sm w-100"
id="button-publish-teachers" data-publication="teacher">Publish for teachers
</button>
</div>
<div class="col-6">
<button type="button" class="btn btn-outline-dark btn-sm w-100"
id="button-publish-everyone" data-publication="everyone">Publish for everyone
</button>
</div>
</div>
</div>
</div>
</div>
Expand Down
Loading