Added user roles, updated admin interface
This commit is contained in:
parent
64d00c8293
commit
e124209da0
34
admin.py
34
admin.py
@ -1,12 +1,32 @@
|
|||||||
from flask_admin import Admin
|
from flask_admin import Admin
|
||||||
from flask_admin.contrib.sqla import ModelView
|
from flask_admin.contrib.sqla import ModelView
|
||||||
|
from flask_login import current_user
|
||||||
from app import app, db
|
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 = Admin(app, name='SAR Admin', template_mode='bootstrap3')
|
||||||
admin.add_view(ModelView(User, db.session))
|
admin.add_view(UserModelView(User, db.session))
|
||||||
admin.add_view(ModelView(SARCall, db.session))
|
admin.add_view(AdminModelView(Role, db.session))
|
||||||
admin.add_view(ModelView(SARCategory, db.session))
|
admin.add_view(AdminModelView(SARCall, db.session))
|
||||||
admin.add_view(ModelView(SARStatus, db.session))
|
admin.add_view(AdminModelView(Comment, db.session))
|
||||||
admin.add_view(ModelView(SARResult, db.session))
|
admin.add_view(AdminModelView(GPSTrack, db.session))
|
||||||
admin.add_view(ModelView(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"))
|
||||||
|
23
models.py
23
models.py
@ -9,6 +9,14 @@ class User(UserMixin, db.Model):
|
|||||||
email = db.Column(db.String(150), unique=True, nullable=False)
|
email = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
phone_number = db.Column(db.String(50), nullable=True)
|
phone_number = db.Column(db.String(50), nullable=True)
|
||||||
password = db.Column(db.String(50))
|
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):
|
class SARCall(db.Model):
|
||||||
@ -30,14 +38,20 @@ class SARCall(db.Model):
|
|||||||
class SARCategory(db.Model):
|
class SARCategory(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
name = db.Column(db.String(150), 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):
|
class SARResult(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
name = db.Column(db.String(150), 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):
|
class SARStatus(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
name = db.Column(db.String(150), 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):
|
class GPSTrack(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
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
|
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_id = db.Column(db.Integer, db.ForeignKey('sar_call.id'), nullable=False)
|
||||||
sar_call = db.relationship('SARCall', backref=db.backref('gps_tracks', lazy=True))
|
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))
|
||||||
|
37
sar_calls.py
37
sar_calls.py
@ -104,3 +104,40 @@ def delete_sar(id):
|
|||||||
flash('SAR call record deleted successfully!', 'success')
|
flash('SAR call record deleted successfully!', 'success')
|
||||||
return redirect(url_for('list_sar'))
|
return redirect(url_for('list_sar'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/add_comment/<int:sar_call_id>', 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/<int:comment_id>', 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/<int:comment_id>', 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))
|
||||||
|
@ -25,12 +25,47 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="map" class="mb-4"></div>
|
<div id="map" class="mb-4"></div>
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ url_for('edit_sar', id=sar.id) }}" class="btn btn-primary">Edit</a>
|
<a href="{{ url_for('edit_sar', id=sar.id) }}" class="btn btn-primary">Edit</a>
|
||||||
<a href="{{ url_for('delete_sar', id=sar.id) }}" class="btn btn-danger">Delete</a>
|
<a href="{{ url_for('delete_sar', id=sar.id) }}" class="btn btn-danger">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Display Comments -->
|
||||||
|
{% for comment in sar_call.comments %}
|
||||||
|
<div class="comment">
|
||||||
|
<strong>{{ comment.user.username }}</strong>:
|
||||||
|
<p>{{ comment.text }}</p>
|
||||||
|
{% if comment.gpx_data %}
|
||||||
|
<!-- Display the GPX data on the map -->
|
||||||
|
{% endif %}
|
||||||
|
{% if current_user.id == comment.user_id or current_user.id == 1 or current_user.id == sar_call.user_id %}
|
||||||
|
<a href="{{ url_for('edit_comment', comment_id=comment.id) }}">Edit</a>
|
||||||
|
<a href="{{ url_for('delete_comment', comment_id=comment.id) }}">Delete</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Add Comment Form -->
|
||||||
|
<form action="{{ url_for('add_comment', sar_call_id=sar_call.id) }}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="text">Comment:</label>
|
||||||
|
<textarea name="text" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="gpx_file">GPX File:</label>
|
||||||
|
<input type="file" name="gpx_file">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Add Comment</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var map = L.map('map').setView([{{ sar.latitude }}, {{ sar.longitude }}], 13);
|
var map = L.map('map').setView([{{ sar.latitude }}, {{ sar.longitude }}], 13);
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
Loading…
Reference in New Issue
Block a user