From a7175987b4961f5ef2bcad53bf89ccac736aa595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 16:55:43 +0200 Subject: [PATCH 01/12] refactor: rooms --- candle/__init__.py | 6 +- candle/api/api.py | 3 +- candle/entities/room/room.py | 57 ------------------- .../room/templates/room/list_rooms.html | 22 ------- candle/models.py | 33 ----------- candle/panel/templates/panel/panel.html | 4 +- candle/{entities/room => rooms}/__init__.py | 0 candle/rooms/frontend.py | 23 ++++++++ candle/rooms/models.py | 35 ++++++++++++ candle/rooms/search.py | 35 ++++++++++++ candle/rooms/templates/rooms/listing.html | 22 +++++++ candle/search/search.py | 3 +- candle/timetable/blueprints/readonly.py | 4 +- 13 files changed, 127 insertions(+), 120 deletions(-) delete mode 100644 candle/entities/room/room.py delete mode 100644 candle/entities/room/templates/room/list_rooms.html rename candle/{entities/room => rooms}/__init__.py (100%) create mode 100644 candle/rooms/frontend.py create mode 100644 candle/rooms/models.py create mode 100644 candle/rooms/search.py create mode 100644 candle/rooms/templates/rooms/listing.html diff --git a/candle/__init__.py b/candle/__init__.py index d28e2df..a83c44e 100644 --- a/candle/__init__.py +++ b/candle/__init__.py @@ -25,6 +25,7 @@ def create_app(config_class=Config): app = Flask(__name__) + app.url_map.strict_slashes = False app.config.from_object(config_class) # Use middleware for 2016 path-prefix. Used for testing. Source: https://dlukes.github.io/flask-wsgi-url-prefix.html#mwe @@ -45,20 +46,19 @@ def register_blueprints(app): from candle.auth.auth import auth from candle.timetable.timetable import timetable from candle.my_timetable.my_timetable import my_timetable - from candle.entities.room.room import room from candle.entities.student_group.student_group import student_group from candle.entities.teacher.teacher import teacher from candle.entities.subject.subject import subject from candle.panel.panel import panel from candle.search.search import search from candle.errors.errors import errors + from candle.rooms.frontend import rooms app.register_blueprint(main) app.register_blueprint(api) app.register_blueprint(auth) app.register_blueprint(timetable) app.register_blueprint(my_timetable) - app.register_blueprint(room) app.register_blueprint(student_group) app.register_blueprint(teacher) app.register_blueprint(subject) @@ -66,6 +66,8 @@ def register_blueprints(app): app.register_blueprint(search) app.register_blueprint(errors) + app.register_blueprint(rooms) + def init_extensions(app): db.init_app(app) diff --git a/candle/api/api.py b/candle/api/api.py index b35e85e..4b249da 100644 --- a/candle/api/api.py +++ b/candle/api/api.py @@ -5,7 +5,8 @@ from flask import Blueprint, request, jsonify -from candle.models import Teacher, Room, StudentGroup, Subject, Lesson, teacher_lessons +from candle.models import Teacher, StudentGroup, Subject, Lesson, teacher_lessons +from candle.rooms.models import Room api = Blueprint('api', __name__) diff --git a/candle/entities/room/room.py b/candle/entities/room/room.py deleted file mode 100644 index efd53b0..0000000 --- a/candle/entities/room/room.py +++ /dev/null @@ -1,57 +0,0 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - -from flask import render_template, Blueprint -from candle.models import Room -from typing import Dict - -from candle.timetable.blueprints.readonly import register_timetable_routes - -room = Blueprint('room', - __name__, - template_folder='templates', url_prefix="/miestnosti") - - -@room.route('') -def list_rooms(): - """Show all rooms.""" - rooms_list = Room.query.order_by(Room.name).all() - rooms_dict = get_rooms_sorted_by_dashes(rooms_list) # rooms are in the dictionary sorted by prefix - title = "Rozvrhy miestností" - return render_template('room/list_rooms.html', rooms_dict=rooms_dict, title=title, - web_header=title) - - -def get_room(room_url_id: str) -> Room: - if room_url_id.isnumeric(): - return Room.query.filter_by(id_=room_url_id).first_or_404() - return Room.query.filter_by(name=room_url_id).first_or_404() - - -register_timetable_routes(room, get_room) - - -def get_rooms_sorted_by_dashes(rooms_lst) -> Dict: - """ - Return OrderedDict that contains rooms sorted by categories. - - The key in the OrderedDict is always the room prefix and the values are rooms (objects of model Room) - (For example'F1-108' has prefix 'F1'). - - :parameter rooms_lst: The list of the Room model objects. - :return OrderedDict (dict of str: list), where key is the prefix and the value is a list of Rooms. - """ - d = {} - for room in rooms_lst: - url_id = room.url_id - if url_id == " ": # we have one room with empty name (" ") - continue - prefix = room.prefix - - # insert data into dictionary: - if prefix not in d: - d[prefix] = [] - d[prefix].append(room) - return d diff --git a/candle/entities/room/templates/room/list_rooms.html b/candle/entities/room/templates/room/list_rooms.html deleted file mode 100644 index 864ced9..0000000 --- a/candle/entities/room/templates/room/list_rooms.html +++ /dev/null @@ -1,22 +0,0 @@ -{# Print out all rooms #} - -{% extends "main/base.html" %} - -{% block obsah_in %} - {{ super() }} -

- {% for room_prefix in rooms_dict %} - {{ room_prefix }} - {% endfor %} -

