From 1cbe6a267628509c24d32b458363ddb74cb82838 Mon Sep 17 00:00:00 2001 From: Anson Bridges Date: Tue, 5 Nov 2024 15:38:54 -0500 Subject: MMXXIV PROGRESS --- .../__pycache__/datastructs.cpython-312.pyc | Bin 7576 -> 7899 bytes dashboard_website/__pycache__/db.cpython-312.pyc | Bin 11011 -> 11486 bytes .../__pycache__/router.cpython-312.pyc | Bin 15831 -> 15791 bytes dashboard_website/dashboard.py | 36 ++++++- dashboard_website/datastructs.py | 12 ++- dashboard_website/db.py | 23 +++- .../static/img/marker-icon-black-req.png | Bin 0 -> 6863 bytes .../static/img/marker-icon-blue-req.png | Bin 0 -> 8222 bytes .../static/img/marker-icon-green-req.png | Bin 0 -> 8170 bytes .../static/img/marker-icon-grey-req.png | Bin 0 -> 7573 bytes .../static/img/marker-icon-orange-req.png | Bin 0 -> 8191 bytes .../static/img/marker-icon-red-req.png | Bin 0 -> 8256 bytes .../static/img/marker-icon-yellow-req.png | Bin 0 -> 8194 bytes dashboard_website/static/js/dashboard.js | 117 +++++++++++++++++---- 14 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 dashboard_website/static/img/marker-icon-black-req.png create mode 100644 dashboard_website/static/img/marker-icon-blue-req.png create mode 100644 dashboard_website/static/img/marker-icon-green-req.png create mode 100644 dashboard_website/static/img/marker-icon-grey-req.png create mode 100644 dashboard_website/static/img/marker-icon-orange-req.png create mode 100644 dashboard_website/static/img/marker-icon-red-req.png create mode 100644 dashboard_website/static/img/marker-icon-yellow-req.png diff --git a/dashboard_website/__pycache__/datastructs.cpython-312.pyc b/dashboard_website/__pycache__/datastructs.cpython-312.pyc index 1436e72..ff4cf5d 100644 Binary files a/dashboard_website/__pycache__/datastructs.cpython-312.pyc and b/dashboard_website/__pycache__/datastructs.cpython-312.pyc differ diff --git a/dashboard_website/__pycache__/db.cpython-312.pyc b/dashboard_website/__pycache__/db.cpython-312.pyc index 99f804e..2172d4d 100644 Binary files a/dashboard_website/__pycache__/db.cpython-312.pyc and b/dashboard_website/__pycache__/db.cpython-312.pyc differ diff --git a/dashboard_website/__pycache__/router.cpython-312.pyc b/dashboard_website/__pycache__/router.cpython-312.pyc index 7252768..2611148 100644 Binary files a/dashboard_website/__pycache__/router.cpython-312.pyc and b/dashboard_website/__pycache__/router.cpython-312.pyc differ diff --git a/dashboard_website/dashboard.py b/dashboard_website/dashboard.py index ba29799..8ec53e0 100644 --- a/dashboard_website/dashboard.py +++ b/dashboard_website/dashboard.py @@ -172,10 +172,33 @@ def visitTeam(): # ERROR CODES: 1 = missing fields, 2 = clue doesn't exist, 3 = already marked as visited def visitGeneric(): content = request.get_json() + unvisit = False + print(content) + if "unvisit" in content: + unvisit = True + if not ('clue_name' in content): return jsonify({'status' : "ERROR 1"}) - result = db.visitClue(content['clue_name']) + result = db.visitClue(content['clue_name'], unvisit) + if result != 0: + return jsonify({'status' : f"ERROR {result}"}) + return jsonify({'status' : "OK"}) + +@app.route("/requireClue", methods=['POST']) +# Expected JSON +# {"clue_name" : xxxx, str} +# Returns JSON +# {"status" : "OK"/"ERROR XX" } +# ERROR CODES: 1 = missing fields, 2 = clue doesn't exist, 3 = already marked as visited +def requireClue(): + content = request.get_json() + print(content) + + if not ('clue_name' in content): + return jsonify({'status' : "ERROR 1"}) + + result = db.toggleClueRequired(content['clue_name']) if result != 0: return jsonify({'status' : f"ERROR {result}"}) return jsonify({'status' : "OK"}) @@ -236,11 +259,14 @@ def getLatestInfo(): @app.route("/addClue", methods=['POST']) def addClueWeb(): content = request.get_json() - + print("adding clue:", content) if not ('clue_name' in content and 'longitude' in content and 'latitude' in content and 'clue_info' in content): - return jsonify({'status' : "ERROR 1"}) - #db.addClue() - return jsonify({'status' : "OK",}) + return jsonify({'status' : "ERROR: INVALID CLUE JSON FORMAT"}) + res = db.addClue(content['clue_name'], content['clue_info'], content['latitude'], content['longitude']) + if res == 0: + return jsonify({'status' : "OK",}) + elif res == -1: + return jsonify({'status' : "CLUE NAME ALREADY EXISTS"}) # main page diff --git a/dashboard_website/datastructs.py b/dashboard_website/datastructs.py index f150825..1b4b92b 100644 --- a/dashboard_website/datastructs.py +++ b/dashboard_website/datastructs.py @@ -48,31 +48,37 @@ class Point: class Clue(Point): - def __init__(self, lat, long, name, info, status, pool_index = 0, required=False): + def __init__(self, lat, long, name, info, status, assigned_team = 0, required=False): self.longitude = long self.latitude = lat self.name = name self.info = info self.status = status # UNVISITED | VISITED | DISABLED | ASSIGNED - self.pool_inbex = pool_index + self.assigned_team = assigned_team # 0 = to be assigned algorithmically self.required = required - def visit(self): self.status = "VISITED" + def unvisit(self): + self.status = "UNVISITED" + def toggle_enable(self): if self.status == "UNVISITED" or self.status == "ASSIGNED": self.status = "DISABLED" elif self.status == "DISABLED": self.status = "UNVISITED" + def toggle_required(self): + self.required = False if self.required else True + def toJSON(self): json_dict = {'longitude' : self.longitude, 'latitude' : self.latitude, 'clue_name' : self.name.replace('"', "'"), 'clue_info' : self.info.replace('"', "'"), 'clue_status' : self.status, + 'assigned_team' : self.assigned_team, 'clue_required' : self.required} return json_dict diff --git a/dashboard_website/db.py b/dashboard_website/db.py index 6991121..7bb5a18 100644 --- a/dashboard_website/db.py +++ b/dashboard_website/db.py @@ -141,9 +141,14 @@ def getCluesJSON(timestamp): def addClue(clue_name, clue_info, longitude, latitude, status="UNVISITED"): + global clues_last_changed + for clue in clues: + if clue.name == clue_name: + return -1 # clue already exists newClue = Clue(latitude, longitude, clue_name, clue_info, status) clues.append(newClue) clues_last_changed = time.time() + return 0 def deleteClue(clue_name): @@ -155,11 +160,15 @@ def deleteClue(clue_name): break -def visitClue(clue_name): +def visitClue(clue_name, unvisit=False): global clues_last_changed for clue in clues: if clue.name == clue_name: if clue.status == "VISITED": + if unvisit: + clue.unvisit() + clues_last_changed = time.time() + return 0 return 3 # already visited clue.visit() clues_last_changed = time.time() @@ -178,6 +187,16 @@ def toggleEnableClue(clue_name): return 0 # OK return 2 # no clue +def toggleClueRequired(clue_name): + global clues_last_changed + for clue in clues: + if clue.name == clue_name: + clue.toggle_required() + clues_last_changed = time.time() + updateRoutes() + return 0 # OK + return 2 # no clue + def visitClueTeam(team_name, clue_name): global clues_last_changed @@ -287,4 +306,4 @@ def save(): csvwriter.writerow([clue.name, clue.latitude, clue.longitude, clue.info, clue.status]) -load() \ No newline at end of file +#load("all_clues.csv") \ No newline at end of file diff --git a/dashboard_website/static/img/marker-icon-black-req.png b/dashboard_website/static/img/marker-icon-black-req.png new file mode 100644 index 0000000..372b07d Binary files /dev/null and b/dashboard_website/static/img/marker-icon-black-req.png differ diff --git a/dashboard_website/static/img/marker-icon-blue-req.png b/dashboard_website/static/img/marker-icon-blue-req.png new file mode 100644 index 0000000..4ee83f5 Binary files /dev/null and b/dashboard_website/static/img/marker-icon-blue-req.png differ diff --git a/dashboard_website/static/img/marker-icon-green-req.png b/dashboard_website/static/img/marker-icon-green-req.png new file mode 100644 index 0000000..173476d Binary files /dev/null and b/dashboard_website/static/img/marker-icon-green-req.png differ diff --git a/dashboard_website/static/img/marker-icon-grey-req.png b/dashboard_website/static/img/marker-icon-grey-req.png new file mode 100644 index 0000000..7d6dde1 Binary files /dev/null and b/dashboard_website/static/img/marker-icon-grey-req.png differ diff --git a/dashboard_website/static/img/marker-icon-orange-req.png b/dashboard_website/static/img/marker-icon-orange-req.png new file mode 100644 index 0000000..55316ce Binary files /dev/null and b/dashboard_website/static/img/marker-icon-orange-req.png differ diff --git a/dashboard_website/static/img/marker-icon-red-req.png b/dashboard_website/static/img/marker-icon-red-req.png new file mode 100644 index 0000000..4fba983 Binary files /dev/null and b/dashboard_website/static/img/marker-icon-red-req.png differ diff --git a/dashboard_website/static/img/marker-icon-yellow-req.png b/dashboard_website/static/img/marker-icon-yellow-req.png new file mode 100644 index 0000000..41e8d3f Binary files /dev/null and b/dashboard_website/static/img/marker-icon-yellow-req.png differ diff --git a/dashboard_website/static/js/dashboard.js b/dashboard_website/static/js/dashboard.js index 3ff0de9..43c07f3 100644 --- a/dashboard_website/static/js/dashboard.js +++ b/dashboard_website/static/js/dashboard.js @@ -25,12 +25,27 @@ var baseIcon = L.Icon.extend({ var homeIcon = new baseIcon({iconUrl: 'static/img/marker-home.png'}), activeBikeIcon = new baseIcon({iconUrl: 'static/img/marker-bike-active.png', className:"leaflet-bike-marker"}), inactiveBikeIcon = new baseIcon({iconUrl: 'static/img/marker-bike-inactive.png'}), - visitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-grey.png'}), - unvisitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-blue.png'}), // generic, becomes colored when assigned to route - orangeIcon = new baseIcon({iconUrl: 'static/img/marker-icon-orange.png'}), - redIcon = new baseIcon({iconUrl: 'static/img/marker-icon-red.png'}), - greenIcon = new baseIcon({iconUrl: 'static/img/marker-icon-green.png'}), - yellowIcon = new baseIcon({iconUrl: 'static/img/marker-icon-yellow.png'}); + visitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-green.png'}), + disabledIcon = new baseIcon({iconUrl: 'static/img/marker-icon-black.png'}), + unvisitedIcon = new baseIcon({iconUrl: 'static/img/marker-icon-grey.png'}), // generic, becomes colored when assigned to route + visitedIconReq = new baseIcon({iconUrl: 'static/img/marker-icon-green-req.png'}), + disabledIconReq = new baseIcon({iconUrl: 'static/img/marker-icon-black-req.png'}), + unvisitedIconReq = new baseIcon({iconUrl: 'static/img/marker-icon-grey-req.png'}); // generic, becomes colored when assigned to route +var teamIcons = [ unvisitedIcon, + new baseIcon({iconUrl: 'static/img/marker-icon-orange.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-red.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-green.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-yellow.png'}) ]; +var teamIconsReq = [ disabledIconReq, + new baseIcon({iconUrl: 'static/img/marker-icon-orange-req.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-red-req.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-green-req.png'}), + new baseIcon({iconUrl: 'static/img/marker-icon-yellow-req.png'}) ]; + + team1IconReq = new baseIcon({iconUrl: 'static/img/marker-icon-orange.png'}), + team2IconReq = new baseIcon({iconUrl: 'static/img/marker-icon-red.png'}), + team3IconReq = new baseIcon({iconUrl: 'static/img/marker-icon-green.png'}), + team4Icon = new baseIcon({iconUrl: 'static/img/marker-icon-yellow.png'}); var bikeIcons = {'ACTIVE' : activeBikeIcon, 'INACTIVE' : inactiveBikeIcon} function zoomToBike(team_name){ @@ -121,41 +136,83 @@ function drawClues(){ unvisited_clues_m.clearLayers(); visited_clues_m.clearLayers(); for (var i = 0; i < clues.length; i++) { - var tempIcon = visitedIcon; - if (clues[i]['clue_status'] == "UNVISITED") tempIcon = unvisitedIcon; + var tempIcon = teamIcons[clues[i]['assigned_team']]; + if(clues[i]['clue_required']){ + tempIcon = teamIconsReq[clues[i]['assigned_team']]; + if (clues[i]['clue_status'] == "UNVISITED") tempIcon = unvisitedIconReq; + if (clues[i]['clue_status'] == "VISITED") tempIcon = visitedIconReq; + } + else{ + if (clues[i]['clue_status'] == "UNVISITED") tempIcon = unvisitedIcon; + if (clues[i]['clue_status'] == "VISITED") tempIcon = visitedIcon; + } + var popupdiv = document.createElement('p'); var toggleVisitText = clues[i]['clue_status'] == "UNVISITED" ? "Visit" : "Unvisit"; var toggleDisableText = clues[i]['clue_status'] != "DISABLED" ? "Disable" : "Enable"; - popupdiv.innerHTML = "" + clues[i]['clue_name'] + ": " + clues[i]['clue_info'] + ""; - popupdiv.innerHTML += ""; - popupdiv.innerHTML += ""; - var clueMarker = L.marker([clues[i]['latitude'], clueVisits[i]['longitude']], {icon: tempIcon}).bindPopup(popupdiv); + popupdiv.innerHTML = "" + clues[i]['clue_name'] + ": " + clues[i]['clue_info'] + "
"; + popupdiv.innerHTML += ""; + popupdiv.innerHTML += ""; + popupdiv.innerHTML += "
"; + popupdiv.innerHTML += "
Assigned team: "; + var clueMarker = L.marker([clues[i]['longitude'], clues[i]['latitude']], {icon: tempIcon}).bindPopup(popupdiv); clue_rels[clues[i]['clue_name']] = clueMarker; if (clues[i]['clue_status'] == "UNVISITED") unvisited_clues_m.addLayer(clueMarker); else visited_clues_m.addLayer(clueMarker); + popupdiv.querySelector("#assignedteam_"+clues[i]['clue_name']).selectedIndex = clues[i]['assigned_team']; } } -function toggleVisitClue(clue_name){ +function toggle_visit_clue(clue_name){ if(!confirm("Are you sure you want to visit/unvisit this clue?")) return; console.log("toggling visited status for "+clue_name); - document.getElementById("visitbutton_"+clues[i]['clue_name']).disabled = true; // temporarily disable buttons until new server frame received - document.getElementById("enablebutton_"+clues[i]['clue_name']).disabled = true; + var clue = get_clue_by_name(clue_name); + document.getElementById("visitbutton_"+clue['clue_name']).disabled = true; // temporarily disable buttons until new server frame received + document.getElementById("enablebutton_"+clue['clue_name']).disabled = true; + document.getElementById("requirebutton_"+clue['clue_name']).disabled = true; fetch(host+'/visitClueGeneric', { method: "POST", headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: JSON.stringify({ "clue_name" : clue_name })}) + body: JSON.stringify({ "clue_name" : clue_name, "unvisit": true})}) // unvisit (if appropriate) .then((response) => response.json()) .then((json) => console.log(json)); } -function toggleEnableClue(clue_name){ +function toggle_required_clue(clue_name){ + if(!confirm("Are you sure you want to mark/unmark this clue?")) return; + console.log("toggling visited status for "+clue_name); + var clue = get_clue_by_name(clue_name); + document.getElementById("visitbutton_"+clue['clue_name']).disabled = true; // temporarily disable buttons until new server frame received + document.getElementById("enablebutton_"+clue['clue_name']).disabled = true; + document.getElementById("requirebutton_"+clue['clue_name']).disabled = true; + fetch(host+'/requireClue', { + method: "POST", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ "clue_name" : clue_name, "unvisit": true})}) // unvisit (if appropriate) + .then((response) => response.json()) + .then((json) => console.log(json)); +} + +function get_clue_by_name(clue_name){ + for (var i = 0; i < clues.length; i++) + if(clues[i]['clue_name'] == clue_name) + return clues[i]; + return NaN; +} + +function toggle_enable_clue(clue_name){ + if(!confirm("Are you sure you want to enable/disable this clue?")) return; console.log("toggling enabled status for "+clue_name); - document.getElementById("visitbutton_"+clues[i]['clue_name']).disabled = true; // temporarily disable buttons until new server frame received - document.getElementById("enablebutton_"+clues[i]['clue_name']).disabled = true; + var clue = get_clue_by_name(clue_name); + document.getElementById("visitbutton_"+clue['clue_name']).disabled = true; // temporarily disable buttons until new server frame received + document.getElementById("enablebutton_"+clue['clue_name']).disabled = true; + document.getElementById("requirebutton_"+clue['clue_name']).disabled = true; fetch(host+'/enableClue', { method: "POST", headers: { @@ -168,6 +225,8 @@ function toggleEnableClue(clue_name){ } function addClue(){ + var clue_name = document.getElementById("new_clue_name").value; + var clue_info = document.getElementById("new_clue_info").value; var long = parseFloat(document.getElementById("new_clue_longitude").value); var lat = parseFloat(document.getElementById("new_clue_latitude").value); if (isNaN(long) || isNaN(lat)){ @@ -175,12 +234,28 @@ function addClue(){ return; } if(confirm("Are you sure this is the right location?")){ - console.log("yes"); + console.log("adding clue..."); + fetch(host+'/addClue', { + method: "POST", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ "clue_name" : clue_name, "clue_info" : clue_info, "latitude" : lat, "longitude" : long})}) + .then((response) => response.json()) + .then((json) => { + console.log(json); + if(json['status'] != "OK") + alert(json['status']); + else + alert("Clue added successfully."); + }); } } // run every x seconds to get latest info from server function requestLatestInfo(){ + console.log("requesting update"); function handleLatestInfo(json){ if(json['status'] != "OK") return; latest_timestamp = json['timestamp']; @@ -278,7 +353,7 @@ window.onload = function() { }).addTo(previewmap); //homemarker = L.marker([homebase['latitude'], homebase['longitude']], {icon: homeIcon}).addTo(map).bindPopup("Home is where the club is."); - previewmarker = L.marker([0,0], {icon: greenIcon}).addTo(previewmap); + previewmarker = L.marker([0,0], {icon: visitedIcon}).addTo(previewmap); map.addLayer(visited_clues_m); map.addLayer(unvisited_clues_m); map.addLayer(bikes_m); -- cgit v1.2.3