Added departure filter

This commit is contained in:
Filip Znachor 2022-12-20 19:29:51 +01:00
parent 3fce82e971
commit ee7bf407b2
7 changed files with 79 additions and 20 deletions

View file

@ -79,6 +79,15 @@ class API:
d.set_stop(request.forms.get('stop_id')) d.set_stop(request.forms.get('stop_id'))
return {'success': True} return {'success': True}
@route('/admin/devices/change_filter', method='POST')
def admin_devices_change_filter():
if main.config["admin"]["secret"] != request.forms.get('secret'):
return {'error': "unauthorized"}
for i, d in enumerate(main.controller.devices):
if str(i) == request.forms.get('index'):
d.set_filter(request.forms.get('filter'))
return {'success': True}
@route('/admin/devices/toggle', method='POST') @route('/admin/devices/toggle', method='POST')
def admin_devices_toggle(): def admin_devices_toggle():
if main.config["admin"]["secret"] != request.forms.get('secret'): if main.config["admin"]["secret"] != request.forms.get('secret'):

View file

@ -2,6 +2,38 @@ from math import floor
from datetime import datetime from datetime import datetime
from dateutil import parser from dateutil import parser
class Filter:
storage = dict()
@staticmethod
def get(name: str):
if name in Filter.storage:
return Filter.storage[name]
return None
def __init__(self, name: str, filters):
if "lines" not in filters:
filters["lines"] = None
if "types" not in filters:
filters["types"] = None
if "destinations" not in filters:
filters["destinations"] = None
self.name = name
self.lines = filters["lines"]
self.types = filters["types"]
self.destinations = filters["destinations"]
Filter.storage[name] = self
def filter(self, departures):
results = []
for d in departures:
if not self.types or d.type in self.types:
if not self.lines or d.line in self.lines:
if not self.destinations or d.last_stop in self.destinations:
results.append(d)
return results
class Departure: class Departure:
id_pool = {} id_pool = {}

View file

@ -7,7 +7,7 @@ from base64 import b64encode
from datetime import datetime from datetime import datetime
from random import randint, shuffle from random import randint, shuffle
from time import sleep from time import sleep
from departures import Departure from departures import Departure, Filter
import urllib3 import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@ -34,22 +34,23 @@ class LoraController:
data = requests.post(url, verify=False, headers=headers, data=json.dumps(data)).json() data = requests.post(url, verify=False, headers=headers, data=json.dumps(data)).json()
self.token = data["token"] self.token = data["token"]
def new(self, id: int, stop_id: str, enabled: bool): def new(self, id: int, stop_id: str, enabled: bool, filter: Filter):
self.devices.append(LoraDevice(self, id, stop_id, enabled)) self.devices.append(LoraDevice(self, id, stop_id, enabled, filter))
def json(self): def json(self):
resp = [] resp = []
for d in self.devices: for d in self.devices:
resp.append({'id': f"{d.id:0>16x}", 'stop_id': d.stop_id, 'enabled': d.enabled}) resp.append({'id': f"{d.id:0>16x}", 'stop_id': d.stop_id, 'enabled': d.enabled, 'filter': d.filter.name if d.filter else None})
return resp return resp
class LoraDevice: class LoraDevice:
def __init__(self, controller: LoraController, deveui: int, stop_id: str, enabled: bool): def __init__(self, controller: LoraController, deveui: int, stop_id: str, enabled: bool, filter: Filter):
self.controller = controller self.controller = controller
self.id = deveui self.id = deveui
self.stop_id = stop_id self.stop_id = stop_id
self.enabled = enabled self.enabled = enabled
self.filter = filter
self.message_pool = [] self.message_pool = []
self.thread = None self.thread = None
self.port = 1 self.port = 1
@ -59,11 +60,17 @@ class LoraDevice:
self.clear() self.clear()
self.stop_id = stop_id self.stop_id = stop_id
def set_filter(self, name: str):
self.filter = Filter.get(name)
def get_updated_departures(self): def get_updated_departures(self):
if not self.enabled: if not self.enabled:
return return
self.send_time() self.send_time()
for d in Departure.get(self.stop_id): departures = Departure.get(self.stop_id)
if self.filter:
departures = self.filter.filter(departures)
for d in departures:
if self.id not in d.updated or d.updated[self.id] > 0: if self.id not in d.updated or d.updated[self.id] > 0:
self.send(d.data) self.send(d.data)

View file

@ -1,4 +1,4 @@
from departures import Departure from departures import Departure, Filter
from requests.structures import CaseInsensitiveDict from requests.structures import CaseInsensitiveDict
import json import json
import requests import requests
@ -24,11 +24,15 @@ class Main:
self.controller = lora_controller self.controller = lora_controller
self.api = API(self) self.api = API(self)
lora_controller.generate_token() lora_controller.generate_token()
for name in self.config["filters"]:
Filter(name, self.config["filters"][name])
for d in self.config["devices"]: for d in self.config["devices"]:
if "stop_id" in d: if "stop_id" in d:
if "enabled" not in d: if "enabled" not in d:
d["enabled"] = True d["enabled"] = True
lora_controller.new(d["id"], str(d["stop_id"]), d["enabled"]) if "filter" not in d:
d["filter"] = None
lora_controller.new(d["id"], str(d["stop_id"]), d["enabled"], Filter.get(d["filter"]))
self.thread = threading.Thread(target=self.update_loop) self.thread = threading.Thread(target=self.update_loop)
self.thread.start() self.thread.start()

