pannenkoekenwachtrij/src/main.v
2023-12-07 21:53:44 +01:00

312 lines
6.6 KiB
V

module main
import vweb
import db.sqlite
import time
import net.http
import arrays
import maps
import math.stats
const http_port = 8080
enum Status {
besteld = 0
bakken = 1
klaar = 2
afgegeven = 3
}
pub fn (s Status) str() string {
return match s {
.besteld { 'Pannenkoek besteld' }
.bakken { 'Pannenkoek aan het bakken' }
.klaar { 'Pannenkoek klaar' }
.afgegeven { 'Pannenkoek afgegeven' }
}
}
struct Person {
id int @[primary; sql: serial]
status int
name string
remark string
order_time time.Time
delivery_time time.Time
}
pub fn (p Person) str() string {
return 'Person[id:${p.id}, name: ${p.name}, status:${p.status}, time:${p.order_time}, end:${p.delivery_time}]'
}
pub fn (p Person) order_time_humanized() string {
return p.order_time.relative()
}
pub fn (p Person) remark() string {
return if p.remark.len > 0 {
'(${p.remark})'
} else {
''
}
}
pub fn (p Person) status_str() string {
unsafe {
s := Status(p.status)
return s.str()
}
}
// === Database ===
pub fn create_db_connection() !sqlite.DB {
return sqlite.connect('pancakes.db')!
}
fn (mut app App) get_people() ![]Person {
status_filter := int(Status.afgegeven)
people := sql app.db {
select from Person where status < status_filter
}!
return people
}
fn (mut app App) get_finished_count() !int {
people := sql app.db {
select from Person where status == 3
}!
return people.len
}
struct PerHour {
t time.Time
label string
amount int
percentage int
color string
}
fn (mut app App) get_all() ![]Person {
return sql app.db {
select from Person order by id desc
}!
}
fn (mut app App) get_last_delivered() ![]Person {
return sql app.db {
select from Person order by delivery_time desc limit 1
}!
}
fn (mut app App) get_finished_per_hour() ![]PerHour {
people := sql app.db {
select from Person where status == 3
}!
grouped := arrays.group_by(people, fn (p Person) string {
return '${p.order_time.hour}:${int(p.order_time.minute / 30) * 30} ${p.order_time.day}/${p.order_time.month}'
})
max_per_hour := arrays.max(grouped.values().map(it.len)) or { 1 } + 10
mut grouped_arr := maps.to_array(grouped, fn [max_per_hour] (k string, v []Person) PerHour {
return PerHour{
t: v[0].order_time
label: k
amount: v.len
percentage: int(v.len * 100 / max_per_hour)
color: (if v[0].order_time.hour % 2 == 0 { 'green' } else { 'red' })
}
})
grouped_arr.sort(a.t < b.t)
return grouped_arr
}
fn (mut app App) get_ppu() !f64 {
mut people := sql app.db {
select from Person where status == 3
}!
if people.len == 0 {
return 0
}
people.sort(a.order_time < b.order_time)
time_range := people.last().order_time - people.first().order_time
return people.len / time_range.hours()
}
fn (mut app App) get_mean_time_between_pannenkoeken() !time.Duration {
time_zero := time.Time{
unix: 0
}
mut people := sql app.db {
select from Person where (status == 3 && delivery_time > time_zero) order by delivery_time desc limit 10
}!
return stats.mean(arrays.window(people, size: 2).map(it[0].delivery_time - it[1].delivery_time))
}
fn (mut app App) get_last_done_person() ![]Person {
people := sql app.db {
select from Person where status == 3 order by delivery_time desc limit 1
}!
return people
}
fn (mut app App) get_next_person() ![]Person {
people := sql app.db {
select from Person where status < 3 order by id limit 1
}!
return people
}
fn (mut app App) do_status_update(user_id int) !Person {
people := sql app.db {
select from Person where id == user_id
}!
person := people.first()
sql app.db {
update Person set status = person.status + 1 where id == person.id
}!
if person.status == 2 {
sql app.db {
update Person set delivery_time = time.now() where id == person.id
}!
}
return person
}
fn (mut app App) do_add_person(name string, remark string) ! {
people := sql app.db {
select from Person where name == name && status < 3
}!
if people.len == 0 {
p := Person{
status: 0
order_time: time.now()
name: name
remark: remark
}
sql app.db {
insert p into Person
}!
}
}
// === WEB ===
struct App {
vweb.Context
mut:
db sqlite.DB
}
pub fn (mut app App) before_request() {
println('[Vweb] ${app.Context.req.method} ${app.Context.req.url}')
}
fn main() {
println('Start 🥞 webserver')
mut db := create_db_connection() or { panic(err) }
sql db {
create table Person
} or { panic('error on create table: ${err}') }
// db.close() or { panic(err) }
vweb.run(&App{
db: db
}, http_port)
}
@['/'; get]
pub fn (mut app App) home() vweb.Result {
people := app.get_people() or {
app.set_status(400, '')
return app.text('${err}')
}
person_finished_count := app.get_finished_count() or {
app.set_status(400, '')
return app.text('${err}')
}
finished_per_hour := app.get_finished_per_hour() or {
app.set_status(400, '')
return app.text('${err}')
}
// pannenkoek per uur
ppu := app.get_ppu() or {
app.set_status(400, '')
return app.text('${err}')
}
all_people := app.get_all() or {
app.set_status(400, '')
return app.text('${err}')
}
mean_time := app.get_mean_time_between_pannenkoeken() or {
app.set_status(400, '')
return app.text('${err}')
}
mut last_delivered := app.get_last_delivered() or {
app.set_status(400, '')
return app.text('${err}')
}
if last_delivered.len == 0 {
last_delivered << Person{
delivery_time: time.now()
}
}
// Time to pannenkoek
time_since_last := (time.now() - last_delivered.first().delivery_time)
ttp := time.unix(i64(mean_time.seconds() - time_since_last.seconds())) - time.unix(0)
return $vweb.html()
}
@['/banner'; get]
pub fn (mut app App) banner() vweb.Result {
last_done := app.get_last_done_person() or {
app.set_status(400, '')
return app.text('${err}')
}
next_person := app.get_next_person() or {
app.set_status(400, '')
return app.text('${err}')
}
now := time.now()
return $vweb.html()
}
@['/status_update'; post]
pub fn (mut app App) status_update() vweb.Result {
if person := app.do_status_update(app.form['id'].int()) {
if person.status == 1 {
spawn fn () {
http.post('http://10.1.0.224:8080/blink', '') or {}
}()
spawn fn (name string) {
http.post('http://10.1.2.3', 'ScrollingText >>> ${name} <<< Enjoy! ') or {}
http.post('http://10.1.2.3', 'Option text_trailingWhitespace 1') or {}
}(person.name)
}
}
return app.redirect('/')
}
@['/add_person'; post]
pub fn (mut app App) add_person() vweb.Result {
name := app.form['name']
if name.len == 0 {
return app.redirect('/')
}
app.do_add_person(app.form['name'], app.form['remark']) or {
app.set_status(400, '')
return app.text('${err}')
}
return app.redirect('/')
}