4545from flask_migrate import Migrate , stamp , upgrade
4646from flask_cors import CORS , cross_origin
4747from s3 import S3Bucket
48- from typing import Optional
48+ from typing import Optional , Union
4949import shutil
5050import pandas as pd
5151import json_log_formatter
5252from pathlib import Path
5353from dotenv import load_dotenv
54- from helpers import floor_to_integer , RoomNumber , integer_to_floor , MapLocation , ServiceNowStatus , ServiceNowUpdateType , save_user_details , check_for_admin_role , get_logged_in_user_id , get_logged_in_user
54+ from helpers import floor_to_integer , RoomNumber , integer_to_floor , MapLocation , ServiceNowStatus , ServiceNowUpdateType , save_user_details , check_for_admin_role , get_logged_in_user_id , get_logged_in_user , get_logged_in_user_info
5555from urllib .parse import quote_plus , urlencode
5656from authlib .integrations .flask_client import OAuth
5757
@@ -249,6 +249,24 @@ def access_point_json(access_point: AccessPoint):
249249 })
250250 return base_data
251251
252+ def access_point_admin_json (access_point : AccessPoint ):
253+
254+ status = get_item_status (access_point )
255+ report = get_item_report (access_point )
256+
257+ status_categories = {i .name : i .value for i in StatusType }
258+
259+
260+ # TODO: use marshmallow to serialize
261+ admin_data = {
262+ "status_ticket_number" : (status .report .ref or "No Ticket" ) if status else "No Status" ,
263+ "status_report_id" : status .report .id ,
264+ "report_id" : report .id ,
265+ "status_categories" : status_categories
266+ }
267+
268+ return admin_data
269+
252270
253271"""
254272Creates a geojson for map feature
@@ -597,7 +615,7 @@ def import_data(file):
597615"""
598616
599617
600- def getAccessPoint (id ):
618+ def getAccessPoint (id , is_admin = False ):
601619 access_point = db .session .execute (
602620 db .select (AccessPoint ).where (AccessPoint .id == id )
603621 ).scalar ()
@@ -608,6 +626,8 @@ def getAccessPoint(id):
608626 return None
609627
610628 accessPointInfo = access_point_json (access_point )
629+ if is_admin :
630+ accessPointInfo .update (access_point_admin_json (access_point ))
611631 logging .debug (accessPointInfo )
612632 return accessPointInfo
613633
@@ -910,15 +930,18 @@ def tags():
910930
911931@app .route ("/access_points/<id>" )
912932def access_point (id ):
913- if checkAccessPointExists (id ):
914- return render_template (
915- "access_point.html" ,
916- authsession = get_logged_in_user (),
917- is_admin = check_for_admin_role (get_logged_in_user_id ()),
918- accessPointDetails = getAccessPoint (id )
919- )
920- else :
933+ if not checkAccessPointExists (id ):
921934 return render_template ("404.html" ), 404
935+
936+ is_admin = check_for_admin_role (get_logged_in_user_id ())
937+ return render_template (
938+ "access_point.html" ,
939+ authsession = get_logged_in_user (),
940+ is_admin = is_admin ,
941+ accessPointDetails = getAccessPoint (id , is_admin = is_admin )
942+
943+ )
944+
922945
923946
924947########################
@@ -1138,6 +1161,53 @@ def add_ticket(item_id):
11381161
11391162 return ("" , 200 )
11401163
1164+
1165+ @app .route ("/add_status/<item_id>" , methods = ["POST" ])
1166+ @requires_admin
1167+ def add_status (item_id ):
1168+ if not checkAccessPointExists (item_id ):
1169+ return "Not found" , 404
1170+
1171+ status_text = request .form .get ("status" )
1172+ note_text = request .form .get ("note" )
1173+ category = StatusType (int (request .form .get ("category" )))
1174+ author = get_logged_in_user_info ()
1175+ author_identifier = f" - manually added by { author ['name' ]} ({ author ['sub' ]} ) via web UI"
1176+
1177+ note_text += author_identifier
1178+
1179+ prior_report = get_item_report (item_id )
1180+ current_report = None
1181+
1182+ previous_report_has_ticket = prior_report .ref is not None
1183+
1184+ if previous_report_has_ticket or category == StatusType .BROKEN :
1185+ current_report = Report ()
1186+ db .session .add (current_report )
1187+ db .session .flush ()
1188+
1189+ # associate new report with this access point
1190+ association = AccessPointReports (
1191+ report_id = current_report .id ,
1192+ access_point_id = item_id
1193+ )
1194+ db .session .add (association )
1195+
1196+ else :
1197+ current_report = prior_report
1198+
1199+ status_update = Status (
1200+ report = current_report ,
1201+ status = status_text ,
1202+ status_type = category ,
1203+ timestamp = datetime .utcnow (),
1204+ notes = note_text
1205+ )
1206+ db .session .add (status_update )
1207+ db .session .commit ()
1208+
1209+ return redirect (f"/access_points/{ item_id } " )
1210+
11411211########################
11421212#
11431213# region Management Helpers
@@ -1225,25 +1295,48 @@ def associate_thumbnail(file_hash, thumbnail_file, item_identifier):
12251295 db .session .commit ()
12261296
12271297
1228- def get_item_status (item ):
1229- """Fetch the status for the provided item.
1298+ def get_item_status (item : Union [ AccessPoint , int ] ):
1299+ """Fetch the most recent status for the provided item.
12301300
12311301 Args:
1232- item (AccessPoint): The item (in this case AccessPoint) to fetch status for
1302+ item (Union[ AccessPoint, int] ): The item (in this case AccessPoint) to fetch status for (or its integer ID)
12331303
12341304 Returns:
12351305 Status: the status of the access point, or None if none were found
12361306 """
1237-
1307+ item_id = item . id if isinstance ( item , AccessPoint ) else item
12381308 status = db .session .execute (
12391309 db .select (Status )
12401310 .join (AccessPointReports , AccessPointReports .report_id == Status .report_id )
1241- .where (AccessPointReports .access_point_id == item . id )
1311+ .where (AccessPointReports .access_point_id == item_id )
12421312 .order_by (Status .timestamp .desc ())
12431313 ).scalars ().first ()
12441314
12451315 return status
12461316
1317+ def get_item_report (item :Union [AccessPoint , int ]):
1318+ """Fetch the latest report for the provided item.
1319+
1320+ While you can get this using get_item_status and accessing it through the associated report,
1321+ that method can miss scenarios where a report has been created but there is no status yet
1322+ This can happen when associating a ticket before any email has come in yet.
1323+
1324+ Args:
1325+ item (Union[AccessPoint, int]): The item (in this case AccessPoint) to fetch the report for (or its integer ID)
1326+
1327+ Returns:
1328+ Report: the report of the access point, or None if none were found
1329+ """
1330+ item_id = item .id if isinstance (item , AccessPoint ) else item
1331+ report = db .session .execute (
1332+ db .select (Report )
1333+ .join (AccessPointReports , AccessPointReports .report_id == Report .id )
1334+ .where (AccessPointReports .access_point_id == item_id )
1335+ .order_by (Report .id .desc ())
1336+ ).scalars ().first ()
1337+
1338+ return report
1339+
12471340
12481341"""
12491342Delete tag and all relations from DB
0 commit comments