View file

@ -3,11 +3,13 @@
.cards .header {padding: 15px; display: grid; grid-template-columns: 1fr max-content; user-select: none;} .cards .header {padding: 15px; display: grid; grid-template-columns: 1fr max-content; user-select: none;}
.cards .first {font-weight: 700;} .cards .first {font-weight: 700;}
.cards .settings {padding: 15px; border-top: 1px solid var(--border-color2); background: var(--alt-bg2); display: none;} .cards .settings {padding: 15px; border-top: 1px solid var(--border-color2); background: var(--alt-bg2); display: none; flex-direction: column; gap: 13px;}
.cards .settings.visible {display: block;} .cards .settings.visible {display: flex;}
.cards .actions {display: flex; gap: 10px; flex-wrap: wrap; align-items: center;} .cards .actions {display: flex; gap: 10px; flex-wrap: wrap; align-items: center;}
.cards .actions::before {content: "Akce:"; font-weight: 500; font-size: 90%; opacity: .5; text-transform: uppercase;} .cards .actions::before, .cards .filter::before {content: "Akce:"; font-weight: 500; font-size: 90%; opacity: .5; text-transform: uppercase;}
.cards .filter::before {content: "Aktivní filtr:"; margin-right: 10px;}
@media screen and (max-width: 500px) { @media screen and (max-width: 500px) {
.cards .actions::before {display: none;} .cards .actions::before {display: none;}
} }

View file

@ -18,12 +18,14 @@
<div class="second">{{ stops[d.stop_id] ? stops[d.stop_id].name : "Nenastaveno" }}</div> <div class="second">{{ stops[d.stop_id] ? stops[d.stop_id].name : "Nenastaveno" }}</div>
</div> </div>
<div class="settings" :class="{ visible: d.visible }"> <div class="settings" :class="{ visible: d.visible }">
<div class="filter">{{ d.filter ? d.filter : "žádný" }}</div>
<div class="actions"> <div class="actions">
<button @click="device_clear(i)">Vymazat</button> <button @click="device_clear(i)">Vymazat</button>
<button @click="device_resend(i)" v-if="d.enabled">Přeposlat</button>
<button @click="device_change_stop(i)">Změnit zastávku</button>
<button @click="device_toggle(i, false)" v-if="d.enabled">Deaktivovat</button> <button @click="device_toggle(i, false)" v-if="d.enabled">Deaktivovat</button>
<button @click="device_toggle(i, true)" v-else>Aktivovat</button> <button @click="device_toggle(i, true)" v-else>Aktivovat</button>
<button @click="device_resend(i)" v-if="d.enabled">Přeposlat</button>
<button @click="device_change_stop(i)">Zastávka</button>
<button @click="device_change_filter(i)">Filtr</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -3,10 +3,8 @@ const { createApp } = Vue;
let app = createApp({ let app = createApp({
data() { data() {
return { return {
// logged: false, logged: false,
// secret: "", secret: "",
logged: true,
secret: "TajneHeslo",
stops: {}, stops: {},
devices: [] devices: []
} }
@ -14,11 +12,11 @@ let app = createApp({
methods: { methods: {
async update() { async update() {
let devices = await api("/admin/devices", {secret: this.secret}); let devices = await api("/admin/devices", {secret: this.secret});
let stops = await api("/stops");
if(devices.error) { if(devices.error) {
alert("Neplatné heslo!"); alert("Neplatné heslo!");
return; return;
} }
let stops = await api("/stops");
this.devices = devices.devices; this.devices = devices.devices;
this.stops = stops.stops; this.stops = stops.stops;
this.logged = true; this.logged = true;
@ -38,6 +36,12 @@ let app = createApp({
if(!stop_id || stop_id.trim() == "") return; if(!stop_id || stop_id.trim() == "") return;
this.devices[index].stop_id = stop_id; this.devices[index].stop_id = stop_id;
await api("/admin/devices/change_stop", {index, stop_id, secret: this.secret}); await api("/admin/devices/change_stop", {index, stop_id, secret: this.secret});
},
async device_change_filter(index) {
let filter = prompt("Zadejte jméno filtru:");
if(!filter || filter.trim() == "") return;
this.devices[index].filter = filter;
await api("/admin/devices/change_filter", {index, filter, secret: this.secret});
} }
} }
}).mount('#app'); }).mount('#app');
@ -63,5 +67,4 @@ async function api(url, data) {
} }
xhr.send(str.join("&")); xhr.send(str.join("&"));
}); });
} }
app.update();