- - {# For each categeroy:#} - {% for room_prefix in rooms_dict %} -

{{ room_prefix }}

- - {% endfor %} -{% endblock %} diff --git a/candle/models.py b/candle/models.py index 4ac09e8..20a9b01 100644 --- a/candle/models.py +++ b/candle/models.py @@ -35,33 +35,6 @@ def lessons(self): raise NotImplementedError() -class Room(SchoolTimetable): - id_ = db.Column('id', db.Integer, primary_key=True) - name = db.Column('name', db.String(30), nullable=False) - room_type_id = db.Column(db.Integer, db.ForeignKey('room_type.id'), nullable=False) - capacity = db.Column(db.Integer, nullable=False) - lessons = db.relationship('Lesson', backref='room', - lazy='dynamic') # 'lazy dynamic' allows us to work with lessons attribute like with query ( we can run order_by, etc) - - @property - def prefix(self): - # xMieRez is a special case: - if 'xMieRez' in self.name: - return "Ostatné" - - first_dash_position = self.name.find('-') - if first_dash_position == -1: # name doesn't contain '-' - return self.name - return self.name[0 : first_dash_position] - - def __repr__(self): - return "" % self.name - - @property - def timetable_name(self) -> str: - return self.name - - teacher_lessons = db.Table('teacher_lessons', db.Column('id', db.Integer, primary_key=True), db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')), @@ -227,12 +200,6 @@ def timetable_name(self) -> str: return self.name -class RoomType(db.Model): - id_ = db.Column('id', db.Integer, primary_key=True) - name = db.Column(db.String(30), nullable=False) - code = db.Column(db.String(1), nullable=False) - - user_timetable_lessons = db.Table('user_timetable_lessons', db.Column('id', db.Integer(), primary_key=True), db.Column('user_timetable_id', db.Integer, db.ForeignKey('user_timetable.id')), diff --git a/candle/panel/templates/panel/panel.html b/candle/panel/templates/panel/panel.html index 0a6a4df..d1556ee 100644 --- a/candle/panel/templates/panel/panel.html +++ b/candle/panel/templates/panel/panel.html @@ -29,7 +29,7 @@
{% include('search/room_search.html') %}
@@ -46,4 +46,4 @@ - \ No newline at end of file + diff --git a/candle/entities/room/__init__.py b/candle/rooms/__init__.py similarity index 100% rename from candle/entities/room/__init__.py rename to candle/rooms/__init__.py diff --git a/candle/rooms/frontend.py b/candle/rooms/frontend.py new file mode 100644 index 0000000..e20d4f9 --- /dev/null +++ b/candle/rooms/frontend.py @@ -0,0 +1,23 @@ +from flask import Blueprint, render_template, request + +from candle.rooms.models import Room +from candle.rooms.search import search_rooms, get_rooms_sorted_by_dashes +from candle.timetable.blueprints.readonly import register_timetable_routes + +rooms = Blueprint('rooms', __name__, url_prefix="/rooms", template_folder="templates") + + +@rooms.route("/") +def listing(): + room_list = search_rooms(request.args.get("q")) + rooms_dict = get_rooms_sorted_by_dashes(room_list) # rooms are in the dictionary sorted by prefix + title = "Rozvrhy miestností" + return render_template('rooms/listing.html', rooms_dict=rooms_dict, title=title, + web_header=title) + + +def get_room(room_url_id: str) -> Room: + return Room.query.filter((Room.id_ == room_url_id) | (Room.name == room_url_id)).first_or_404() + + +register_timetable_routes(rooms, get_room) diff --git a/candle/rooms/models.py b/candle/rooms/models.py new file mode 100644 index 0000000..952eb95 --- /dev/null +++ b/candle/rooms/models.py @@ -0,0 +1,35 @@ +from candle import db +from candle.models import SchoolTimetable + + +class Room(SchoolTimetable): + id_ = db.Column('id', db.Integer, primary_key=True) + name = db.Column('name', db.String(30), nullable=False) + room_type_id = db.Column(db.Integer, db.ForeignKey('room_type.id'), nullable=False) + capacity = db.Column(db.Integer, nullable=False) + lessons = db.relationship('Lesson', backref='room', + lazy='dynamic') # 'lazy dynamic' allows us to work with lessons attribute like with query ( we can run order_by, etc) + + @property + def prefix(self): + # xMieRez is a special case: + if 'xMieRez' in self.name: + return "Ostatné" + + first_dash_position = self.name.find('-') + if first_dash_position == -1: # name doesn't contain '-' + return self.name + return self.name[0 : first_dash_position] + + def __repr__(self): + return "" % self.name + + @property + def timetable_name(self) -> str: + return self.name + + +class RoomType(db.Model): + id_ = db.Column('id', db.Integer, primary_key=True) + name = db.Column(db.String(30), nullable=False) + code = db.Column(db.String(1), nullable=False) diff --git a/candle/rooms/search.py b/candle/rooms/search.py new file mode 100644 index 0000000..0cf125e --- /dev/null +++ b/candle/rooms/search.py @@ -0,0 +1,35 @@ +from typing import Iterable + +from candle.rooms.models import Room +from flask_sqlalchemy.query import Query + + +def search_rooms(query: str|None) -> Query: + rooms = Room.query.order_by(Room.name) + if query: + rooms = rooms.filter(Room.name.ilike(f"%{query}%")) + return rooms + + +def get_rooms_sorted_by_dashes(rooms: Iterable[Room]) -> dict[str, list[Room]]: + """ + Return OrderedDict that contains rooms sorted by categories. + + The key in the OrderedDict is always the room prefix and the values are rooms (objects of model Room) + (For example'F1-108' has prefix 'F1'). + + :parameter rooms: The list of the Room model objects. + :return OrderedDict (dict of str: list), where key is the prefix and the value is a list of Rooms. + """ + d = {} + for room in rooms: + url_id = room.url_id + if url_id == " ": # we have one room with empty name (" ") + continue + prefix = room.prefix + + # insert data into dictionary: + if prefix not in d: + d[prefix] = [] + d[prefix].append(room) + return d diff --git a/candle/rooms/templates/rooms/listing.html b/candle/rooms/templates/rooms/listing.html new file mode 100644 index 0000000..5336015 --- /dev/null +++ b/candle/rooms/templates/rooms/listing.html @@ -0,0 +1,22 @@ +{# Print out all rooms #} + +{% extends "main/base.html" %} + +{% block obsah_in %} + {{ super() }} + +

+ {% for room_prefix in rooms_dict %} + {{ room_prefix }} + {% endfor %} +

+ + {% for room_prefix in rooms_dict %} +

{{ room_prefix }}

+
    + {% for room in rooms_dict[room_prefix] -%} +
  • {{ room.name }}
  • + {%- endfor %} +
+ {% endfor %} +{% endblock %} diff --git a/candle/search/search.py b/candle/search/search.py index 7acc07b..e14114e 100644 --- a/candle/search/search.py +++ b/candle/search/search.py @@ -8,7 +8,8 @@ from flask import Blueprint, request, jsonify, render_template from flask_login import current_user -from candle.models import Teacher, Room, StudentGroup, Lesson, teacher_lessons, Subject, UserTimetable +from candle.models import Teacher, StudentGroup, Lesson, teacher_lessons, Subject, UserTimetable +from candle.rooms.models import Room search = Blueprint('search', __name__, diff --git a/candle/timetable/blueprints/readonly.py b/candle/timetable/blueprints/readonly.py index d111bb7..0670962 100644 --- a/candle/timetable/blueprints/readonly.py +++ b/candle/timetable/blueprints/readonly.py @@ -10,7 +10,7 @@ def register_timetable_routes(bp: Blueprint, getter: Callable[[str], SchoolTimetable]): - @bp.route('/') + @bp.route('//') def show_timetable(slug): """Shows a timetable.""" obj = getter(slug) @@ -27,7 +27,7 @@ def export_timetable(slug, format): return export_timetable_as(format, obj.lessons) @login_required - @bp.route("//duplicate", methods=['POST']) + @bp.route("//duplicate/", methods=['POST']) def duplicate(slug): new_timetable_id = duplicate_timetable(getter(slug)) return jsonify({'next_url': url_for("my_timetable.show_timetable", id_=new_timetable_id)}) From b3007701392a365036df44a4d443dae9947c9020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 17:20:53 +0200 Subject: [PATCH 02/12] refactor: groups --- candle/api/api.py | 3 +- .../entities/student_group/student_group.py | 46 ------------------- .../student_group => groups}/__init__.py | 0 candle/groups/frontend.py | 37 +++++++++++++++ candle/groups/models.py | 20 ++++++++ candle/groups/search.py | 10 ++++ .../templates/groups/listing.html} | 2 +- candle/models.py | 19 -------- candle/panel/templates/panel/panel.html | 2 +- candle/search/search.py | 3 +- .../timetable/templates/timetable/list.html | 2 +- .../templates/timetable/timetable_cell.html | 2 +- 12 files changed, 75 insertions(+), 71 deletions(-) delete mode 100644 candle/entities/student_group/student_group.py rename candle/{entities/student_group => groups}/__init__.py (100%) create mode 100644 candle/groups/frontend.py create mode 100644 candle/groups/models.py create mode 100644 candle/groups/search.py rename candle/{entities/student_group/templates/student_group/list_student_groups.html => groups/templates/groups/listing.html} (82%) diff --git a/candle/api/api.py b/candle/api/api.py index 4b249da..4c54fe8 100644 --- a/candle/api/api.py +++ b/candle/api/api.py @@ -5,7 +5,8 @@ from flask import Blueprint, request, jsonify -from candle.models import Teacher, StudentGroup, Subject, Lesson, teacher_lessons +from candle.models import Teacher, Subject, Lesson, teacher_lessons +from candle.groups.models import StudentGroup from candle.rooms.models import Room api = Blueprint('api', __name__) diff --git a/candle/entities/student_group/student_group.py b/candle/entities/student_group/student_group.py deleted file mode 100644 index a26af90..0000000 --- a/candle/entities/student_group/student_group.py +++ /dev/null @@ -1,46 +0,0 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - -from typing import Dict -from flask import render_template, Blueprint - -from candle.models import StudentGroup -from candle.timetable.blueprints.readonly import register_timetable_routes - -student_group = Blueprint('student_group', - __name__, - template_folder='templates', url_prefix='/kruzky') - - -@student_group.route('') -def list_student_groups(): - """Show all student groups.""" - groups_list = StudentGroup.query.order_by(StudentGroup.name).all() - student_groups_dict = get_student_groups_sorted_by_first_letter(groups_list) - title = "Rozvrhy krúžkov" - return render_template('student_group/list_student_groups.html', student_groups_dict=student_groups_dict, - title=title, web_header=title) - - -def get_group(group_url_id: str) -> StudentGroup: - if group_url_id.isnumeric(): - student_group = StudentGroup.query.filter_by(id_=group_url_id).first_or_404() - else: - student_group = StudentGroup.query.filter_by(name=group_url_id).first_or_404() - return student_group - - -register_timetable_routes(student_group, get_group) - - -def get_student_groups_sorted_by_first_letter(student_groups) -> Dict: - """Return student-groups in a dictionary sorted by the first letter.""" - result_dict = {} - for group in student_groups: - first_letter = group.name[0] - if first_letter not in result_dict: - result_dict[first_letter] = [] - result_dict[first_letter].append(group) - return result_dict diff --git a/candle/entities/student_group/__init__.py b/candle/groups/__init__.py similarity index 100% rename from candle/entities/student_group/__init__.py rename to candle/groups/__init__.py diff --git a/candle/groups/frontend.py b/candle/groups/frontend.py new file mode 100644 index 0000000..0f05d50 --- /dev/null +++ b/candle/groups/frontend.py @@ -0,0 +1,37 @@ + +from typing import Dict +from flask import render_template, Blueprint, request + +from candle.groups.models import StudentGroup +from candle.groups.search import search_groups +from candle.timetable.blueprints.readonly import register_timetable_routes + +groups = Blueprint('groups', __name__, template_folder='templates', url_prefix='/groups') + + +@groups.route('/') +def listing(): + """Show all student groups.""" + groups_list = search_groups(request.args.get("q")) + student_groups_dict = get_student_groups_sorted_by_first_letter(groups_list) + title = "Rozvrhy krúžkov" + return render_template('groups/listing.html', student_groups_dict=student_groups_dict, + title=title, web_header=title) + + +def get_group(group_url_id: str) -> StudentGroup: + return StudentGroup.query.filter((StudentGroup.id_==group_url_id) | (StudentGroup.name==group_url_id)).first_or_404() + + +register_timetable_routes(groups, get_group) + + +def get_student_groups_sorted_by_first_letter(student_groups) -> Dict: + """Return student-groups in a dictionary sorted by the first letter.""" + result_dict = {} + for group in student_groups: + first_letter = group.name[0] + if first_letter not in result_dict: + result_dict[first_letter] = [] + result_dict[first_letter].append(group) + return result_dict diff --git a/candle/groups/models.py b/candle/groups/models.py new file mode 100644 index 0000000..5af05b8 --- /dev/null +++ b/candle/groups/models.py @@ -0,0 +1,20 @@ +from candle import db +from candle.models import SchoolTimetable + +student_group_lessons = db.Table('student_group_lessons', + db.Column('student_group_id', db.Integer, db.ForeignKey('student_group.id')), + db.Column('lesson_id', db.Integer, db.ForeignKey('lesson.id'))) + + +class StudentGroup(SchoolTimetable): + id_ = db.Column('id', db.Integer, primary_key=True) + name = db.Column(db.String(30), nullable=False) + lessons = db.relationship('Lesson', secondary=student_group_lessons, lazy='dynamic') + + @property + def timetable_name(self) -> str: + return f"Rozvh krúžku {self.name}" + + @property + def timetable_short_name(self) -> str: + return self.name diff --git a/candle/groups/search.py b/candle/groups/search.py new file mode 100644 index 0000000..6937560 --- /dev/null +++ b/candle/groups/search.py @@ -0,0 +1,10 @@ +from candle.groups.models import StudentGroup +from flask_sqlalchemy.query import Query + + +def search_groups(query: str|None) -> Query: + groups = StudentGroup.query.order_by(StudentGroup.name) + if query: + groups = groups.filter(StudentGroup.name.ilike(f"%{query}%")) + return groups + diff --git a/candle/entities/student_group/templates/student_group/list_student_groups.html b/candle/groups/templates/groups/listing.html similarity index 82% rename from candle/entities/student_group/templates/student_group/list_student_groups.html rename to candle/groups/templates/groups/listing.html index e8a254e..b0818b0 100644 --- a/candle/entities/student_group/templates/student_group/list_student_groups.html +++ b/candle/groups/templates/groups/listing.html @@ -15,7 +15,7 @@

{{ category }}

