From e124209da0dcba441c4c01f958b32ee04a9002d1 Mon Sep 17 00:00:00 2001 From: vadik likholetov Date: Mon, 13 Nov 2023 19:51:57 +0200 Subject: [PATCH] Added user roles, updated admin interface --- admin.py | 34 +++++++++++++++++++++++++++------- models.py | 23 +++++++++++++++++++++++ sar_calls.py | 37 +++++++++++++++++++++++++++++++++++++ templates/sar_details.html | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 7 deletions(-) diff --git a/admin.py b/admin.py index b8a1073..fe28ab3 100644 --- a/admin.py +++ b/admin.py @@ -1,12 +1,32 @@ from flask_admin import Admin from flask_admin.contrib.sqla import ModelView +from flask_login import current_user from app import app, db -from models import User, SARCall, SARCategory, GPSTrack, SARStatus, SARResult +from models import User, Role, SARCall, Comment, SARCategory, GPSTrack, SARStatus, SARResult + +class AdminModelView(ModelView): + def is_accessible(self): + # return current_user.is_authenticated and current_user.role.name == "admin" + return True + + +class UserModelView(AdminModelView): + # Display human-readable names for foreign keys + column_list = ('id', 'username', 'full_name', "email", "phone_number", "role.name", "password") + column_labels = {'role.name': 'user role'} + + # Allow searching and filtering by related fields + column_searchable_list = ('role.name', 'username', 'full_name', "email", "phone_number") + column_filters = ('role.name', 'username', 'full_name', "email", "phone_number") + + admin = Admin(app, name='SAR Admin', template_mode='bootstrap3') -admin.add_view(ModelView(User, db.session)) -admin.add_view(ModelView(SARCall, db.session)) -admin.add_view(ModelView(SARCategory, db.session)) -admin.add_view(ModelView(SARStatus, db.session)) -admin.add_view(ModelView(SARResult, db.session)) -admin.add_view(ModelView(GPSTrack, db.session)) +admin.add_view(UserModelView(User, db.session)) +admin.add_view(AdminModelView(Role, db.session)) +admin.add_view(AdminModelView(SARCall, db.session)) +admin.add_view(AdminModelView(Comment, db.session)) +admin.add_view(AdminModelView(GPSTrack, db.session)) +admin.add_view(AdminModelView(SARCategory, db.session, category="Dictionaries")) +admin.add_view(AdminModelView(SARStatus, db.session, category="Dictionaries")) +admin.add_view(AdminModelView(SARResult, db.session, category="Dictionaries")) diff --git a/models.py b/models.py index 9df011e..62adb2f 100644 --- a/models.py +++ b/models.py @@ -9,6 +9,14 @@ class User(UserMixin, db.Model): email = db.Column(db.String(150), unique=True, nullable=False) phone_number = db.Column(db.String(50), nullable=True) password = db.Column(db.String(50)) + role_id = db.Column(db.Integer, db.ForeignKey('role.id')) + role = db.relationship('Role', backref=db.backref('users', lazy='dynamic')) + +class Role(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(80), unique=True) + def __repr__(self): + return self.name # This is so that when we print the Role class, it will print the name instead of the object memory address class SARCall(db.Model): @@ -30,14 +38,20 @@ class SARCall(db.Model): class SARCategory(db.Model): id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False) name = db.Column(db.String(150), unique=True, nullable=False) + def __repr__(self): + return self.name # Assuming 'name' is the field you want to display class SARResult(db.Model): id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False) name = db.Column(db.String(150), unique=True, nullable=False) + def __repr__(self): + return self.name # Assuming 'name' is the field you want to display class SARStatus(db.Model): id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False) name = db.Column(db.String(150), unique=True, nullable=False) + def __repr__(self): + return self.name # Assuming 'name' is the field you want to display class GPSTrack(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -45,3 +59,12 @@ class GPSTrack(db.Model): color = db.Column(db.String(7)) # Stores the color as a HEX code like #FF5733 sar_call_id = db.Column(db.Integer, db.ForeignKey('sar_call.id'), nullable=False) sar_call = db.relationship('SARCall', backref=db.backref('gps_tracks', lazy=True)) + +class Comment(db.Model): + id = db.Column(db.Integer, primary_key=True) + text = db.Column(db.Text, nullable=True) + gpx_data = db.Column(db.Text, nullable=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + user = db.relationship('User', backref=db.backref('comments', lazy=True)) + sar_call_id = db.Column(db.Integer, db.ForeignKey('sar_call.id'), nullable=False) + sar_call = db.relationship('SARCall', backref=db.backref('comments', lazy=True)) diff --git a/sar_calls.py b/sar_calls.py index 7624751..46145d6 100644 --- a/sar_calls.py +++ b/sar_calls.py @@ -104,3 +104,40 @@ def delete_sar(id): flash('SAR call record deleted successfully!', 'success') return redirect(url_for('list_sar')) + +@app.route('/add_comment/', methods=['POST']) +@login_required +def add_comment(sar_call_id): + text = request.form.get('text') + gpx_file = request.files.get('gpx_file') + gpx_data = gpx_file.read().decode("utf-8") if gpx_file else None + comment = Comment(text=text, gpx_data=gpx_data, user_id=current_user.id, sar_call_id=sar_call_id) + db.session.add(comment) + db.session.commit() + return redirect(url_for('view_sar', sar_call_id=sar_call_id)) + +@app.route('/edit_comment/', methods=['GET', 'POST']) +@login_required +def edit_comment(comment_id): + comment = Comment.query.get_or_404(comment_id) + if current_user.id != comment.user_id and current_user.id != 1 and current_user.id != comment.sar_call.user_id: + abort(403) + # Handle the form submission and save changes + if request.method == 'POST': + comment.text = request.form.get('text') + gpx_file = request.files.get('gpx_file') + if gpx_file: + comment.gpx_data = gpx_file.read().decode("utf-8") + db.session.commit() + return redirect(url_for('view_sar', sar_call_id=comment.sar_call_id)) + + +@app.route('/delete_comment/', methods=['POST']) +@login_required +def delete_comment(comment_id): + comment = Comment.query.get_or_404(comment_id) + if current_user.id != comment.user_id and current_user.id != 1 and current_user.id != comment.sar_call.user_id: + abort(403) + db.session.delete(comment) + db.session.commit() + return redirect(url_for('view_sar', sar_call_id=comment.sar_call_id)) diff --git a/templates/sar_details.html b/templates/sar_details.html index 490e969..c8920d4 100644 --- a/templates/sar_details.html +++ b/templates/sar_details.html @@ -25,12 +25,47 @@ +
Edit Delete
+ + +{% for comment in sar_call.comments %} +
+ {{ comment.user.username }}: +

{{ comment.text }}

+ {% if comment.gpx_data %} + + {% endif %} + {% if current_user.id == comment.user_id or current_user.id == 1 or current_user.id == sar_call.user_id %} + Edit + Delete + {% endif %} +
+{% endfor %} + + +
+
+ + +
+
+ + +
+ +
+ + + + + +