1

Compare commits

...

70 Commits

Author SHA1 Message Date
ventilaar
305dad62a1 AI lesopdrachten toevoegen 2021-11-25 14:25:37 +01:00
Ventilaar
5f587f1f49 typfout 2021-01-19 21:07:36 +01:00
Ventilaar
f23f75520b tijd geven aan de servo om te draiien 2021-01-19 18:34:22 +01:00
Ventilaar
25e0f26e3c preventief mensen waarschuwen voor poll_sensor block 2021-01-19 17:53:56 +01:00
Ventilaar
16c75bcecb domme fouten man 2021-01-19 17:41:28 +01:00
Ventilaar
9909cc5c86 int doorgeven naar set_servo 2021-01-19 17:38:33 +01:00
Ventilaar
a6c0888b58 button heeft pulldown resistor om geest triggers te voorkomen 2021-01-19 16:38:22 +01:00
Ventilaar
67b93513b5 test dit 2021-01-19 16:34:06 +01:00
Ventilaar
5309692d89 letstrip feller gemaakt 2021-01-19 16:21:38 +01:00
Ventilaar
42b696834f a 2021-01-19 16:18:48 +01:00
Ventilaar
666b66ca72 ok de led strip hoort nu perfect te werken 2021-01-19 16:17:10 +01:00
Ventilaar
cdbfa40f4b ok de hardware probleem is nu echt fixed 2021-01-19 15:44:12 +01:00
Ventilaar
249ee1910f staat led opslaan in string voor de browser 2021-01-19 15:40:23 +01:00
Ventilaar
c1d35c6d56 readme python3 2021-01-19 15:37:55 +01:00
Ventilaar
449b97b757 fix led hex(string) to hex(int) 2021-01-19 15:35:05 +01:00
Ventilaar
84467c00d0 hardware.py hernoemd naar hw_funcs omdat de pi het niet zo leuk vind 2021-01-19 15:23:55 +01:00
Ventilaar
f73c20ad3d ledstrip ontvangen string omzetten naar hex 2021-01-19 15:08:02 +01:00
Ventilaar
aa0382f8c5 spi doc 2021-01-19 15:01:52 +01:00
Ventilaar
dd7033af64 pinnen van ledstrip genoteerd 2021-01-19 15:00:47 +01:00
Ventilaar
d0f54a73d2 carousel werkt in app html 2021-01-19 14:21:42 +01:00
Ventilaar
c60f2a3927 mediaan van prijzen genereren 2021-01-19 13:35:32 +01:00
Ventilaar
1e6f63cfbe readme aangepast 2021-01-18 21:02:15 +01:00
Ventilaar
4a48535dfc default pinnen aangepast naar iets acceptabels 2021-01-18 20:33:36 +01:00
Ventilaar
a082c7a038 al het hardware hoort in principe te werken 2021-01-18 20:30:03 +01:00
Ventilaar
696ecc02ca theoretisch gezien hoort al het hardware behalve het schuifregister te werken 2021-01-18 20:17:37 +01:00
Ventilaar
56051b886d afstand sensor werkt 2021-01-18 18:55:41 +01:00
Ventilaar
7406ef157f pushbutton api geimplementeerd en een rpi check toegevoegd voor de html pagina 2021-01-18 18:38:46 +01:00
Ventilaar
b2a397d4f8 pushbutton functie werkt 2021-01-18 18:38:14 +01:00
Ventilaar
f48e9309b9 debug print toegevoegd voor ledstrip 2021-01-18 18:07:45 +01:00
Ventilaar
80d6bf0061 noway de letstrip werkt 2021-01-18 18:05:35 +01:00
Ventilaar
55a217a128 heel wat dingen aangepast 2021-01-18 15:47:23 +01:00
Ventilaar
f2cc8ca5cc functies aangepast zodat het in een web omgeving kan draaien 2021-01-18 14:15:17 +01:00
Ventilaar
80e3ddb568 refactored variables 2021-01-18 14:04:13 +01:00
Ventilaar
83838731d9 remove colorpicker 2021-01-18 14:02:19 +01:00
Ventilaar
dd8f489961 formatted hardware.py en variabelen globaal bovenaan gezet voor overzichtelijkheid 2021-01-18 13:59:02 +01:00
Ventilaar
86f0c2df5b hardware.py toegevoegd maar moet nog aangepast worden om normaal aan te sturen 2021-01-18 13:48:45 +01:00
Ventilaar
72e10cc148 basic formatting 2021-01-18 13:47:40 +01:00
Ventilaar
6530a57d5a apa102 library toegevoegd 2021-01-18 13:47:28 +01:00
Ventilaar
b2da49888c kleur kiezer toevoegen aan assets 2021-01-18 13:25:41 +01:00
Ventilaar
c4d50a3581 statistieken nav weggehaald en hardware layout aangepast 2021-01-18 13:00:48 +01:00
Ventilaar
e3f97c35ad steam_api gedocumenteerd 2021-01-18 13:00:20 +01:00
Ventilaar
34c1b5c120 commentaar toegevoegd 2021-01-18 12:08:04 +01:00
Ventilaar
457269fc01 commentaar toegevoegd 2021-01-18 12:04:55 +01:00
Ventilaar
3d52e89df1 idk 2021-01-18 12:02:59 +01:00
Ventilaar
c367efa285 api voor hardware toegevoegd, statistics weg gehaald, en hardware pagina aangepast 2021-01-18 11:57:39 +01:00
Ventilaar
46aaa6a0d0 toch geen matplotlib gebruiken omdat de grafieken er maar niet goed uit zien(ik ben geen designer bro) 2021-01-18 11:32:20 +01:00
Ventilaar
89a4a680bf route toegevoegd voor hardware 2021-01-16 19:45:27 +01:00
Ventilaar
052a4804bd mini tabel met data toegevoegd 2021-01-16 19:44:06 +01:00
Ventilaar
357daf3b4f hardware.html toegevoegd en title aangepast 2021-01-16 19:43:57 +01:00
Ventilaar
98ce2706aa aaaaa veranderd naar lorem ipsum 2021-01-16 19:09:19 +01:00
Ventilaar
85e5ee5e57 steam foto gebruiken ipv login form 2021-01-16 18:57:55 +01:00
Ventilaar
965a15f2a4 staem fotos toegevoegd 2021-01-16 18:55:06 +01:00
Ventilaar
9f61ce84df stats.popgenre() aanroepen 2021-01-16 18:51:56 +01:00
Ventilaar
2b0b2c846f functie om de populairste genre op tevragen toegevoegd 2021-01-16 18:51:33 +01:00
Ventilaar
ec70c7ee97 appid in tabel klikbaar maken 2021-01-16 18:50:31 +01:00
Ventilaar
ebcebbaaaa fulltable aangepast zodat maximale regels werken 2021-01-16 18:38:52 +01:00
Ventilaar
aaf752fbbb variabelen na het laden van de json vrijmaken voor minder memory usage 2021-01-16 18:33:49 +01:00
Ventilaar
b20fbe0dc8 app hyperlinking toegevoegd in search pagina 2021-01-16 18:33:18 +01:00
Ventilaar
3e74ad4b26 html template voor de app data, dit moet nog gemaakt en mooi gemaakt worden 2021-01-14 20:37:13 +01:00
Ventilaar
77797e5066 app wildcard route toegevoegd, zodat we applicatie data kunnen inzien 2021-01-14 20:36:55 +01:00
Ventilaar
6df8d3cc0f add jquery 2021-01-14 20:31:17 +01:00
Ventilaar
a770fe82ab add bootstrap javascript files 2021-01-14 20:23:09 +01:00
Ventilaar
717de0b5bf voorbeeld response aangepast naar engels. en de request paramater was verkeerd 2021-01-14 19:52:25 +01:00
Ventilaar
89735445be steam api aangepast van library naar echte url 2021-01-14 19:32:34 +01:00
Ventilaar
f3ccf9d32d commented out steam, and added requests dependency 2021-01-14 19:01:35 +01:00
Ventilaar
5a4fa0b78f stats aangepast op basis van de laatste versie fa3 2021-01-14 09:41:46 +01:00
Ventilaar
37d5ec1155 idk what i changed 2021-01-13 12:02:59 +01:00
Ventilaar
c3c2564418 todo: implement pagination buttons in the html file 2021-01-12 21:16:00 +01:00
Ventilaar
cb36718655 pagination for big table 2021-01-12 20:53:47 +01:00
Ventilaar
e796777ec6 cleanup 2021-01-12 20:42:07 +01:00
31 changed files with 20203 additions and 125 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Oriëntatie op AI
Practicum 2: algoritmiek
(c) 2019 Hogeschool Utrecht
Tijmen Muller (tijmen.muller@hu.nl)
Opdracht: beantwoord onderstaande vragen en werk onderstaande functies uit.
Je kunt je functies testen met het gegeven raamwerk door het bestand
uit te voeren (of met behulp van pytest, als je weet hoe dat werkt).
Lever je werk in op Canvas als alle tests slagen.
Let op! Het is niet toegestaan om bestaande modules te importeren en te
gebruiken, zoals `math` en `statistics`.
"""
# Vul hier je naam, klas en studentnummer in
naam = "Taha Genc"
klas = "TICT-PV-SG5-20"
studentnummer = 1788230
"""
1. Sorteeralgoritme
Hieronder staat de pseudocode van een sorteeralgoritme:
1. Startend vanaf het begin van een lijst,
vergelijk elk element met zijn volgende buur.
2. Als het element groter is dan zijn volgende buur,
verwissel ze van plaats.
3. Doorloop zo de lijst tot het eind.
4. Als er verwisselingen zijn geweest bij stap 2., ga naar stap 1.
1a. Handmatig toepassen
Gegeven is de lijst l = [ 4, 3, 1, 2 ]. Geef de waardes die deze
lijst aanneemt bij álle tussenstappen bij toepassing van
bovenstaand sorteeralgoritme.
0 = [ 4, 3, 1, 2 ]
1 = [ 3, 4, 1, 2 ]
2 = [ 3, 1, 4, 2 ]
3 = [ 1, 3, 4, 2 ]
4 = [ 1, 3, 2, 4 ]
5 = [ 1, 2, 3, 4 ]
1b. Implementatie
Implementeer het sorteeralgoritme in Python in een functie
hieronder genaamd my_sort(lst).
1c. Best en worst case
- Stel je hebt een lijst met de waarden 1, 2 en 3. Bij welke
volgorde van de waarden in de lijst is het sorteeralgoritme
het snelste klaar (best-case scenario)?
Hoeveel vergelijkingen (zoals beschreven in stap 1. van de
pseudocode) zijn nodig geweest?
bij een array van [1,2,3] zijn er minimaal 2 vergelijkingen nodig
- Bij welke volgorde van de waarden in de lijst is het
sorteeralgoritme het minst snel klaar (worst-case scenario)?
Hoeveel vergelijkingen zijn nodig geweest?
bij een array van [3,2,1] zijn er maximaal 3 vergelijkingen nodig
- Stel je hebt een lijst met de waarden 1 tot en met 4.
Wat is nu het best-case scenario? [1,2,3,4]
Hoeveel vergelijkingen zijn er nodig? 3 vergelijkingen
En wat is nu het worst-case scenario? [4,3,2,1]
Hoeveel vergelijkingen zijn er nodig? 7 vergelijkingen
- Stel je hebt een lijst met de waarden 1 tot en met n
(je weet nu dus niet precies hoeveel waarden er in de lijst
zitten, het zijn er 'n').
Wat is nu het best-case scenario? een gesorteerde lijst of n=1
Hoeveel vergelijkingen zijn er nodig? n-1
En wat is nu het worst-case scenario? gesorteerd in de verkeerde richting
Hoeveel vergelijkingen zijn er nodig? 1/2 * n + 1
2. Recursie
2a. Lineair zoeken
Implementeer het lineair zoekalgoritme in Python op een
*recursieve* manier. Gebruik hiervoor de functie genaamd
linear_search_recursive(lst, target).
2b. Binair zoeken
Implementeer het binair zoekalgoritme in Python op een
*recursieve* manier. Gebruik hiervoor de functie genaamd
binary_search_recursive(lst, target).
"""
def my_sort(lst):
"""
Sorteer gegeven lijst lst volgens het algoritme zoals beschreven in de pseudocode bij 1. hierboven.
Zorg dat de gegeven lijst niet verandert, maar geef een nieuwe, gesorteerde variant van de lijst terug.
"""
lst_sorted = lst.copy() # kopie maken van ingegeven list
for _ in range(len(lst) - 1):
for x in range(len(lst)-1):
if lst_sorted[x] > lst_sorted[x+1]:
lst_sorted[x], lst_sorted[x+1] = lst_sorted[x+1], lst_sorted[x]
return lst_sorted
def linear_search_recursive(lst, target):
"""
Zoek een element target in gegeven lijst lst door middel van recursief lineair zoeken.
De inhoud van de gegeven lijst verandert niet.
Args:
lst -- de lijst waarin gezocht wordt (list)
target -- het element dat gezocht wordt
Returns:
of het element in de lijst voorkomt (bool)
"""
if len(lst) == 0: # als lijst geen entries meer bevat false returnen
return False
elif lst[0] == target: # als positie 0 de target bevat true returnen
return True
else:
return linear_search_recursive(lst[1:], target) # zichzelf starten min de eerste entry in de list
def binary_search_recursive(lst, target):
"""
Zoek een element target in gegeven lijst door middel van recursief binair zoeken.
De inhoud van de gegeven lijst verandert niet.
Args:
lst -- de (reeds gesorteerde) lijst waarin gezocht wordt (list)
target -- het element dat gezocht wordt
Returns:
of het element in de lijst voorkomt (bool)
"""
laag = 0 # laagste positie
hoog = len(lst) - 1 # hoogste positie
cur = (len(lst) - 1) // 2 # middenste index berekenen afrondend naar beneden
if target == lst[cur]: # als cursor positie al de target is true returnen
return True
if hoog > laag: # als de lijst meer dan 1 item bevat, anders bevat de lijst 1 item die niet de target is
if lst[cur] > target: # als target lager ligt dan de cursor
return binary_search_recursive(lst[0:cur+1], target) # functie oproepen met de lijst lager dan de cursor
elif lst[cur] < target: # als de target hoger ligt dan de cursor
return binary_search_recursive(lst[cur+1:hoog+1], target) # functie oproepen met de lijst hoger dan de cursor
return False
"""
==========================[ HU TESTRAAMWERK ]================================
Onderstaand staan de tests voor je code -- hieronder mag je niets wijzigen!
Je kunt je code testen door deze file te runnen of met behulp van pytest.
"""
import random
def test_id():
assert naam != "", "Je moet je naam nog invullen!"
assert studentnummer != -1, "Je moet je studentnummer nog invullen!"
assert klas != "", "Je moet je klas nog invullen!"
def test_my_sort():
lst_test = random.choices(range(-99, 100), k=6)
lst_copy = lst_test.copy()
lst_output = my_sort(lst_test)
assert lst_copy == lst_test, "Fout: my_sort(lst) verandert de inhoud van lijst lst"
assert lst_output == sorted(lst_test), \
f"Fout: my_sort({lst_test}) geeft {lst_output} in plaats van {sorted(lst_test)}"
def test_linear_search_recursive():
for _ in range(99): # ik heb deze waarde van 10 naar 99 aangepast zodat ik iteratie bugs kan vinden
lst_test = random.sample(range(20), 6)
target = random.randrange(20)
found = target in lst_test
lst_copy = lst_test.copy()
outcome = linear_search_recursive(lst_test, target)
assert lst_copy == lst_test, "Fout: linear_search_recursive(lst, target) verandert de inhoud van lijst lst"
assert outcome == found, \
f"Fout: linear_search_recursive({lst_test}, {target}) geeft {outcome} in plaats van {found}"
def test_binary_search_recursive():
for _ in range(99): # ik heb deze waarde van 10 naar 99 aangepast zodat ik iteratie bugs kan vinden
lst_test = sorted(random.sample(range(20), 6))
target = random.randrange(20)
found = target in lst_test
lst_copy = lst_test.copy()
outcome = binary_search_recursive(lst_test, target)
assert outcome == found, \
f"Fout: binary_search_recursive({lst_test}, {target}) geeft {outcome} in plaats van {found}"
assert lst_copy == lst_test, "Fout: binary_search_recursive(lst, target) verandert de inhoud van lijst lst"
if __name__ == '__main__':
try:
print("\x1b[0;32m")
test_id()
test_my_sort()
print("Je functie my_sort() werkt goed!")
test_linear_search_recursive()
print("Je functie linear_search_recursive() werkt goed!")
test_binary_search_recursive()
print("Je functie binary_search_recursive() werkt goed!")
print("\nGefeliciteerd, alles lijkt te werken!")
print("Lever je werk nu in op Canvas...")
except AssertionError as ae:
print("\x1b[0;31m")
print(ae)

File diff suppressed because it is too large Load Diff

View File

@@ -22,19 +22,29 @@ Voorbeeldvragen die Steam beantwoord wil zien voor haar klanten:
Deze applicatie maakt gebruik van de volgende python libraries, zorg ervoor dat deze zijn geinstalleerd.
- [Flask](https://github.com/pallets/flask/)
- [steam](https://github.com/ValvePython/steam)
- gevent
- eventemitter
- [matplotlib](https://pypi.org/project/matplotlib/)
- [~~steam~~](https://github.com/ValvePython/steam)
- ~~gevent~~
- ~~eventemitter~~
- [~~matplotlib~~](https://pypi.org/project/matplotlib/)
- [requests](https://pypi.org/project/requests/)
- [apa102-pi](https://github.com/tinue/apa102-pi)
Om de dependencies te installeren voer het onderste commando uit.
pip install flask steam steam[client] matplotlib
pip install flask requests apa102-pi
De applicatie is simpel uit te voeren door de python script uit te voeren. En via de browser naar de gegeven
url te gaan.
Als je op een RaspberryPI draait en de hardware wil aansturen moet je in run.py aangeven dat de software draait op een
RaspberryPI, en in hardware.py kan je de default pinnen aanpassen. De APA102 library gebruikt SPI, dus zorg ervoor dat
je via ```sudo raspi-config``` spi aan hebt gezet.
python run.py
In run.py kan je aangeven dat je op een RaspberryPI draait zodat de hardware
pagina werkt. Default werkt de hardware **niet** omdat de software libraries oproept die alleen op een RaspberryPI
werken.
De applicatie is simpel uit te voeren door de python script uit te voeren. En via de browser naar de gegeven
url te gaan. Let op! Gebruik python 3!
python3 run.py of python run.py
## Data formaat geheugen tabel
Om het sorteren makkelijker te maken gaan we de json in een tuple/list opslaan in het geheugen. Het formaat is een tabel
@@ -93,13 +103,34 @@ meegegeven wordt {1: 1, 4: 2, 6: 1} terug gegeven
#### ```gmld(lst)```
Geeft de gemiddelde van een lijst terug in een float
### steam_api.py
#### ```getappdata(appid)```
Deze functie vraagt de gegeven appid op via de http steam api en retourneert het als json. Een voorbeeld van de json is
te vinden in steam_api.py
### hardware.py
#### ```set_strip(value)```
Deze functie stuurt de ledstrip met 8 APA102 led's aan zodat de hele strip in 1 kleur oplicht. Value moet in HEX RGB
gegeven worden. Bijvoorbeeld set_strip('FFAABB')
#### ```set_register(value)```
Deze functie accepteerd waardes in machten van 2 tot 128. Waarde 1 zet de eerste pinout van het register op ON. Waarde 2
zet de 2e pinout op ON en de rest op OFF. Waarde 4 zet de 3e pin op ON enz... Tot waarde 128(pin 8). Accepteerd ook
waarde 0 dat alles op OFF zet.
#### ```poll_sensor()```
Een simpele functie die de afstandsensor aanstuurt. Wanneer de functie wordt aangeroepen retourneert het een tuple met
als eerste waarde de afstand gemeten in cm van de sensor tot object. En waarde 2 weergeeft de afstand in tijd.
**LET OP** als de sensor niet is aangesloten, en de functie wordt opgeroepen, blokkeert de functie. Zorg ervoor dat de
sensor is aangesloten voordat de functie wordt opgeroepen. Dit wordt niet aangepast anders wordt de nauwkeurigheid van
de sensor substantieel negatief beinvloed, vooral op langzame computers.
#### ```set_servo(degree)```
Deze functie accepteert waardes van 0 tot en met 180. Deze waardes corresponderen met de gradens vanaf het meest linkse
positie van de servo([voorbeeld](https://files.ventilaar.net/files/2021-01/servo.jpg)).
#### ````poll_button()````
Dit is de meest simpele functie geschreven in dit programme. Wanneer de knop is ingedrukt returnt de functie True,
anders False
### graphs.py
De functie van dit bestand is om grafieken te maken van lijsten met waardes, deze grafieken worden als svg terug gegeven
om door flask geserveerd te worden.
#### WIP verdere uitleg komt nog
### steam_abstraction.py
De functie van dit bestand is om makkelijker te kunnen communiceren met de steam api door middel van een python library.
#### verdere uitleg komt nog, als we het uberhoupt gaan gebruiken
#### WIP verdere uitleg komt nog

125
hw_funcs.py Normal file
View File

@@ -0,0 +1,125 @@
# imports all necessary libraries and sets predetermined values
import RPi.GPIO as GPIO
import time
from apa102_pi.driver import apa102
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(0)
# de onderstaande pin waardes mogen aangepast worden
register_shift_clock_pin = 17
register_latch_clock_pin = 27
register_data_pin = 22
sensor_trigger_pin = 23
sensor_echo_pin = 24
servo_pin = 14
button_pin = 25
# led clock = 11 apa102_pi default
# led data = 10 apa102_pi default
GPIO.setup(register_shift_clock_pin, GPIO.OUT)
GPIO.setup(register_latch_clock_pin, GPIO.OUT)
GPIO.setup(register_data_pin, GPIO.OUT)
GPIO.setup(sensor_trigger_pin, GPIO.OUT)
GPIO.setup(sensor_echo_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(servo_pin, GPIO.OUT)
GPIO.setup(button_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
################################
def set_strip(value):
strip = apa102.APA102(num_led=8, order='rgb')
strip.set_global_brightness(31)
for x in range(8):
strip.set_pixel_rgb(x, value)
strip.show()
######################################
def set_register(value):
"""waardes die worden geaccepteerd: 1, 2 ,4, 8, 16, 32, 64, 128"""
for k in range(0, 8): # 8 keer loopen
if value % 2 == 1: # als waarde oneven is
GPIO.output(register_data_pin, GPIO.HIGH) # data pin hoog zetten
else: # waarde is even
GPIO.output(register_data_pin, GPIO.LOW) # data pin laag houden
value = value // 2 # waarde halveren
GPIO.output(register_shift_clock_pin, GPIO.HIGH) # klok hoog, om de waarde pin te laten aflezen
GPIO.output(register_shift_clock_pin, GPIO.LOW) # klok laag, default positie
GPIO.output(register_latch_clock_pin, GPIO.HIGH) # geen idee, waarschijnlijk om de waardes op te laten slaan in de chip
GPIO.output(register_latch_clock_pin, GPIO.LOW) # default positie
return True
###############################################
def poll_sensor():
"""
Return the distance in cm as measured by an SR04
that is connected to the trig_pin and the echo_pin.
These pins must have been configured as output and input.s
"""
GPIO.output(sensor_trigger_pin, False) # altijd met false starten
time.sleep(0.5)
GPIO.output(sensor_trigger_pin, True)
time.sleep(0.00001) # pulse trigger
GPIO.output(sensor_trigger_pin, False)
# hmmm, als de sensor niet is aangesloten zal deze functie blokkeren. heb geen zin om dit aan te passen
while not GPIO.input(sensor_echo_pin):
start_time = time.time() # tijd opslaan wanneer de echo false is
while GPIO.input(sensor_echo_pin):
end_time = time.time() # tijd opslaan wanneer de echo true is
total_time = end_time - start_time # totaal aantal afstand in seconden
return (
round(total_time * 34300 / 2, 2), total_time) # tuple terug geven met afstand in cm en totaal aantal seconden
# sensor is niet helemaal naukeurig(~1-2cm variatie), kan ook aan de rpi timing liggen
###########################################
# geef het aantal graden in tussen 0 en 180 (dit is niet hoeveelheid verplaatsing maar naar welke positie)
def set_servo(degrees):
if degrees > 180:
return False
elif degrees < 0:
return False
left_span = 180 - 0
right_span = 12 - 2.5
value_scaled = float(degrees - 0) / float(left_span)
integer = 2.5 + (value_scaled * right_span)
p = GPIO.PWM(servo_pin, 50)
p.start(integer)
time.sleep(0.05)
p.stop()
return True
###########################################
# Function to check if Switch is pressed, will return a True or False Boolean
def poll_button():
if GPIO.input(button_pin):
return True
else:
return False

177
run.py
View File

@@ -1,12 +1,22 @@
import json
from flask import Flask, render_template, request, url_for, Response
from flask import Flask, render_template, request, Response
import ss
import stats
#import steam_abstraction
import graphs
import steam_api
app = Flask(__name__)
RPI = False # ZET NAAR TRUE ALS JE OP EEN RPI DRAAIT MET DE JUISTE LIBRARIES, ZODAT DE HARDWARE TAB WERKT
if RPI:
import hw_funcs
app = Flask(__name__) # flask app initialiseren
state_schuifregister = 0 # deze 3 waardes slaan de status op van de hardware onderdelen
state_ledstrip = '000000'
state_servo = 0
# lees json bestand en maak de tabel structuur aan, data_tpl
with open('steam.json') as file:
@@ -32,6 +42,9 @@ with open('steam.json') as file:
# datalijst omzetten in tuple, dit is de globale variabele die opgeroepen mag worden
data_tpl = tuple(temp_biglst)
# geheugen/variabelen vrijmaken
del blob, temp_biglst, temp_lst, temp_tpl
@app.route('/')
def root():
@@ -40,17 +53,25 @@ def root():
# sorteer op de meest aantal gebruikers
data_lst = ss.nest_qsort(data_tpl, 16, True)
genre = 0
genre = stats.popgenre(data_tpl)
prijsmed = stats.median_prijs(data_tpl)
# limiteer tabel tot en met 15 entries
for x in range(0, 15):
entries.append(data_lst[x])
return render_template('index.html', first_entry=data_tpl[0], sizedb=len(data_tpl), entries=entries, genre=genre)
return render_template('index.html', first_entry=data_tpl[0], sizedb=len(data_tpl), entries=entries, genre=genre,
prijsmed=prijsmed)
@app.route('/fulltable')
def fulltable():
max_rows_per_page = request.args.get('r', 100, int)
if max_rows_per_page == -1:
max_rows_per_page = len(data_tpl)
entries = []
if request.args.get('reverse') == 'True':
@@ -69,67 +90,133 @@ def fulltable():
else:
data_lst = data_tpl # hoezo memory efficient werken?
return render_template('fulltable.html', first_entry=data_tpl[0], sizedb=len(data_tpl), entries=data_lst,
reverse=not reverse)
for x in range(0, max_rows_per_page):
entries.append(data_lst[x])
return render_template('fulltable.html', first_entry=data_tpl[0], entries=entries, reverse=not reverse)
@app.route('/statistics')
def statistics():
return render_template('statistics.html')
@app.route('/hardware')
def hardware():
return render_template('hardware.html', RPI=RPI)
@app.route('/search')
def search():
tosearch = []
query = request.args.get('q')
query = request.args.get('q') # de request argument q opslaan in variabele query
entries = ss.nest_search(data_tpl, query)
entries = ss.nest_search(data_tpl, query) # de data_tpl doorzoeken voor de waarde query, entries wordt een list
return render_template('search.html', entries=entries)
# @app.route('/login', methods=['GET', 'POST'])
# def login():
# if request.method == 'POST':
# if steam_abstraction.steamlogin(request.form['loginACC'], request.form['loginPASS'], request.form['loginMFA']):
# return url_for('root')
# else:
# return "login problem"
# else:
# return "login page, please post to this path"
@app.route('/app/<appid>')
def app_appid(appid):
data = steam_api.getappdata(appid) # applicatie data van steam opvragen en opslaan in variabele data
return render_template('app.html', data=data[appid])
# @app.route('/logout')
# def logout():
# steam_abstraction.steamlogout()
@app.route('/api/afstandsensor')
def api_afstandsensor():
"""Dit pad geeft een json array met de key 'afstand' met waarde van de afstandsensor in cm """
if not RPI:
return Response('Hardware kan niet aangestuurt worden omdat het niet op een RPI draait!', status=418)
return {'afstand': hw_funcs.poll_sensor()[0]}
@app.route('/svgplot/randomplot.svg')
def randomplot():
svg = graphs.random_plot()
return Response(svg, mimetype='image/svg+xml')
@app.route('/api/schuifregister', methods=['POST', 'GET'])
def api_schuifregister():
"""
Door het opvragen van dit pad met de GET functie is het mogelijk om de status van het schuifregister uit te lezen.
Door te POST'en naar dit pad met de waarde 'setstate' is het mogelijk om de status aan te passen.
"""
if not RPI:
return Response('Hardware kan niet aangestuurt worden omdat het niet op een RPI draait!', status=418)
global state_schuifregister
if request.method == 'POST':
desired_state = request.form['setstate']
try:
hw_funcs.set_register(int(desired_state))
except:
return {'request': 'False'}
print(f'Set value for schuifregister {desired_state}')
state_schuifregister = desired_state
return {'request': 'True'}
else:
return {'state': state_schuifregister}
@app.route('/svgplot/releasemean.svg')
def releasemean():
lst = []
dates = []
times = []
@app.route('/api/servo', methods=['POST', 'GET'])
def api_servo():
"""
Door het opvragen van dit pad met de GET functie is het mogelijk om de status van de servo uit te lezen.
Door te POST'en naar dit pad met de waarde 'setstate' is het mogelijk om de status aan te passen.
"""
if not RPI:
return Response('Hardware kan niet aangestuurt worden omdat het niet op een RPI draait!', status=418)
for x in data_tpl:
lst.append(x[2])
global state_servo
lst = ss.qsort(lst)
if request.method == 'POST':
desired_state = request.form['setstate']
freqs = stats.freq(lst)
try:
hw_funcs.set_servo(int(desired_state))
except:
return {'request': 'False'}
for x in freqs:
dates.append(x)
times.append(freqs.get(x))
print(f'Set value for servo {desired_state}')
state_servo = desired_state
return {'request': 'True'}
else:
return {'state': state_servo}
svg = graphs.plot_xy(dates, times)
return Response(svg, mimetype='image/png')
@app.route('/api/pushbutton')
def api_pushbutton():
"""Dit pad geeft de status van de pushbuttons af in True en False, de keys alsvolgd genoemd: button0, button1"""
if not RPI:
return Response('Hardware kan niet aangestuurt worden omdat het niet op een RPI draait!', status=418)
if hw_funcs.poll_button():
return {'button0': True}
else:
return {'button0': False}
@app.route('/api/ledstrip', methods=['POST', 'GET'])
def api_ledstrip_set():
"""
Door het opvragen van dit pad met de GET functie is het mogelijk om de status van de ledstrip uit te lezen.
Door te POST'en naar dit pad met de waarde 'setstate' is het mogelijk om de status aan te passen.
"""
if not RPI:
return Response('Hardware kan niet aangestuurt worden omdat het niet op een RPI draait!', status=418)
global state_ledstrip
if request.method == 'POST':
string_state = request.form['setstate']
try:
# we ontvangen de volgenende waarde '#XXXXXX' maar de driver ondersteunt alleen hex in integer
desired_state = int(string_state[1:], 16)
hw_funcs.set_strip(desired_state)
except:
return {'request': 'False'}
print(f'Set value for ledstrip {string_state}')
state_ledstrip = string_state
return {'request': 'True'}
else:
return {'state': state_ledstrip}
if __name__ == "__main__":
app.run('0.0.0.0', port=5001, debug=True)
app.run('0.0.0.0', port=5001, debug=True)

20
ss.py
View File

@@ -1,5 +1,8 @@
def nest_qsort(tpl, colnum, reverse=False):
"""deze functie sorteert de geneste tuple zoals beschreven in de README.md"""
"""
Deze functie sorteert de geneste tuple zoals beschreven in de README.md
Het algoritme wat we gebruiken is quicksort, als we lineair gaan sorteren is het met een grote dataset niet te doen
"""
small = []
same = []
@@ -29,7 +32,10 @@ def nest_qsort(tpl, colnum, reverse=False):
def qsort(tpl, reverse=False):
"""Deze functie kan een lijst of tuple met waardes sorteren"""
"""
Deze functie kan een lijst of tuple met waardes sorteren
Het algoritme wat we gebruiken is quicksort, als we lineair gaan sorteren is het met een grote dataset niet te doen
"""
small = []
same = []
@@ -59,10 +65,10 @@ def qsort(tpl, reverse=False):
def nest_search(lst, word):
found = []
found = [] # lege lijst
for x in lst:
if word.lower() in x[1].lower():
found.append(x)
for x in lst: # voor elke nest in de lijst
if word.lower() in x[1].lower(): # als de keywordt overkomt met de naam van de app
found.append(x) # app nest opslaan in found
return found
return found # return de lijst

399
static/css/colorpicker.css Normal file

File diff suppressed because it is too large Load Diff

BIN
static/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/img/staem trek.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
static/img/staem.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

6252
static/js/Colorpicker.js Normal file

File diff suppressed because one or more lines are too long

7023
static/js/bootstrap.bundle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
static/js/bootstrap.bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4410
static/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
static/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
static/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,17 @@
# deze functies zijn overgenomen van practicum_3_statistiek_student.py, geimplementeerd door Taha
def mediaan(lst):
def mean(lst):
""" Retourneer het gemiddelde (float) van de lijst lst. """
return float(sum(lst) / len(lst)) # totaal waarde van lijst - aantal waardes in lijst in float returnen
def rnge(lst):
""" Retourneer het bereik (int) van de lijst lst. """
lst.sort() # sorteer lijst
return int(lst[-1] - lst[0]) # hoogste waarde - laagste waarde in int returnen
def median(lst):
""" Retourneer de mediaan (float) van de lijst lst. """
lst.sort() # sorteer lijst
p = len(lst) # lengte lijst opvragen
@@ -11,9 +22,25 @@ def mediaan(lst):
return float(sum(lst[p // 2 - 1:p // 2 + 1]) / 2) # return het gemiddelde van de middelste 2 waardes als float
def var(lst):
""" Retourneer de variantie (float) van de lijst lst. """
t = [] # tijdelijke lijst aanmaken
m = mean(lst) # mean van de lijst opslaan
for i in lst: # voor elke entry in lijst
t.append((i - m) ** 2) # kwadraat van i - mean opslaan in tijdelijke lijst
return float(sum(t) / len(lst)) # float van totaal waarde lijst - lengte lijst returnen
def std(lst):
""" Retourneer de standaardafwijking (float) van de lijst lst. """
return float(var(lst) ** 0.5) # wortel trekken van variatie
def freq(lst):
""" Retourneer een dictionary met als keys de waardes die voorkomen in lst en als value het aantal voorkomens
van die waarde. """
"""
Retourneer een dictionary met als keys de waardes die voorkomen in lst en
als value het aantal voorkomens van die waarde.
"""
t = {} # lege dict aanmaken
for i in lst: # voor elk cijfer in lijst
@@ -25,6 +52,45 @@ def freq(lst):
return t # return de dict
def gmld(lst):
""" Retourneer het gemiddelde (float) van de lijst lst. """
return float(sum(lst) / len(lst)) # totaal waarde van lijst - aantal waardes in lijst in float returnen
def modes(lst):
""" Retourneer een gesorteerde lijst (list) van de modi van lijst lst. """
m = [] # lijst aanmaken om de values op te slaan
r = [] # lijst maken om de keys op te slaan(de waardes die we gaan retourneren)
f = freq(lst) # een frequentie dict maken
for x in f: # voor elke key in de dict
m.append(f[x]) # de waarde opslaan in de lijst m
maximum_v = max(m) # pak de hoogste waarde in de lijst met waardes
for x in f: # voor elke key in de dict
if f[x] == maximum_v: # als de waarde van de dict overeenkomt met de maximum berekende waarde
r.append(x) # de key opslaan in de resultaten lijst
return sorted(r) # geef de r lijst terug gesorteerd
########################################
# stats genereren #
########################################
def popgenre(data_tpl):
genres = []
for x in data_tpl: # voor elke "game"
lst_genres = x[9].split(';') # genres opsplitten door de semicolons
for y in lst_genres: # voor elke genre in game
genres.append(y) # toevoegen genre in lijst genres
return modes(genres)[0] # eerst populairste genre terug geven op basis van gegevens van modes(), wat is de kans
# dat er meerdere genres tegelijk populair zijn, toch?
def median_prijs(data_tpl):
prijzen = []
for x in data_tpl:
prijzen.append(x[17])
return float(median(prijzen))

View File

@@ -1,41 +0,0 @@
from steam.client import SteamClient
from getpass import getpass
client = SteamClient()
@client.on('error')
def error(result):
return f"Logon result: {result}"
def steamlogin(accname, passwd, twofa):
try:
client.login(accname, passwd, two_factor_code=twofa)
return True
except:
return False
def steamlogout():
try:
client.logout()
return True
except:
return False
# dit is als je de py code alleen uitvoert. dus meer testen
if __name__ == "__main__":
username = input('Username: ')
password = getpass('Password: ')
twofactor = input('Twofactor: ')
steamlogin(username, password, twofactor)
print("Logged on as:", client.user.name)
print("Community profile:", client.steam_id.community_url)
print("Last logon:", client.user.last_logon)
print("Last logoff:", client.user.last_logoff)
client.logout()

415
steam_api.py Normal file

File diff suppressed because one or more lines are too long

81
templates/app.html Normal file
View File

@@ -0,0 +1,81 @@
<html>
<head>
<title>App Info</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<body>
{% include "t-navbar.html" %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>{{ data['data']['name'] }}</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div id="pictures" class="carousel slide" data-ride="carousel">
<div class="carousel-inner">
{% for x in data['data']['screenshots'] %}
<div class="carousel-item{% if x['id'] == 0 %} active{% endif %}">
<img class="d-block w-100" src="{{ x['path_full'] }}" />
</div>
{% endfor %}
</div>
<a class="carousel-control-prev" href="#pictures" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
</a>
<a class="carousel-control-next" href="#pictures" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
</a>
</div>
</div>
<div class="col-md-6">
<p>{{ data['data']['short_description'] }}</p>
<table class="table table-sm table-dark">
<tr>
<td>Applicatie ID</td>
<td>{{ data['data']['steam_appid'] }}</td>
</tr>
<tr>
<td>Developers</td>
<td>{% for x in data['data']['developers'] %}{{ x }} {% endfor %}</td>
</tr>
<tr>
<td>Publishers</td>
<td>{% for x in data['data']['publishers'] %}{{ x }} {% endfor %}</td>
</tr>
<tr>
<td>Platforms</td>
<td><ul>{% for x in data['data']['platforms'] %}
<li>
{% if x == "windows" and data['data']['platforms'][x] == True %}
Windows: Ja
{% elif x == "windows" %}
Windows: Nee
{% endif %}
{% if x == "mac" and data['data']['platforms'][x] == True %}
Mac: Ja
{% elif x == "mac" %}
Mac: Nee
{% endif %}
{% if x == "linux" and data['data']['platforms'][x] == True %}
Linux: Ja
{% elif x == "linux" %}
Linux: Nee
{% endif %}
</li>
{% endfor %}</ul></td>
</tr>
<tr>
<td>Uitgave datum</td>
<td>{{ data['data']['release_date']['date'] }}</td>
</tr>
</table>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
</body>
</html>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>Index</title>
<title>Volledig tabel</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<body>
@@ -21,7 +21,7 @@
<tbody>
{% for row in entries %}
<tr style="text-align: center;">
<td>{{ row[0] }}</td>
<td><a href="/app/{{ row[0] }}">{{ row[0] }}</a></td>
<td>{{ row[1] }}</td>
<td>{{ row[16] }} gebruikers</td>
<td>{{ row[2] }}</td>
@@ -30,6 +30,15 @@
{% endfor %}
</tbody>
</table>
<form>
<label>Maximaal aantal rijen</label>
<select name="r">
<option value="-1">Max</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
<input type="submit" value="Submit">
</form>
</div>
</div>
</div>

140
templates/hardware.html Normal file
View File

@@ -0,0 +1,140 @@
<html>
<head>
<title>Hardware</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<body class="text-center">
{% include "t-navbar.html" %}
<div class="container">
{% if RPI == False %}
<div class="alert alert-danger mt-4" role="alert">
Volgens de webserver is de hardware niet aangesloten en kan dus niet worden bestuurd. (Is de variabele aangepast in run.py?)
</div>
{% endif %}
<div class="row row-cols-2">
<div class="col">
<h1>Afstand sensor</h1>
<h2>Gemeten afstand is <span class="badge badge-secondary" id="afstand-badge">x cm</span></h2>
</div>
<div class="col">
<h1>Push buttons</h1>
<h2>Button0 is <span class="badge badge-danger" id="button-badge">Nog niet opgevraagd</span></h2>
</div>
</div>
<br>
<div class="row row-cols-3">
<div class="col">
<h1>Ledstrip</h1>
<p>Kies een kleur uit in de colorpicker en klik op set value</p>
<input type="color" id="ledstrip-input" />
<button type="button" id="ledstrip-set" class="btn btn-primary" onclick="setval()">Set value</button>
</div>
<div class="col">
<h1>Schuifregister</h1>
<p>Kies de bit uit die je positief wil aansluiten</p>
<select name="Bit volgorde" id="register-input" onchange="set_register(this.value)">
<option value="0">Uit</option>
<option value="1">Bit 1</option>
<option value="2">Bit 2</option>
<option value="4">Bit 3</option>
<option value="8">Bit 4</option>
<option value="16">Bit 5</option>
<option value="32">Bit 6</option>
<option value="64">Bit 7</option>
<option value="128">Bit 8</option>
</select>
</div>
<div class="col">
<h1>Servo</h1>
<p>Stel waarde in tussen 0 en 180 graden</p>
<input id="servo-range" type="range" min="0" max="180" step="10" value="90" oninput="this.nextElementSibling.value = this.value" onchange="set_servo(this.value)">
<output id="servo-span">90</output>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script>
// dit script blok geldt alleen voor de ledstrip
$(document).ready(function() {
$.getJSON('/api/ledstrip', function(data) {
document.getElementById('ledstrip-input').value = data.state
});
});
function setval() {
let valinbox = document.getElementById('ledstrip-input').value
$.post("/api/ledstrip", {setstate:valinbox})
}
</script>
<script>
// dit script blok geldt alleen voor de pushknoppen
// functie om de badge groen te maken en text 'ingedrukt'
function set_true() {
document.getElementById('button-badge').className = "badge badge-success";
document.getElementById('button-badge').textContent = 'Ingedrukt';
};
// functie om de badge rood te maken en text 'niet ingedrukt'
function set_false() {
document.getElementById('button-badge').className = "badge badge-danger";
document.getElementById('button-badge').textContent = 'Niet ingedrukt';
};
function button_poll() {
$.getJSON('/api/pushbutton', function(data) {
if (data.button0 == true) {
set_true();
} else {
set_false();
};
});
};
{% if RPI == False %}// {% endif %}setInterval(function(){ button_poll(); }, 500);
</script>
<script>
// dit script blok geldt alleen voor de afstandsensor
// functie om de waarde aan te passen in html
function set_afstand(waarde) {
document.getElementById('afstand-badge').textContent = waarde + ' cm';
};
// waarde opvragen en dan set_afstand() uitvoeren
function afstand_poll() {
$.getJSON('/api/afstandsensor', function(data) {
set_afstand(data.afstand);
});
};
{% if RPI == False %}// {% endif %}setInterval(function(){ afstand_poll(); }, 1500);
</script>
<script>
// dit script blok geldt alleen voor de servo
$(document).ready(function() {
$.getJSON('/api/servo', function(data) {
document.getElementById('servo-range').value = data.state
document.getElementById('servo-span').value = data.state
});
});
function set_servo(deg) {
$.post("/api/servo", {setstate:deg})
};
</script>
<script>
// dit script blok geldt alleen voor het schuifregister
$(document).ready(function() {
$.getJSON('/api/schuifregister', function(data) {
document.getElementById('register-input').value = data.state
});
});
function set_register(bit) {
$.post("/api/schuifregister", {setstate:bit})
};
</script>
</body>
</html>

View File

@@ -12,8 +12,11 @@
<p>Eerste game in memory is <b>{{ first_entry[1] }}</b> met de game id <b>{{ first_entry[0] }}</b></p>
<p>Totaal aantal games in memory is <b>{{ sizedb }}</b></p>
<p>Meest populaire genre is <b>{{ genre }}</b></p>
<p>Het mediaan van alle geindexeerde prijzen is <b>{{ prijsmed }}</b>,-</p>
</div>
<div class="col mt-3">
<!--
Dit gaan we voorlopig niet meer gebruiken
<form action="/login" method="post">
<div class="form-floating mb-3">
<input type="text" class="form-control" name="loginACC" placeholder="accountname">
@@ -28,6 +31,8 @@
<input type="submit" class="form-control" value="Login">
</div>
</form>
-->
<img src="{{ url_for('static', filename='img/staem.png') }}" width="500" height="500"/>
</div>
<div class="col col-lg">
@@ -46,7 +51,7 @@
<tbody>
{% for row in entries %}
<tr style="text-align: center;">
<td>{{ row[0] }}</td>
<td><a href="/app/{{ row[0] }}">{{ row[0] }}</a></td>
<td>{{ row[1] }}</td>
<td>{{ row[16] }}</td>
<td>{{ row[2] }}</td>

View File

@@ -22,7 +22,7 @@
<tbody>
{% for row in entries %}
<tr style="text-align: center;">
<td>{{ row[0] }}</td>
<td><a href="/app/{{ row[0] }}">{{ row[0] }}</a></td>
<td>{{ row[1] }}</td>
<td>{{ row[16] }} gebruikers</td>
<td>{{ row[2] }}</td>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>Index</title>
<title>Statistics</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<body>
@@ -9,19 +9,19 @@
<div class="row row-cols-2">
<div class="col">
<h1>container</h1>
<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
{{ lipsum() }}
</div>
<div class="col">
<h1>container</h1>
<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
{{ lipsum() }}
</div>
<div class="col col-lg">
<h1>container</h1>
<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
{{ lipsum() }}
</div>
<div class="col col-lg">
<h1>container</h1>
<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
{{ lipsum() }}
</div>
</div>
</div>

View File

@@ -5,10 +5,10 @@
<a class="nav-link" href="/">Home {{ path }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/fulltable">Volledige tabel</a>
<a class="nav-link" href="/fulltable">Volledig tabel</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/statistics">Statistieken</a>
<a class="nav-link" href="/hardware">Hardware</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0" action="/search">