{% endfor %} diff --git a/candle/models.py b/candle/models.py index 20a9b01..721ba9c 100644 --- a/candle/models.py +++ b/candle/models.py @@ -79,25 +79,6 @@ def timetable_short_name(self) -> str: return self.short_name -student_group_lessons = db.Table('student_group_lessons', - db.Column('student_group_id', db.Integer, db.ForeignKey('student_group.id')), - db.Column('lesson_id', db.Integer, db.ForeignKey('lesson.id'))) - - -class StudentGroup(SchoolTimetable): - id_ = db.Column('id', db.Integer, primary_key=True) - name = db.Column(db.String(30), nullable=False) - lessons = db.relationship('Lesson', secondary=student_group_lessons, lazy='dynamic') - - @property - def timetable_name(self) -> str: - return f"Rozvh krúžku {self.name}" - - @property - def timetable_short_name(self) -> str: - return self.name - - class Lesson(db.Model): id_ = db.Column('id', db.Integer, primary_key=True) day = db.Column(db.Integer, nullable=False) diff --git a/candle/panel/templates/panel/panel.html b/candle/panel/templates/panel/panel.html index d1556ee..98c4a48 100644 --- a/candle/panel/templates/panel/panel.html +++ b/candle/panel/templates/panel/panel.html @@ -41,7 +41,7 @@
{% include('search/group_search.html') %}
diff --git a/candle/search/search.py b/candle/search/search.py index e14114e..bcdbb35 100644 --- a/candle/search/search.py +++ b/candle/search/search.py @@ -8,7 +8,8 @@ from flask import Blueprint, request, jsonify, render_template from flask_login import current_user -from candle.models import Teacher, StudentGroup, Lesson, teacher_lessons, Subject, UserTimetable +from candle.models import Teacher, Lesson, teacher_lessons, Subject, UserTimetable +from candle.groups.models import StudentGroup from candle.rooms.models import Room search = Blueprint('search', diff --git a/candle/timetable/templates/timetable/list.html b/candle/timetable/templates/timetable/list.html index fe91489..8fefaaf 100644 --- a/candle/timetable/templates/timetable/list.html +++ b/candle/timetable/templates/timetable/list.html @@ -8,7 +8,7 @@ {{ lesson.day_abbreviated }} {{ lesson.start_formatted }} {{ lesson.end_formatted }} - {{ lesson.room.name }} + {{ lesson.room.name }} {{ lesson.type }} {{ lesson.subject.short_code }} diff --git a/candle/timetable/templates/timetable/timetable_cell.html b/candle/timetable/templates/timetable/timetable_cell.html index 70a4385..d955045 100644 --- a/candle/timetable/templates/timetable/timetable_cell.html +++ b/candle/timetable/templates/timetable/timetable_cell.html @@ -40,7 +40,7 @@
{{ placed_lesson.lesson.type.code| upper()}} From 08de9a9823c7df24e627e8f794b0db3236ac0f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 17:35:24 +0200 Subject: [PATCH 03/12] refactor: subjects --- candle/api/api.py | 3 +- candle/entities/subject/subject.py | 33 ------------------- candle/models.py | 27 --------------- candle/my_timetable/my_timetable.py | 2 +- candle/search/search.py | 3 +- .../subject => subjects}/__init__.py | 0 candle/subjects/frontend.py | 25 ++++++++++++++ candle/subjects/models.py | 29 ++++++++++++++++ candle/subjects/search.py | 10 ++++++ .../templates/subjects/listing.html} | 2 +- candle/timetable/export.py | 17 +++++----- candle/timetable/placed_lesson.py | 2 +- candle/timetable/render.py | 3 +- candle/timetable/timetable.py | 6 ++-- 14 files changed, 85 insertions(+), 77 deletions(-) delete mode 100644 candle/entities/subject/subject.py rename candle/{entities/subject => subjects}/__init__.py (100%) create mode 100644 candle/subjects/frontend.py create mode 100644 candle/subjects/models.py create mode 100644 candle/subjects/search.py rename candle/{entities/subject/templates/subject/list.html => subjects/templates/subjects/listing.html} (53%) diff --git a/candle/api/api.py b/candle/api/api.py index 4c54fe8..a161675 100644 --- a/candle/api/api.py +++ b/candle/api/api.py @@ -5,7 +5,8 @@ from flask import Blueprint, request, jsonify -from candle.models import Teacher, Subject, Lesson, teacher_lessons +from candle.models import Teacher, Lesson, teacher_lessons +from candle.subjects.models import Subject from candle.groups.models import StudentGroup from candle.rooms.models import Room diff --git a/candle/entities/subject/subject.py b/candle/entities/subject/subject.py deleted file mode 100644 index ed9f51c..0000000 --- a/candle/entities/subject/subject.py +++ /dev/null @@ -1,33 +0,0 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - -from flask import Blueprint, render_template - -from typing import Dict -from candle.models import Teacher, Subject -from candle.entities.helpers import string_starts_with_ch -import unidecode - -from candle.timetable.blueprints.readonly import register_timetable_routes - -subject = Blueprint('subject', - __name__, - template_folder='templates', url_prefix='/predmety') - - -@subject.route('') -def list_teachers(): - """Show all teachers in the list.""" - subject_list = Subject.query.order_by(Subject.short_code).all() - title = "Zoznam predmetov" - return render_template('subject/list.html', subjects=subject_list, title=title, - web_header=title) - - -def get_subject(slug: str) -> Subject: - return Subject.query.filter(Subject.short_code == slug).first_or_404() - - -register_timetable_routes(subject, get_subject) diff --git a/candle/models.py b/candle/models.py index 721ba9c..4325109 100644 --- a/candle/models.py +++ b/candle/models.py @@ -154,33 +154,6 @@ def __repr__(self): return self.name -class Subject(SchoolTimetable): - id_ = db.Column('id', db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False) - code = db.Column(db.String(50), nullable=False) - short_code = db.Column(db.String(20), nullable=False) - credit_value = db.Column(db.Integer, nullable=False) - rozsah = db.Column(db.String(30), nullable=True) - external_id = db.Column(db.String(30), nullable=True) - lessons = db.relationship('Lesson', backref='subject', lazy=True) - - def __repr__(self): - return f"Subject(id:'{self.id_}', name:'{self.name}' )" - - def to_dict(self) -> dict: - """ - Returns the Subject as dict for JSON rendering. - """ - return { - "name": self.name, - "shortcode": self.short_code, - } - - @property - def timetable_name(self) -> str: - return self.name - - user_timetable_lessons = db.Table('user_timetable_lessons', db.Column('id', db.Integer(), primary_key=True), db.Column('user_timetable_id', db.Integer, db.ForeignKey('user_timetable.id')), diff --git a/candle/my_timetable/my_timetable.py b/candle/my_timetable/my_timetable.py index 6ae6551..c602b34 100644 --- a/candle/my_timetable/my_timetable.py +++ b/candle/my_timetable/my_timetable.py @@ -12,9 +12,9 @@ from candle.models import ( UserTimetable, Lesson, - Subject, user_timetable_lessons, ) +from candle.subjects.models import Subject from candle.timetable.export import export_timetable_as from candle.timetable.layout import Layout, TooManyColumnsError from candle.timetable.render import render_timetable diff --git a/candle/search/search.py b/candle/search/search.py index bcdbb35..37cc311 100644 --- a/candle/search/search.py +++ b/candle/search/search.py @@ -8,7 +8,8 @@ from flask import Blueprint, request, jsonify, render_template from flask_login import current_user -from candle.models import Teacher, Lesson, teacher_lessons, Subject, UserTimetable +from candle.models import Teacher, Lesson, teacher_lessons, UserTimetable +from candle.subjects.models import Subject from candle.groups.models import StudentGroup from candle.rooms.models import Room diff --git a/candle/entities/subject/__init__.py b/candle/subjects/__init__.py similarity index 100% rename from candle/entities/subject/__init__.py rename to candle/subjects/__init__.py diff --git a/candle/subjects/frontend.py b/candle/subjects/frontend.py new file mode 100644 index 0000000..4c6ee92 --- /dev/null +++ b/candle/subjects/frontend.py @@ -0,0 +1,25 @@ +from flask import Blueprint, render_template, request + +from candle.subjects.models import Subject +from candle.subjects.search import search_subjects +from candle.timetable.blueprints.readonly import register_timetable_routes + +subjects = Blueprint('subjects', + __name__, + template_folder='templates', url_prefix='/subjects') + + +@subjects.route("/") +def listing(): + """Show all teachers in the list.""" + subject_list = search_subjects(request.args.get("q")) + title = "Zoznam predmetov" + return render_template('subjects/listing.html', subjects=subject_list, title=title, + web_header=title) + + +def get_subject(slug: str) -> Subject: + return Subject.query.filter(Subject.short_code == slug).first_or_404() + + +register_timetable_routes(subjects, get_subject) diff --git a/candle/subjects/models.py b/candle/subjects/models.py new file mode 100644 index 0000000..36aba9a --- /dev/null +++ b/candle/subjects/models.py @@ -0,0 +1,29 @@ +from candle import db +from candle.models import SchoolTimetable + + +class Subject(SchoolTimetable): + id_ = db.Column('id', db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + code = db.Column(db.String(50), nullable=False) + short_code = db.Column(db.String(20), nullable=False) + credit_value = db.Column(db.Integer, nullable=False) + rozsah = db.Column(db.String(30), nullable=True) + external_id = db.Column(db.String(30), nullable=True) + lessons = db.relationship('Lesson', backref='subject', lazy='dynamic') + + def __repr__(self): + return f"Subject(id:'{self.id_}', name:'{self.name}' )" + + def to_dict(self) -> dict: + """ + Returns the Subject as dict for JSON rendering. + """ + return { + "name": self.name, + "shortcode": self.short_code, + } + + @property + def timetable_name(self) -> str: + return self.name diff --git a/candle/subjects/search.py b/candle/subjects/search.py new file mode 100644 index 0000000..22894f1 --- /dev/null +++ b/candle/subjects/search.py @@ -0,0 +1,10 @@ +from flask_sqlalchemy.query import Query + +from candle.subjects.models import Subject + + +def search_subjects(query: str|None) -> Query: + subjects = Subject.query.order_by(Subject.short_code) + if query: + subjects = subjects.filter(Subject.name.ilike(f"%{query}%") | Subject.short_code.ilike(f"%{query}%")) + return subjects diff --git a/candle/entities/subject/templates/subject/list.html b/candle/subjects/templates/subjects/listing.html similarity index 53% rename from candle/entities/subject/templates/subject/list.html rename to candle/subjects/templates/subjects/listing.html index 7b3b70c..5bc5621 100644 --- a/candle/entities/subject/templates/subject/list.html +++ b/candle/subjects/templates/subjects/listing.html @@ -4,7 +4,7 @@ {{ super() }} {% endblock %} diff --git a/candle/timetable/export.py b/candle/timetable/export.py index f7ac99d..a2feea0 100644 --- a/candle/timetable/export.py +++ b/candle/timetable/export.py @@ -8,7 +8,8 @@ from flask import make_response, abort, current_app from icalendar import Calendar, Event -from candle.models import Subject, Lesson +from candle.models import Lesson +from candle.subjects.models import Subject from candle.timetable.layout import Layout @@ -39,7 +40,7 @@ def _to_csv(layout: Layout): for lesson in layout.get_lessons(): w.writerow([ lesson.day_abbreviated, lesson.start_formatted, lesson.end_formatted, lesson.room.name, - lesson.type, lesson.subject.short_code, lesson.subject.name, + lesson.type, lesson.subjects.short_code, lesson.subjects.name, lesson.get_teachers_formatted(), lesson.get_note(), ]) @@ -64,7 +65,7 @@ def _to_list(layout: Layout): subjects = set() for lesson in layout.get_lessons(): - subjects.add(lesson.subject.short_code) + subjects.add(lesson.subjects.short_code) response = make_response("\n".join(subjects)) response.mimetype = "text/plain" @@ -76,8 +77,8 @@ def _to_txt(layout: Layout): for lesson in layout.get_lessons(): row = [lesson.day_abbreviated, f"{lesson.start_formatted} - {lesson.end_formatted}", - f"({lesson.rowspan} v.hod.)", lesson.room.name, lesson.type.name, lesson.subject.short_code, - lesson.subject.name, lesson.get_note(), lesson.get_teachers_formatted()] + f"({lesson.rowspan} v.hod.)", lesson.room.name, lesson.type.name, lesson.subjects.short_code, + lesson.subjects.name, lesson.get_note(), lesson.get_teachers_formatted()] rows.append("\t".join(row)) response = make_response("\n".join(rows)) @@ -106,7 +107,7 @@ def _to_ics(layout: Layout): event.add("dtstamp", start) event.add("dtstart", start) event.add("dtend", end) - event.add("summary", lesson.subject.name) + event.add("summary", lesson.subjects.name) event.add("location", lesson.room.name) description = [lesson.type.name] @@ -134,8 +135,8 @@ def _to_xml(layout: Layout): elem = ElementTree.SubElement(lesson_elem, "room") elem.text = lesson.room.name - elem = ElementTree.SubElement(lesson_elem, "subject", shortcode=lesson.subject.short_code) - elem.text = lesson.subject.name + elem = ElementTree.SubElement(lesson_elem, "subject", shortcode=lesson.subjects.short_code) + elem.text = lesson.subjects.name elem = ElementTree.SubElement(lesson_elem, "day") elem.text = lesson.day_abbreviated diff --git a/candle/timetable/placed_lesson.py b/candle/timetable/placed_lesson.py index fc57d3d..e2a93b0 100644 --- a/candle/timetable/placed_lesson.py +++ b/candle/timetable/placed_lesson.py @@ -75,5 +75,5 @@ def print_info(self): """Only for debug purpose. Print info about placed lesson.""" print() print("Placed-Lesson Info:") - print(f"{self.lesson.subject.name} {self.lesson.room.name} {self.lesson.type.name} {self.lesson.subject.code}") + print(f"{self.lesson.subjects.name} {self.lesson.room.name} {self.lesson.type.name} {self.lesson.subjects.code}") print(f"column: {self.column}") diff --git a/candle/timetable/render.py b/candle/timetable/render.py index f6107df..cf1edef 100644 --- a/candle/timetable/render.py +++ b/candle/timetable/render.py @@ -4,7 +4,8 @@ from flask_login import current_user from sqlalchemy.orm import joinedload, contains_eager -from candle.models import Lesson, Subject +from candle.models import Lesson +from candle.subjects.models import Subject from candle.timetable.layout import Layout diff --git a/candle/timetable/timetable.py b/candle/timetable/timetable.py index 55f4d42..1c19a2a 100644 --- a/candle/timetable/timetable.py +++ b/candle/timetable/timetable.py @@ -23,8 +23,8 @@ def get_lessons_as_csv_response(layout, filename): row.append(str(lesson.end_formatted)) row.append(str(lesson.room.name)) row.append(str(lesson.type)) - row.append(str(lesson.subject.short_code)) - row.append(str(lesson.subject.name)) + row.append(str(lesson.subjects.short_code)) + row.append(str(lesson.subjects.name)) row.append(str(lesson.get_teachers_formatted())) row.append(str(lesson.get_note())) row_formatted = ';'.join(row) @@ -36,4 +36,4 @@ def get_lessons_as_csv_response(layout, filename): cd = f'attachment; filename={filename}.csv' response.headers['Content-Disposition'] = cd response.mimetype='text/csv' - return response \ No newline at end of file + return response From 36e1ccc88fefbcd1263d98326667f4cf39ce1576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 17:40:59 +0200 Subject: [PATCH 04/12] refactor: teachers --- candle/api/api.py | 3 +- candle/models.py | 45 ----------------- candle/panel/templates/panel/panel.html | 2 +- candle/search/search.py | 3 +- .../teacher => teachers}/__init__.py | 0 .../teacher.py => teachers/frontend.py} | 24 ++++------ candle/teachers/models.py | 48 +++++++++++++++++++ candle/teachers/search.py | 10 ++++ .../templates/teachers/listing.html} | 2 +- .../timetable/templates/timetable/list.html | 2 +- 10 files changed, 74 insertions(+), 65 deletions(-) rename candle/{entities/teacher => teachers}/__init__.py (100%) rename candle/{entities/teacher/teacher.py => teachers/frontend.py} (74%) create mode 100644 candle/teachers/models.py create mode 100644 candle/teachers/search.py rename candle/{entities/teacher/templates/teacher/list_teachers.html => teachers/templates/teachers/listing.html} (86%) diff --git a/candle/api/api.py b/candle/api/api.py index a161675..e3d09e6 100644 --- a/candle/api/api.py +++ b/candle/api/api.py @@ -5,7 +5,8 @@ from flask import Blueprint, request, jsonify -from candle.models import Teacher, Lesson, teacher_lessons +from candle.models import Lesson +from candle.teachers.models import teacher_lessons, Teacher from candle.subjects.models import Subject from candle.groups.models import StudentGroup from candle.rooms.models import Room diff --git a/candle/models.py b/candle/models.py index 4325109..13ae018 100644 --- a/candle/models.py +++ b/candle/models.py @@ -6,7 +6,6 @@ from typing import Union from flask_login import UserMixin -from sqlalchemy.ext.hybrid import hybrid_property from candle import db, login_manager from candle.timetable.layout import Layout @@ -35,50 +34,6 @@ def lessons(self): raise NotImplementedError() -teacher_lessons = db.Table('teacher_lessons', - db.Column('id', db.Integer, primary_key=True), - db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')), - db.Column('lesson_id', db.Integer, db.ForeignKey('lesson.id'))) - -class Teacher(SchoolTimetable): - id_ = db.Column('id', db.Integer, primary_key=True) - given_name = db.Column(db.String(50), nullable=True) - family_name = db.Column(db.String(50), nullable=False) - iniciala = db.Column(db.String(50), nullable=True) - oddelenie = db.Column(db.String(), nullable=True) - katedra = db.Column(db.String(), nullable=True) - external_id = db.Column(db.String(), nullable=True) - login = db.Column(db.String(), nullable=True) - slug = db.Column(db.String(), nullable=True) - lessons = db.relationship('Lesson', secondary=teacher_lessons, lazy='dynamic', - backref=db.backref('teachers', lazy='joined', order_by="asc(Teacher.family_name)")) - def __repr__(self): - return f"Teacher(id:'{self.id_}', :'{self.given_name} {self.family_name}' )" - - @property - def short_name(self): - """E.g. for 'Andrej Blaho' return 'A. Blaho'""" - if self.given_name is None or self.given_name.strip() == '': - return self.family_name - return self.given_name[0] + ". " + self.family_name - - @hybrid_property - def fullname(self): - return self.given_name + " " + self.family_name - - @hybrid_property # we need it in SQL queries - def fullname_reversed(self): - return self.family_name + " " + self.given_name - - @property - def timetable_name(self) -> str: - return self.fullname - - @property - def timetable_short_name(self) -> str: - return self.short_name - - class Lesson(db.Model): id_ = db.Column('id', db.Integer, primary_key=True) day = db.Column(db.Integer, nullable=False) diff --git a/candle/panel/templates/panel/panel.html b/candle/panel/templates/panel/panel.html index 98c4a48..ec8a06d 100644 --- a/candle/panel/templates/panel/panel.html +++ b/candle/panel/templates/panel/panel.html @@ -16,7 +16,7 @@
{% include('search/teacher_search.html') %}
diff --git a/candle/search/search.py b/candle/search/search.py index 37cc311..bdf5e24 100644 --- a/candle/search/search.py +++ b/candle/search/search.py @@ -8,7 +8,8 @@ from flask import Blueprint, request, jsonify, render_template from flask_login import current_user -from candle.models import Teacher, Lesson, teacher_lessons, UserTimetable +from candle.models import Lesson, UserTimetable +from candle.teachers.models import teacher_lessons, Teacher from candle.subjects.models import Subject from candle.groups.models import StudentGroup from candle.rooms.models import Room diff --git a/candle/entities/teacher/__init__.py b/candle/teachers/__init__.py similarity index 100% rename from candle/entities/teacher/__init__.py rename to candle/teachers/__init__.py diff --git a/candle/entities/teacher/teacher.py b/candle/teachers/frontend.py similarity index 74% rename from candle/entities/teacher/teacher.py rename to candle/teachers/frontend.py index c87a5f2..f6170d5 100644 --- a/candle/entities/teacher/teacher.py +++ b/candle/teachers/frontend.py @@ -1,29 +1,23 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - -from flask import Blueprint, render_template +from flask import Blueprint, render_template, request from typing import Dict -from candle.models import Teacher +from candle.teachers.models import Teacher from candle.entities.helpers import string_starts_with_ch import unidecode +from candle.teachers.search import search_teachers from candle.timetable.blueprints.readonly import register_timetable_routes -teacher = Blueprint('teacher', - __name__, - template_folder='templates', url_prefix='/ucitelia') +teachers = Blueprint('teachers', __name__, template_folder='templates', url_prefix='/teachers') -@teacher.route('') -def list_teachers(): +@teachers.route('/') +def listing(): """Show all teachers in the list.""" - teachers_list = Teacher.query.order_by(Teacher.family_name).all() + teachers_list = search_teachers(request.args.get("q")) teachers_dict = get_teachers_sorted_by_family_name(teachers_list) title = "Rozvrhy učiteľov" - return render_template('teacher/list_teachers.html', teachers_dict=teachers_dict, title=title, + return render_template('teachers/listing.html', teachers_dict=teachers_dict, title=title, web_header=title) @@ -31,7 +25,7 @@ def get_teacher(slug: str) -> Teacher: return Teacher.query.filter((Teacher.slug == slug) | (Teacher.login == slug)).first_or_404() -register_timetable_routes(teacher, get_teacher) +register_timetable_routes(teachers, get_teacher) def get_teachers_sorted_by_family_name(teachers) -> Dict: diff --git a/candle/teachers/models.py b/candle/teachers/models.py new file mode 100644 index 0000000..a7f488e --- /dev/null +++ b/candle/teachers/models.py @@ -0,0 +1,48 @@ +from sqlalchemy.ext.hybrid import hybrid_property + +from candle import db +from candle.models import SchoolTimetable + +teacher_lessons = db.Table('teacher_lessons', + db.Column('id', db.Integer, primary_key=True), + db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')), + db.Column('lesson_id', db.Integer, db.ForeignKey('lesson.id'))) + + +class Teacher(SchoolTimetable): + id_ = db.Column('id', db.Integer, primary_key=True) + given_name = db.Column(db.String(50), nullable=True) + family_name = db.Column(db.String(50), nullable=False) + iniciala = db.Column(db.String(50), nullable=True) + oddelenie = db.Column(db.String(), nullable=True) + katedra = db.Column(db.String(), nullable=True) + external_id = db.Column(db.String(), nullable=True) + login = db.Column(db.String(), nullable=True) + slug = db.Column(db.String(), nullable=True) + lessons = db.relationship('Lesson', secondary=teacher_lessons, lazy='dynamic', + backref=db.backref('teachers', lazy='joined', order_by="asc(Teacher.family_name)")) + def __repr__(self): + return f"Teacher(id:'{self.id_}', :'{self.given_name} {self.family_name}' )" + + @property + def short_name(self): + """E.g. for 'Andrej Blaho' return 'A. Blaho'""" + if self.given_name is None or self.given_name.strip() == '': + return self.family_name + return self.given_name[0] + ". " + self.family_name + + @hybrid_property + def fullname(self): + return self.given_name + " " + self.family_name + + @hybrid_property # we need it in SQL queries + def fullname_reversed(self): + return self.family_name + " " + self.given_name + + @property + def timetable_name(self) -> str: + return self.fullname + + @property + def timetable_short_name(self) -> str: + return self.short_name diff --git a/candle/teachers/search.py b/candle/teachers/search.py new file mode 100644 index 0000000..71a3b7a --- /dev/null +++ b/candle/teachers/search.py @@ -0,0 +1,10 @@ +from flask_sqlalchemy.query import Query + +from candle.teachers.models import Teacher + + +def search_teachers(query: str|None) -> Query: + teachers = Teacher.query.order_by(Teacher.family_name, Teacher.given_name) + if query: + teachers = teachers.filter(Teacher.fullname.ilike(f"%{query}%") | Teacher.fullname_reversed.ilike(f"%{query}%")) + return teachers diff --git a/candle/entities/teacher/templates/teacher/list_teachers.html b/candle/teachers/templates/teachers/listing.html similarity index 86% rename from candle/entities/teacher/templates/teacher/list_teachers.html rename to candle/teachers/templates/teachers/listing.html index 98e2701..76b1e00 100644 --- a/candle/entities/teacher/templates/teacher/list_teachers.html +++ b/candle/teachers/templates/teachers/listing.html @@ -15,7 +15,7 @@

{{ category_key }}

    {% for teacher in teachers_dict[category_key] -%} -
  • {{ teacher.family_name }} +
  • {{ teacher.family_name }} {%- if teacher.given_name -%} {{ ', ' }}{{ teacher.given_name }} {%- endif -%} diff --git a/candle/timetable/templates/timetable/list.html b/candle/timetable/templates/timetable/list.html index 8fefaaf..e11bc7b 100644 --- a/candle/timetable/templates/timetable/list.html +++ b/candle/timetable/templates/timetable/list.html @@ -21,7 +21,7 @@ {%- for teacher in lesson.teachers -%} {%- if teacher.slug -%} - {{ teacher.short_name }} + {{ teacher.short_name }} {%- if not loop.last -%} {{ ', ' }} {%- endif -%} From 6f2fdc34bbedda21905348d5ef73d730aed00d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 18:25:09 +0200 Subject: [PATCH 05/12] refactor: api --- candle/api.py | 29 +++++++++++++ candle/api/__init__.py | 0 candle/api/api.py | 82 ------------------------------------- candle/groups/api.py | 18 ++++++++ candle/groups/frontend.py | 7 +--- candle/groups/models.py | 6 +++ candle/groups/search.py | 3 ++ candle/rooms/api.py | 18 ++++++++ candle/rooms/frontend.py | 7 +--- candle/rooms/models.py | 7 ++++ candle/rooms/search.py | 4 ++ candle/subjects/api.py | 18 ++++++++ candle/subjects/frontend.py | 7 +--- candle/subjects/models.py | 2 + candle/subjects/search.py | 4 ++ candle/teachers/api.py | 18 ++++++++ candle/teachers/frontend.py | 7 +--- candle/teachers/models.py | 10 +++++ candle/teachers/search.py | 4 ++ 19 files changed, 145 insertions(+), 106 deletions(-) create mode 100644 candle/api.py delete mode 100644 candle/api/__init__.py delete mode 100644 candle/api/api.py create mode 100644 candle/groups/api.py create mode 100644 candle/rooms/api.py create mode 100644 candle/subjects/api.py create mode 100644 candle/teachers/api.py diff --git a/candle/api.py b/candle/api.py new file mode 100644 index 0000000..2c3704b --- /dev/null +++ b/candle/api.py @@ -0,0 +1,29 @@ +from flask import Blueprint + + +def get_error_handler(code, message): + def fn(error): + return {"error": message}, code + + return fn + + +def create_api(): + api = Blueprint('api', __name__, url_prefix="/api") + + from candle.teachers.api import teachers + from candle.subjects.api import subjects + from candle.rooms.api import rooms + from candle.groups.api import groups + + api.register_blueprint(teachers) + api.register_blueprint(subjects) + api.register_blueprint(rooms) + api.register_blueprint(groups) + + api.register_error_handler(404, get_error_handler(404, "Not found.")) + api.register_error_handler(403, get_error_handler(403, "Forbidden.")) + api.register_error_handler(500, get_error_handler(500, "Server error.")) + + return api + diff --git a/candle/api/__init__.py b/candle/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/candle/api/api.py b/candle/api/api.py deleted file mode 100644 index e3d09e6..0000000 --- a/candle/api/api.py +++ /dev/null @@ -1,82 +0,0 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - - -from flask import Blueprint, request, jsonify -from candle.models import Lesson -from candle.teachers.models import teacher_lessons, Teacher -from candle.subjects.models import Subject -from candle.groups.models import StudentGroup -from candle.rooms.models import Room - -api = Blueprint('api', __name__) - - -@api.route('/api/teachers') -def get_teachers(): - """Find all teachers by query string and send them back in a JSON. - - Function will get data from the URL parameter and will respond - with the JSON that will contain a list of teachers. - - Request URL format: /get_data/teachers?term=query, where query is a substring from teacher's name. - Respond JSON format: list, where every element is a dictionary {'id':teacher.slug, 'value':teacher.fullname}. - """ - query_string = request.args.get('term') - query_string = query_string.replace(" ", "%") - query_string = query_string.replace(".", "%") - query_string = "%{}%".format(query_string) - - teachers = Teacher.query.filter( - Teacher.fullname.ilike(query_string) | Teacher.fullname_reversed.ilike(query_string))\ - .order_by(Teacher.family_name) \ - .limit(50).all() - - array = [] - for t in teachers: - array.append({'id': t.slug, 'value': t.fullname}) # do not change key names ('id' and 'value')! (the jquery-ui autocomplete widget will not work) - return jsonify(array) - - -@api.route('/api/rooms') -def get_rooms(): - """Find all rooms by query string and send them back in a JSON. - - Function will get data from the URL parameter and will respond - with the JSON that will contain a list of rooms. - - Request URL format: /get_data/rooms?term=query, where query is a substring from room's name. - Respond JSON format: list, where every element is a dictionary {'id':room.name, 'value':room.name}. - """ - query_string = request.args.get('term') - query_string = query_string.replace(" ", "%") - query_string = "%{}%".format(query_string) - rooms = Room.query.filter(Room.name.ilike(query_string)).limit(50).all() - - array = [] - for r in rooms: - array.append({'id': r.name, 'value': r.name}) - return jsonify(array) - - -@api.route('/api/groups') -def get_groups(): - """Find all student groups by query string and send them back in a JSON. - - Function will get data from the URL parameter and will respond - with the JSON that will contain a list of student groups. - - Request URL format: /get_data/groups?term=query, where query is a substring from group's name. - Respond JSON format: list, where every element is a dictionary {'id':group.name, 'value':group.name}. - """ - query_string = request.args.get('term') - query_string = query_string.replace(" ", "%") - query_string = "%{}%".format(query_string) - groups = StudentGroup.query.filter(StudentGroup.name.ilike(query_string)).limit(50).all() - - array = [] - for g in groups: - array.append({'id': g.name, 'value': g.name}) - return jsonify(array) diff --git a/candle/groups/api.py b/candle/groups/api.py new file mode 100644 index 0000000..5acaf99 --- /dev/null +++ b/candle/groups/api.py @@ -0,0 +1,18 @@ +from flask import Blueprint, request + +from candle.groups.search import get_group, search_groups + +groups = Blueprint('groups', __name__, url_prefix='/groups') + + +@groups.route("/") +def listing(): + groups = search_groups(request.args.get("q")) + return [t.to_dict() for t in groups] + + +@groups.route("//") +def detail(slug): + group = get_group(slug).to_dict() + # TODO: add lessons + return group diff --git a/candle/groups/frontend.py b/candle/groups/frontend.py index 0f05d50..eae857c 100644 --- a/candle/groups/frontend.py +++ b/candle/groups/frontend.py @@ -2,8 +2,7 @@ from typing import Dict from flask import render_template, Blueprint, request -from candle.groups.models import StudentGroup -from candle.groups.search import search_groups +from candle.groups.search import search_groups, get_group from candle.timetable.blueprints.readonly import register_timetable_routes groups = Blueprint('groups', __name__, template_folder='templates', url_prefix='/groups') @@ -19,10 +18,6 @@ def listing(): title=title, web_header=title) -def get_group(group_url_id: str) -> StudentGroup: - return StudentGroup.query.filter((StudentGroup.id_==group_url_id) | (StudentGroup.name==group_url_id)).first_or_404() - - register_timetable_routes(groups, get_group) diff --git a/candle/groups/models.py b/candle/groups/models.py index 5af05b8..20afd2a 100644 --- a/candle/groups/models.py +++ b/candle/groups/models.py @@ -18,3 +18,9 @@ def timetable_name(self) -> str: @property def timetable_short_name(self) -> str: return self.name + + def to_dict(self): + return { + "id": self.id_, + "name": self.name, + } diff --git a/candle/groups/search.py b/candle/groups/search.py index 6937560..50eb4b1 100644 --- a/candle/groups/search.py +++ b/candle/groups/search.py @@ -8,3 +8,6 @@ def search_groups(query: str|None) -> Query: groups = groups.filter(StudentGroup.name.ilike(f"%{query}%")) return groups + +def get_group(slug: str) -> StudentGroup: + return StudentGroup.query.filter((StudentGroup.id_==slug) | (StudentGroup.name==slug)).first_or_404() diff --git a/candle/rooms/api.py b/candle/rooms/api.py new file mode 100644 index 0000000..c742ce3 --- /dev/null +++ b/candle/rooms/api.py @@ -0,0 +1,18 @@ +from flask import Blueprint, request + +from candle.rooms.search import search_rooms, get_room + +rooms = Blueprint('rooms', __name__, url_prefix='/rooms') + + +@rooms.route("/") +def listing(): + rooms = search_rooms(request.args.get("q")) + return [t.to_dict() for t in rooms] + + +@rooms.route("//") +def detail(slug): + room = get_room(slug).to_dict() + # TODO: add lessons + return room diff --git a/candle/rooms/frontend.py b/candle/rooms/frontend.py index e20d4f9..14f51c7 100644 --- a/candle/rooms/frontend.py +++ b/candle/rooms/frontend.py @@ -1,7 +1,6 @@ from flask import Blueprint, render_template, request -from candle.rooms.models import Room -from candle.rooms.search import search_rooms, get_rooms_sorted_by_dashes +from candle.rooms.search import search_rooms, get_rooms_sorted_by_dashes, get_room from candle.timetable.blueprints.readonly import register_timetable_routes rooms = Blueprint('rooms', __name__, url_prefix="/rooms", template_folder="templates") @@ -16,8 +15,4 @@ def listing(): web_header=title) -def get_room(room_url_id: str) -> Room: - return Room.query.filter((Room.id_ == room_url_id) | (Room.name == room_url_id)).first_or_404() - - register_timetable_routes(rooms, get_room) diff --git a/candle/rooms/models.py b/candle/rooms/models.py index 952eb95..8c4c95d 100644 --- a/candle/rooms/models.py +++ b/candle/rooms/models.py @@ -28,6 +28,13 @@ def __repr__(self): def timetable_name(self) -> str: return self.name + def to_dict(self): + return { + "id": self.id_, + "name": self.name, + "capacity": self.capacity, + } + class RoomType(db.Model): id_ = db.Column('id', db.Integer, primary_key=True) diff --git a/candle/rooms/search.py b/candle/rooms/search.py index 0cf125e..e0faf3a 100644 --- a/candle/rooms/search.py +++ b/candle/rooms/search.py @@ -33,3 +33,7 @@ def get_rooms_sorted_by_dashes(rooms: Iterable[Room]) -> dict[str, list[Room]]: d[prefix] = [] d[prefix].append(room) return d + + +def get_room(slug: str) -> Room: + return Room.query.filter((Room.id_ == slug) | (Room.name == slug)).first_or_404() diff --git a/candle/subjects/api.py b/candle/subjects/api.py new file mode 100644 index 0000000..d623a91 --- /dev/null +++ b/candle/subjects/api.py @@ -0,0 +1,18 @@ +from flask import Blueprint, request + +from candle.subjects.search import search_subjects, get_subject + +subjects = Blueprint('subjects', __name__, url_prefix='/subjects') + + +@subjects.route("/") +def listing(): + subjects = search_subjects(request.args.get("q")) + return [s.to_dict() for s in subjects] + + +@subjects.route("//") +def detail(slug): + subject = get_subject(slug).to_dict() + # TODO: add lessons + return subject diff --git a/candle/subjects/frontend.py b/candle/subjects/frontend.py index 4c6ee92..874ade7 100644 --- a/candle/subjects/frontend.py +++ b/candle/subjects/frontend.py @@ -1,7 +1,6 @@ from flask import Blueprint, render_template, request -from candle.subjects.models import Subject -from candle.subjects.search import search_subjects +from candle.subjects.search import search_subjects, get_subject from candle.timetable.blueprints.readonly import register_timetable_routes subjects = Blueprint('subjects', @@ -18,8 +17,4 @@ def listing(): web_header=title) -def get_subject(slug: str) -> Subject: - return Subject.query.filter(Subject.short_code == slug).first_or_404() - - register_timetable_routes(subjects, get_subject) diff --git a/candle/subjects/models.py b/candle/subjects/models.py index 36aba9a..67fb8fc 100644 --- a/candle/subjects/models.py +++ b/candle/subjects/models.py @@ -20,8 +20,10 @@ def to_dict(self) -> dict: Returns the Subject as dict for JSON rendering. """ return { + "id": self.id_, "name": self.name, "shortcode": self.short_code, + "code": self.code, } @property diff --git a/candle/subjects/search.py b/candle/subjects/search.py index 22894f1..d22c672 100644 --- a/candle/subjects/search.py +++ b/candle/subjects/search.py @@ -8,3 +8,7 @@ def search_subjects(query: str|None) -> Query: if query: subjects = subjects.filter(Subject.name.ilike(f"%{query}%") | Subject.short_code.ilike(f"%{query}%")) return subjects + + +def get_subject(slug: str) -> Subject: + return Subject.query.filter((Subject.short_code == slug) | (Subject.id_ == slug)).first_or_404() diff --git a/candle/teachers/api.py b/candle/teachers/api.py new file mode 100644 index 0000000..640328f --- /dev/null +++ b/candle/teachers/api.py @@ -0,0 +1,18 @@ +from flask import Blueprint, request + +from candle.teachers.search import search_teachers, get_teacher + +teachers = Blueprint('teachers', __name__, url_prefix='/teachers') + + +@teachers.route("/") +def listing(): + teachers = search_teachers(request.args.get("q")) + return [t.to_dict() for t in teachers] + + +@teachers.route("//") +def detail(slug): + teacher = get_teacher(slug).to_dict() + # TODO: add lessons + return teacher diff --git a/candle/teachers/frontend.py b/candle/teachers/frontend.py index f6170d5..a1ba05e 100644 --- a/candle/teachers/frontend.py +++ b/candle/teachers/frontend.py @@ -1,11 +1,10 @@ from flask import Blueprint, render_template, request from typing import Dict -from candle.teachers.models import Teacher from candle.entities.helpers import string_starts_with_ch import unidecode -from candle.teachers.search import search_teachers +from candle.teachers.search import search_teachers, get_teacher from candle.timetable.blueprints.readonly import register_timetable_routes teachers = Blueprint('teachers', __name__, template_folder='templates', url_prefix='/teachers') @@ -21,10 +20,6 @@ def listing(): web_header=title) -def get_teacher(slug: str) -> Teacher: - return Teacher.query.filter((Teacher.slug == slug) | (Teacher.login == slug)).first_or_404() - - register_timetable_routes(teachers, get_teacher) diff --git a/candle/teachers/models.py b/candle/teachers/models.py index a7f488e..857f75f 100644 --- a/candle/teachers/models.py +++ b/candle/teachers/models.py @@ -46,3 +46,13 @@ def timetable_name(self) -> str: @property def timetable_short_name(self) -> str: return self.short_name + + def to_dict(self): + return { + "id": self.id_, + "given_name": self.given_name, + "family_name": self.family_name, + "department": self.katedra, + "login": self.login, + "slug": self.slug, + } diff --git a/candle/teachers/search.py b/candle/teachers/search.py index 71a3b7a..e936314 100644 --- a/candle/teachers/search.py +++ b/candle/teachers/search.py @@ -8,3 +8,7 @@ def search_teachers(query: str|None) -> Query: if query: teachers = teachers.filter(Teacher.fullname.ilike(f"%{query}%") | Teacher.fullname_reversed.ilike(f"%{query}%")) return teachers + + +def get_teacher(slug: str) -> Teacher: + return Teacher.query.filter((Teacher.slug == slug) | (Teacher.login == slug) | (Teacher.id_ == slug)).first_or_404() From f0d480ceac71c29a170bfa9890d823e8cc999323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:08:22 +0200 Subject: [PATCH 06/12] refactor: update init --- candle/__init__.py | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/candle/__init__.py b/candle/__init__.py index a83c44e..c54b54b 100644 --- a/candle/__init__.py +++ b/candle/__init__.py @@ -10,10 +10,6 @@ from flask_wtf.csrf import CSRFProtect from candle.config import Config -from werkzeug.middleware.dispatcher import DispatcherMiddleware -from werkzeug.wrappers import Response - - login_manager = LoginManager() # keeps session data login_manager.login_view = 'auth.login' # login_manager.login_message_category = 'info' # flash message category (not yet implemented) @@ -28,13 +24,6 @@ def create_app(config_class=Config): app.url_map.strict_slashes = False app.config.from_object(config_class) - # Use middleware for 2016 path-prefix. Used for testing. Source: https://dlukes.github.io/flask-wsgi-url-prefix.html#mwe - if app.config['ENV'] == "development": - app.wsgi_app = DispatcherMiddleware( - Response('Not Found', status=404), - {'/2016-2017-zima': app.wsgi_app} - ) - init_extensions(app) register_blueprints(app) return app @@ -42,31 +31,33 @@ def create_app(config_class=Config): def register_blueprints(app): from candle.main.main import main - from candle.api.api import api from candle.auth.auth import auth from candle.timetable.timetable import timetable from candle.my_timetable.my_timetable import my_timetable - from candle.entities.student_group.student_group import student_group - from candle.entities.teacher.teacher import teacher - from candle.entities.subject.subject import subject from candle.panel.panel import panel from candle.search.search import search from candle.errors.errors import errors from candle.rooms.frontend import rooms + from candle.groups.frontend import groups + from candle.subjects.frontend import subjects + from candle.teachers.frontend import teachers + from candle.api import create_api app.register_blueprint(main) - app.register_blueprint(api) app.register_blueprint(auth) app.register_blueprint(timetable) app.register_blueprint(my_timetable) - app.register_blueprint(student_group) - app.register_blueprint(teacher) - app.register_blueprint(subject) app.register_blueprint(panel) app.register_blueprint(search) app.register_blueprint(errors) + api = create_api() + app.register_blueprint(api) + app.register_blueprint(rooms) + app.register_blueprint(groups) + app.register_blueprint(subjects) + app.register_blueprint(teachers) def init_extensions(app): From 28aab9a8b2f64f61606eabaf9b8e3d754c8cb570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:13:38 +0200 Subject: [PATCH 07/12] refactor: move remaining entities module --- candle/entities/__init__.py | 0 candle/teachers/frontend.py | 2 +- candle/{entities/helpers.py => utils.py} | 0 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 candle/entities/__init__.py rename candle/{entities/helpers.py => utils.py} (100%) diff --git a/candle/entities/__init__.py b/candle/entities/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/candle/teachers/frontend.py b/candle/teachers/frontend.py index a1ba05e..279521e 100644 --- a/candle/teachers/frontend.py +++ b/candle/teachers/frontend.py @@ -1,7 +1,7 @@ from flask import Blueprint, render_template, request from typing import Dict -from candle.entities.helpers import string_starts_with_ch +from candle.utils import string_starts_with_ch import unidecode from candle.teachers.search import search_teachers, get_teacher diff --git a/candle/entities/helpers.py b/candle/utils.py similarity index 100% rename from candle/entities/helpers.py rename to candle/utils.py From dba0ec39c0abecd524eb7294a8dc61be322e5a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:33:19 +0200 Subject: [PATCH 08/12] refactor: main -> common --- candle/auth/auth.py | 6 +++--- candle/{main => common}/__init__.py | 0 candle/{main/main.py => common/frontend.py} | 17 +++++------------ candle/{main => common}/static/css/dark.css | 2 +- candle/{main => common}/static/css/main.css | 2 +- .../static/images/bg-body-kiosk.png | Bin .../{main => common}/static/images/bg-body.png | Bin .../static/images/bg-obsah-top-left.png | Bin .../static/images/bg-obsah-top.png | Bin .../{main => common}/static/images/favicon.ico | Bin .../static/images/ikona-typ-c.png | Bin .../static/images/ikona-typ-k.png | Bin .../static/images/ikona-typ-l.png | Bin .../static/images/ikona-typ-n.png | Bin .../static/images/ikona-typ-p.png | Bin .../static/images/ikona-typ-s.png | Bin .../static/images/ikona-typ-v.png | Bin .../static/images/loading-gray.gif | Bin .../static/images/logo-small.png | Bin .../{main => common}/static/images/logo3.png | Bin .../{main => common}/static/images/smile.svg | 0 .../{main => common}/static/js/csrf_header.js | 0 .../{main => common}/templates/main/base.html | 6 +++--- .../templates/main/first_row.html | 2 +- .../{main => common}/templates/main/head.html | 6 +++--- .../{main => common}/templates/main/title.html | 0 26 files changed, 17 insertions(+), 24 deletions(-) rename candle/{main => common}/__init__.py (100%) rename candle/{main/main.py => common/frontend.py} (72%) rename candle/{main => common}/static/css/dark.css (99%) rename candle/{main => common}/static/css/main.css (99%) rename candle/{main => common}/static/images/bg-body-kiosk.png (100%) rename candle/{main => common}/static/images/bg-body.png (100%) rename candle/{main => common}/static/images/bg-obsah-top-left.png (100%) rename candle/{main => common}/static/images/bg-obsah-top.png (100%) rename candle/{main => common}/static/images/favicon.ico (100%) rename candle/{main => common}/static/images/ikona-typ-c.png (100%) rename candle/{main => common}/static/images/ikona-typ-k.png (100%) rename candle/{main => common}/static/images/ikona-typ-l.png (100%) rename candle/{main => common}/static/images/ikona-typ-n.png (100%) rename candle/{main => common}/static/images/ikona-typ-p.png (100%) rename candle/{main => common}/static/images/ikona-typ-s.png (100%) rename candle/{main => common}/static/images/ikona-typ-v.png (100%) rename candle/{main => common}/static/images/loading-gray.gif (100%) rename candle/{main => common}/static/images/logo-small.png (100%) rename candle/{main => common}/static/images/logo3.png (100%) rename candle/{main => common}/static/images/smile.svg (100%) rename candle/{main => common}/static/js/csrf_header.js (100%) rename candle/{main => common}/templates/main/base.html (88%) rename candle/{main => common}/templates/main/first_row.html (92%) rename candle/{main => common}/templates/main/head.html (76%) rename candle/{main => common}/templates/main/title.html (100%) diff --git a/candle/auth/auth.py b/candle/auth/auth.py index f8249fc..899c14c 100644 --- a/candle/auth/auth.py +++ b/candle/auth/auth.py @@ -19,7 +19,7 @@ def wrapper(*args, **kwargs): if current_app.config['ENV'] == 'production': if request.environ.get('REMOTE_USER') is None: flash('User not logged in', 'error') - return redirect(url_for('main.home')) + return redirect(url_for('common.home')) else: request.environ.setdefault("REMOTE_USER", "svttest") return func(*args, **kwargs) @@ -39,11 +39,11 @@ def login(): # flash('Prihlasenie bolo neuspesne.') login_user(user, remember=True) - return redirect(url_for("main.home")) + return redirect(url_for("common.home")) @auth.route('/odhlasit') @require_remote_user def logout(): logout_user() - return redirect(url_for('main.home')) + return redirect(url_for('common.home')) diff --git a/candle/main/__init__.py b/candle/common/__init__.py similarity index 100% rename from candle/main/__init__.py rename to candle/common/__init__.py diff --git a/candle/main/main.py b/candle/common/frontend.py similarity index 72% rename from candle/main/main.py rename to candle/common/frontend.py index ffedeaf..94a5b38 100644 --- a/candle/main/main.py +++ b/candle/common/frontend.py @@ -1,22 +1,15 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol, FMFI UK -''' - -from flask import Blueprint, redirect, url_for, render_template +from flask import Blueprint, render_template, redirect, url_for from flask_login import current_user from candle import db from candle.models import UserTimetable -main = Blueprint('main', - __name__, - template_folder='templates', +common = Blueprint('common', __name__, template_folder="templates", static_folder='static', - static_url_path='/main/static' - ) + static_url_path='/common/static') + -@main.route('/') +@common.route('/') def home(): if current_user.is_authenticated: my_timetables = current_user.timetables diff --git a/candle/main/static/css/dark.css b/candle/common/static/css/dark.css similarity index 99% rename from candle/main/static/css/dark.css rename to candle/common/static/css/dark.css index 291cd5a..4d3af07 100644 --- a/candle/main/static/css/dark.css +++ b/candle/common/static/css/dark.css @@ -287,4 +287,4 @@ ul.quickswitch li.active a { #web_header ul.quickswitch li.active a { color: black; -} \ No newline at end of file +} diff --git a/candle/main/static/css/main.css b/candle/common/static/css/main.css similarity index 99% rename from candle/main/static/css/main.css rename to candle/common/static/css/main.css index 9e46e19..41ed5f5 100644 --- a/candle/main/static/css/main.css +++ b/candle/common/static/css/main.css @@ -823,4 +823,4 @@ ul.quickswitch li a { #welcome { font-size: 1.25em; -} \ No newline at end of file +} diff --git a/candle/main/static/images/bg-body-kiosk.png b/candle/common/static/images/bg-body-kiosk.png similarity index 100% rename from candle/main/static/images/bg-body-kiosk.png rename to candle/common/static/images/bg-body-kiosk.png diff --git a/candle/main/static/images/bg-body.png b/candle/common/static/images/bg-body.png similarity index 100% rename from candle/main/static/images/bg-body.png rename to candle/common/static/images/bg-body.png diff --git a/candle/main/static/images/bg-obsah-top-left.png b/candle/common/static/images/bg-obsah-top-left.png similarity index 100% rename from candle/main/static/images/bg-obsah-top-left.png rename to candle/common/static/images/bg-obsah-top-left.png diff --git a/candle/main/static/images/bg-obsah-top.png b/candle/common/static/images/bg-obsah-top.png similarity index 100% rename from candle/main/static/images/bg-obsah-top.png rename to candle/common/static/images/bg-obsah-top.png diff --git a/candle/main/static/images/favicon.ico b/candle/common/static/images/favicon.ico similarity index 100% rename from candle/main/static/images/favicon.ico rename to candle/common/static/images/favicon.ico diff --git a/candle/main/static/images/ikona-typ-c.png b/candle/common/static/images/ikona-typ-c.png similarity index 100% rename from candle/main/static/images/ikona-typ-c.png rename to candle/common/static/images/ikona-typ-c.png diff --git a/candle/main/static/images/ikona-typ-k.png b/candle/common/static/images/ikona-typ-k.png similarity index 100% rename from candle/main/static/images/ikona-typ-k.png rename to candle/common/static/images/ikona-typ-k.png diff --git a/candle/main/static/images/ikona-typ-l.png b/candle/common/static/images/ikona-typ-l.png similarity index 100% rename from candle/main/static/images/ikona-typ-l.png rename to candle/common/static/images/ikona-typ-l.png diff --git a/candle/main/static/images/ikona-typ-n.png b/candle/common/static/images/ikona-typ-n.png similarity index 100% rename from candle/main/static/images/ikona-typ-n.png rename to candle/common/static/images/ikona-typ-n.png diff --git a/candle/main/static/images/ikona-typ-p.png b/candle/common/static/images/ikona-typ-p.png similarity index 100% rename from candle/main/static/images/ikona-typ-p.png rename to candle/common/static/images/ikona-typ-p.png diff --git a/candle/main/static/images/ikona-typ-s.png b/candle/common/static/images/ikona-typ-s.png similarity index 100% rename from candle/main/static/images/ikona-typ-s.png rename to candle/common/static/images/ikona-typ-s.png diff --git a/candle/main/static/images/ikona-typ-v.png b/candle/common/static/images/ikona-typ-v.png similarity index 100% rename from candle/main/static/images/ikona-typ-v.png rename to candle/common/static/images/ikona-typ-v.png diff --git a/candle/main/static/images/loading-gray.gif b/candle/common/static/images/loading-gray.gif similarity index 100% rename from candle/main/static/images/loading-gray.gif rename to candle/common/static/images/loading-gray.gif diff --git a/candle/main/static/images/logo-small.png b/candle/common/static/images/logo-small.png similarity index 100% rename from candle/main/static/images/logo-small.png rename to candle/common/static/images/logo-small.png diff --git a/candle/main/static/images/logo3.png b/candle/common/static/images/logo3.png similarity index 100% rename from candle/main/static/images/logo3.png rename to candle/common/static/images/logo3.png diff --git a/candle/main/static/images/smile.svg b/candle/common/static/images/smile.svg similarity index 100% rename from candle/main/static/images/smile.svg rename to candle/common/static/images/smile.svg diff --git a/candle/main/static/js/csrf_header.js b/candle/common/static/js/csrf_header.js similarity index 100% rename from candle/main/static/js/csrf_header.js rename to candle/common/static/js/csrf_header.js diff --git a/candle/main/templates/main/base.html b/candle/common/templates/main/base.html similarity index 88% rename from candle/main/templates/main/base.html rename to candle/common/templates/main/base.html index 75411f2..1700f2e 100644 --- a/candle/main/templates/main/base.html +++ b/candle/common/templates/main/base.html @@ -6,7 +6,7 @@ {% include('main/first_row.html') %} @@ -43,7 +43,7 @@

    {{ web_header }}

    - + {% endblock %} - \ No newline at end of file + diff --git a/candle/main/templates/main/first_row.html b/candle/common/templates/main/first_row.html similarity index 92% rename from candle/main/templates/main/first_row.html rename to candle/common/templates/main/first_row.html index 8c8c10c..45a0752 100644 --- a/candle/main/templates/main/first_row.html +++ b/candle/common/templates/main/first_row.html @@ -21,7 +21,7 @@

    Menu používateľa

    Linky

      -
    • Rozvrh
    • +
    • Rozvrh
    • {#
    • Zoznam chýb
    • #} {#
    • Dokumentácia
    • #} {#
    • FAQ
    • #} diff --git a/candle/main/templates/main/head.html b/candle/common/templates/main/head.html similarity index 76% rename from candle/main/templates/main/head.html rename to candle/common/templates/main/head.html index 01173ed..575b8b3 100644 --- a/candle/main/templates/main/head.html +++ b/candle/common/templates/main/head.html @@ -6,11 +6,11 @@ {% include('main/title.html') %} - + {# Candle CSS #} - - + + {# CSS for JQUERY-UI #} diff --git a/candle/main/templates/main/title.html b/candle/common/templates/main/title.html similarity index 100% rename from candle/main/templates/main/title.html rename to candle/common/templates/main/title.html From 1cf9657d5fad5fd5e78a75d7872f72b8e69ae2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:35:01 +0200 Subject: [PATCH 09/12] refactor: errors -> common --- candle/__init__.py | 9 +++--- candle/common/frontend.py | 21 ++++++++++++++ .../{errors => common}/static/error-404.png | Bin .../templates/errors/403.html | 0 .../templates/errors/404.html | 0 .../templates/errors/500.html | 0 .../templates/errors/csrf_error.html | 0 candle/errors/__init__.py | 0 candle/errors/errors.py | 27 ------------------ 9 files changed, 25 insertions(+), 32 deletions(-) rename candle/{errors => common}/static/error-404.png (100%) rename candle/{errors => common}/templates/errors/403.html (100%) rename candle/{errors => common}/templates/errors/404.html (100%) rename candle/{errors => common}/templates/errors/500.html (100%) rename candle/{errors => common}/templates/errors/csrf_error.html (100%) delete mode 100644 candle/errors/__init__.py delete mode 100644 candle/errors/errors.py diff --git a/candle/__init__.py b/candle/__init__.py index c54b54b..9deb897 100644 --- a/candle/__init__.py +++ b/candle/__init__.py @@ -30,30 +30,29 @@ def create_app(config_class=Config): def register_blueprints(app): - from candle.main.main import main from candle.auth.auth import auth from candle.timetable.timetable import timetable from candle.my_timetable.my_timetable import my_timetable from candle.panel.panel import panel from candle.search.search import search - from candle.errors.errors import errors + + from candle.api import create_api + from candle.common.frontend import common from candle.rooms.frontend import rooms from candle.groups.frontend import groups from candle.subjects.frontend import subjects from candle.teachers.frontend import teachers - from candle.api import create_api - app.register_blueprint(main) app.register_blueprint(auth) app.register_blueprint(timetable) app.register_blueprint(my_timetable) app.register_blueprint(panel) app.register_blueprint(search) - app.register_blueprint(errors) api = create_api() app.register_blueprint(api) + app.register_blueprint(common) app.register_blueprint(rooms) app.register_blueprint(groups) app.register_blueprint(subjects) diff --git a/candle/common/frontend.py b/candle/common/frontend.py index 94a5b38..3169333 100644 --- a/candle/common/frontend.py +++ b/candle/common/frontend.py @@ -3,6 +3,7 @@ from candle import db from candle.models import UserTimetable +from flask_wtf.csrf import CSRFError common = Blueprint('common', __name__, template_folder="templates", static_folder='static', @@ -26,3 +27,23 @@ def home(): return redirect(url_for('my_timetable.show_timetable', id_=ut.id_) ) else: # user is logged out, show welcome-info: return render_template('timetable/timetable.html', title='Rozvrh', show_welcome=True) + + +@common.app_errorhandler(404) +def error_404(error): + return render_template('errors/404.html'), 404 + + +@common.app_errorhandler(403) +def error_403(error): + return render_template('errors/403.html'), 403 + + +@common.app_errorhandler(500) +def error_500(error): + return render_template('errors/500.html'), 500 + + +@common.app_errorhandler(CSRFError) +def csrf_error(reason): + return render_template('errors/csrf_error.html', reason=reason.description), 400 diff --git a/candle/errors/static/error-404.png b/candle/common/static/error-404.png similarity index 100% rename from candle/errors/static/error-404.png rename to candle/common/static/error-404.png diff --git a/candle/errors/templates/errors/403.html b/candle/common/templates/errors/403.html similarity index 100% rename from candle/errors/templates/errors/403.html rename to candle/common/templates/errors/403.html diff --git a/candle/errors/templates/errors/404.html b/candle/common/templates/errors/404.html similarity index 100% rename from candle/errors/templates/errors/404.html rename to candle/common/templates/errors/404.html diff --git a/candle/errors/templates/errors/500.html b/candle/common/templates/errors/500.html similarity index 100% rename from candle/errors/templates/errors/500.html rename to candle/common/templates/errors/500.html diff --git a/candle/errors/templates/errors/csrf_error.html b/candle/common/templates/errors/csrf_error.html similarity index 100% rename from candle/errors/templates/errors/csrf_error.html rename to candle/common/templates/errors/csrf_error.html diff --git a/candle/errors/__init__.py b/candle/errors/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/candle/errors/errors.py b/candle/errors/errors.py deleted file mode 100644 index e6ef50d..0000000 --- a/candle/errors/errors.py +++ /dev/null @@ -1,27 +0,0 @@ -''' -Project: Candle (New Generation): Candle rewrite from PHP to Python. -Author: Daniel Grohol -''' - -from flask import Blueprint, render_template -from flask_wtf.csrf import CSRFError - -errors = Blueprint('errors', - __name__, - template_folder='templates') - -@errors.app_errorhandler(404) -def error_404(error): - return render_template('errors/404.html'), 404 - -@errors.app_errorhandler(403) -def error_403(error): - return render_template('errors/403.html'), 403 - -@errors.app_errorhandler(500) -def error_500(error): - return render_template('errors/500.html'), 500 - -@errors.app_errorhandler(CSRFError) -def csrf_error(reason): - return render_template('errors/csrf_error.html', reason=reason.description), 400 From 94c8e2a4b1cea156a8dd8504c4e9fdafb3d3e2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:50:34 +0200 Subject: [PATCH 10/12] refactor: add unified search --- candle/common/frontend.py | 20 ++++++++++++- candle/common/templates/search.html | 45 +++++++++++++++++++++++++++++ candle/groups/models.py | 3 ++ candle/rooms/models.py | 3 ++ candle/subjects/models.py | 11 +++++++ candle/teachers/models.py | 11 +++++++ 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 candle/common/templates/search.html diff --git a/candle/common/frontend.py b/candle/common/frontend.py index 3169333..121a216 100644 --- a/candle/common/frontend.py +++ b/candle/common/frontend.py @@ -1,10 +1,15 @@ -from flask import Blueprint, render_template, redirect, url_for +from flask import Blueprint, render_template, redirect, url_for, request from flask_login import current_user from candle import db +from candle.groups.search import search_groups from candle.models import UserTimetable from flask_wtf.csrf import CSRFError +from candle.rooms.search import search_rooms +from candle.subjects.search import search_subjects +from candle.teachers.search import search_teachers + common = Blueprint('common', __name__, template_folder="templates", static_folder='static', static_url_path='/common/static') @@ -29,6 +34,19 @@ def home(): return render_template('timetable/timetable.html', title='Rozvrh', show_welcome=True) +@common.route('/search/') +def search(): + query = request.args.get("q") + if query: + return render_template("search.html", title="Vyhľadávanie", + subjects=search_subjects(query).limit(20).all(), + groups=search_groups(query).limit(20).all(), + rooms=search_rooms(query).limit(20).all(), + teachers=search_teachers(query).limit(20).all(), + ) + return render_template('search.html', title='Vyhľadávanie') + + @common.app_errorhandler(404) def error_404(error): return render_template('errors/404.html'), 404 diff --git a/candle/common/templates/search.html b/candle/common/templates/search.html new file mode 100644 index 0000000..0fae65c --- /dev/null +++ b/candle/common/templates/search.html @@ -0,0 +1,45 @@ +{% extends "main/base.html" %} + +{% block obsah %} +
      + + +
      + + {% if subjects %} +

      Predmety

      +
        + {% for subject in subjects %} +
      • {{ subject }}
      • + {% endfor %} +
      + {% endif %} + + {% if groups %} +

      Krúžky

      +
        + {% for group in groups %} +
      • {{ group }}
      • + {% endfor %} +
      + {% endif %} + + {% if teachers %} +

      Učitelia

      +
        + {% for teacher in teachers %} +
      • {{ teacher }}
      • + {% endfor %} +
      + {% endif %} + + {% if rooms %} +

      Miestnosti

      +
        + {% for room in rooms %} +
      • {{ room }}
      • + {% endfor %} +
      + {% endif %} +{% endblock %} + diff --git a/candle/groups/models.py b/candle/groups/models.py index 20afd2a..d008e1d 100644 --- a/candle/groups/models.py +++ b/candle/groups/models.py @@ -11,6 +11,9 @@ class StudentGroup(SchoolTimetable): name = db.Column(db.String(30), nullable=False) lessons = db.relationship('Lesson', secondary=student_group_lessons, lazy='dynamic') + def __str__(self): + return self.name + @property def timetable_name(self) -> str: return f"Rozvh krúžku {self.name}" diff --git a/candle/rooms/models.py b/candle/rooms/models.py index 8c4c95d..d3f7445 100644 --- a/candle/rooms/models.py +++ b/candle/rooms/models.py @@ -10,6 +10,9 @@ class Room(SchoolTimetable): lessons = db.relationship('Lesson', backref='room', lazy='dynamic') # 'lazy dynamic' allows us to work with lessons attribute like with query ( we can run order_by, etc) + def __str__(self): + return self.name + @property def prefix(self): # xMieRez is a special case: diff --git a/candle/subjects/models.py b/candle/subjects/models.py index 67fb8fc..e7b425a 100644 --- a/candle/subjects/models.py +++ b/candle/subjects/models.py @@ -15,6 +15,11 @@ class Subject(SchoolTimetable): def __repr__(self): return f"Subject(id:'{self.id_}', name:'{self.name}' )" + def __str__(self): + if self.short_code: + return f"{self.short_code} - {self.name}" + return self.name + def to_dict(self) -> dict: """ Returns the Subject as dict for JSON rendering. @@ -29,3 +34,9 @@ def to_dict(self) -> dict: @property def timetable_name(self) -> str: return self.name + + @property + def url_id(self): + if self.short_code: + return self.short_code + return self.id_ diff --git a/candle/teachers/models.py b/candle/teachers/models.py index 857f75f..6db01dd 100644 --- a/candle/teachers/models.py +++ b/candle/teachers/models.py @@ -24,6 +24,10 @@ class Teacher(SchoolTimetable): def __repr__(self): return f"Teacher(id:'{self.id_}', :'{self.given_name} {self.family_name}' )" + + def __str__(self): + return self.fullname + @property def short_name(self): """E.g. for 'Andrej Blaho' return 'A. Blaho'""" @@ -56,3 +60,10 @@ def to_dict(self): "login": self.login, "slug": self.slug, } + + @property + def url_id(self): + if self.slug: + return self.slug + return self.id_ + From aa2935497337bc0f32ec93534bcac54ada57ac56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:50:56 +0200 Subject: [PATCH 11/12] refactor: enable FLASK_DEBUG in container --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index e07c343..33551ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: context: . environment: - FLASK_APP=run.py + - FLASK_DEBUG=True - SQLALCHEMY_DATABASE_URI=mysql+pymysql://candle:candle@db/candle env_file: - .env From 7ac1750e4a5a9eb608ef6582e159ff9523f450a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zahradn=C3=ADk?= Date: Mon, 11 Sep 2023 19:57:34 +0200 Subject: [PATCH 12/12] refactor: fix auth --- candle/auth/auth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/candle/auth/auth.py b/candle/auth/auth.py index 899c14c..1e9ea0b 100644 --- a/candle/auth/auth.py +++ b/candle/auth/auth.py @@ -16,12 +16,12 @@ def require_remote_user(func): @wraps(func) def wrapper(*args, **kwargs): - if current_app.config['ENV'] == 'production': + if current_app.config['DEBUG']: + request.environ.setdefault("REMOTE_USER", "svttest") + else: if request.environ.get('REMOTE_USER') is None: flash('User not logged in', 'error') return redirect(url_for('common.home')) - else: - request.environ.setdefault("REMOTE_USER", "svttest") return func(*args, **kwargs) return wrapper