Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 7fe7be79

Přidáno uživatelem Matěj Zeman před asi 2 roky(ů)

Added views for LD logs and forms for connecting body devices with licenses.

Zobrazit rozdíly:

server/sql_app/api/auth.py
1

  
2

  
3 1
from fastapi import Depends, APIRouter, Form
4 2
from fastapi import Request
5 3
from fastapi.responses import HTMLResponse
6 4
from fastapi.templating import Jinja2Templates
7
from fastapi.responses import HTMLResponse
8 5
from fastapi_jwt_auth import AuthJWT
9 6
from pydantic import BaseModel
10 7

  
11

  
12 8
# Path to html templates used in this file
13 9
templates = Jinja2Templates(directory="templates/auth")
14 10

  
......
29 25
def get_config():
30 26
    return Settings()
31 27

  
32

  
28
# admin username and password
33 29
fake_users_db = {
34 30
    "admin": {
35 31
        "username": "admin",
......
40 36

  
41 37
@auth.get("/login", response_class=HTMLResponse)
42 38
async def login_get(request: Request):
39
    """
40
    return html template for login
41
    """
43 42
    return templates.TemplateResponse("login.html", {"request": request})
44 43

  
45 44

  
46 45
@auth.post("/login", response_class=HTMLResponse)
47 46
async def login(username: str = Form(...), password: str = Form(...), Authorize: AuthJWT = Depends()):
47
    """
48
    Endpoint called from login template. Checks if given username and password aligns with admin
49
    username and password and returns token for browser according to given username and password
50
    """
48 51
    user_dict = fake_users_db.get(username)
49

  
50 52
    if user_dict != None:
51 53
        if user_dict["username"] == username and user_dict["password"] == password:
52 54
            access_token = Authorize.create_access_token(subject="admin", expires_time=False)
......
78 80

  
79 81
@auth.post('/refresh')
80 82
def refresh(Authorize: AuthJWT = Depends()):
83
    """
84
    endpoint for refreshing browser token. Not used at the moment since lifetime of given tokens are
85
    unlimited.
86
    """
81 87
    Authorize.jwt_refresh_token_required()
82

  
83 88
    current_user = Authorize.get_jwt_subject()
84 89
    new_access_token = Authorize.create_access_token(subject=current_user)
85 90
    # Set the JWT cookies in the response
......
90 95
@auth.get('/logout', response_class=HTMLResponse)
91 96
def logout(Authorize: AuthJWT = Depends()):
92 97
    """
93
    Because the JWT are stored in an httponly cookie now, we cannot
94
    log the user out by simply deleting the cookies in the frontend.
95
    We need the backend to send us a response to delete the cookies.
98
    Endpoint for deleting cookie token with acces role.
96 99
    """
97 100
    Authorize.jwt_optional()
98 101

  
server/sql_app/api/bodydevices_web.py
1
from datetime import datetime
2

  
3
from fastapi import Depends, APIRouter, Form
4
from fastapi import Request
5
from fastapi.responses import HTMLResponse, RedirectResponse
6
from fastapi.templating import Jinja2Templates
7
from fastapi_jwt_auth import AuthJWT
8
from pydantic import BaseModel
9
from sqlalchemy.orm import Session
10
from sql_app.api.auth import fake_users_db
11
from sql_app import crud, models
12
from ..database import SessionLocal, engine
13

  
14
models.Base.metadata.create_all(bind=engine)
15

  
16
# Path to html templates used in this file
17
templates = Jinja2Templates(directory="templates/body-devices")
18

  
19
# prefix used for all endpoints in this file
20
body_device_web = APIRouter(prefix="")
21

  
22

  
23
# Dependency
24
def get_db():
25
    db = SessionLocal()
26
    try:
27
        yield db
28
    finally:
29
        db.close()
30

  
31

  
32
@body_device_web.get("/body-devices-web", response_class=HTMLResponse)
33
async def read_devices(request: Request, skip: int = 0, limit: int = 100, db: Session = Depends(get_db),
34
                       Authorize: AuthJWT = Depends()):
35
    """
36
    Returns template with all body devices and its current states
37
    """
38
    Authorize.jwt_optional()
39
    current_user = Authorize.get_jwt_subject()
40

  
41
    devices = crud.get_body_devices(db, skip=skip, limit=limit)
42
    statuses = []
43
    # adding state for each device in list
44
    for i in range(0, len(devices)):
45
        statuses.append(devices[i].b_logs[len(devices[i].b_logs) - 1].status)
46
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
47
    if current_user == "admin":
48
        return templates.TemplateResponse("body_devices.html", {"request": request, "devs": len(devices), "devices": devices,
49
                                                           "statuses": statuses, "licenses": licenses, "user": current_user})
50
    else:
51
        current_user = "guest"
52
        return templates.TemplateResponse("body_devices_normal.html", {"request": request, "devs": len(devices), "devices": devices,
53
                                                           "statuses": statuses, "licenses": licenses, "user": current_user})
54

  
55

  
56
@body_device_web.post("/body-devices-web", response_class=HTMLResponse)
57
async def filter_devices(request: Request, skip: int = 0, limit: int = 100, lic: str = Form("all"),
58
                         db: Session = Depends(get_db), Authorize: AuthJWT = Depends()):
59
    """
60
    Endpoint used for filtering body devices by license. returns html template with only
61
    body devices that has assigned license defined by user input
62
    """
63
    Authorize.jwt_optional()
64
    current_user = Authorize.get_jwt_subject()
65
    devices = crud.get_body_devices(db, skip=skip, limit=limit)
66
    def_devices = []
67
    for dev in devices:
68
        for l in dev.debug_licenses:
69
            if dev not in def_devices and l.b_licenses.name == lic:
70
                def_devices.append(dev)
71
    # if input was default all
72
    if lic == "all":
73
        def_devices = devices
74
    statuses = []
75
    for i in range(0, len(def_devices)):
76
        statuses.append(def_devices[i].b_logs[len(def_devices[i].b_logs) - 1].status)
77
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
78
    if current_user == "admin":
79
        return templates.TemplateResponse("body_devices.html",
80
                                          {"request": request, "devs": len(def_devices), "devices": def_devices,
81
                                           "statuses": statuses, "licenses": licenses, "user": current_user})
82
    else:
83
        current_user = "guest"
84
        return templates.TemplateResponse("body_devices_normal.html",
85
                                          {"request": request, "devs": len(def_devices), "devices": def_devices,
86
                                           "statuses": statuses, "licenses": licenses, "user": current_user})
87

  
88

  
89
@body_device_web.get("/body-device-license/{device_id}", response_class=HTMLResponse)
90
async def connect_dev_lic(request: Request, device_id: int, db: Session = Depends(get_db)):
91
    """
92
    Returns template with one body device and all available licenses that can be assigned to it.
93
    """
94
    device = crud.get_body_device(db, device_id)
95
    dev_licenses = crud.get_bodydevice_license(db, device_id)
96
    lic_names = []
97
    dev_lics = []
98
    for dev_lic in dev_licenses:
99
        dev_lics.append(dev_lic.b_licenses)
100
    for dev_lic in dev_licenses:
101
        lic_names.append(dev_lic.b_licenses.name)
102
    licenses = crud.get_licenses(db, 0, 100)
103
    lic_left = []
104
    for lic in licenses:
105
        if lic.name not in lic_names and lic not in lic_left:
106
            lic_left.append(lic)
107
    return templates.TemplateResponse("body_device_license.html",
108
                                      {"request": request, "device": device, "licenses": lic_left, "dev_lic": dev_lics})
109

  
110

  
111
@body_device_web.post("/body-devices-web/{device_id}")
112
async def connect_post(device_id: int, lic: str = Form(...), db: Session = Depends(get_db)):
113
    """
114
    Endpoint called from template for connecting body device with license. Adds entry to bodydevices_licenses
115
    table and redirects to body-devices-web endpoint
116
    """
117
    crud.create_body_device_license(db, device_id, int(lic), datetime.now())
118
    return RedirectResponse(url=f"/body-devices-web", status_code=303)
119

  
120

  
121
@body_device_web.post("/body-devices-web-del/{device_id}")
122
async def delete_post(device_id: int, b_lic: str = Form(...), db: Session = Depends(get_db)):
123
    """
124
    Endpoint called from template for connecting body device with license. Adds entry to devices_licenses
125
    table and redirects to body-devices-web endpoint
126
    """
127
    crud.delete_bodydevice_license(db, device_id, int(b_lic))
128
    return RedirectResponse(url=f"/body-devices-web", status_code=303)
server/sql_app/api/devices_web.py
46 46
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
47 47
    if current_user == "admin":
48 48
        return templates.TemplateResponse("devices.html", {"request": request, "devs": len(devices), "devices": devices,
49
                                                           "statuses": statuses, "licenses": licenses})
49
                                                           "statuses": statuses, "licenses": licenses, "user": current_user})
50 50
    else:
51
        current_user = "guest"
51 52
        return templates.TemplateResponse("devices_normal.html", {"request": request, "devs": len(devices), "devices": devices,
52
                                                           "statuses": statuses, "licenses": licenses})
53
                                                                  "statuses": statuses, "licenses": licenses, "user": current_user})
53 54

  
54 55

  
55 56
@device_web.post("/devices-web", response_class=HTMLResponse)
56 57
async def filter_devices(request: Request, skip: int = 0, limit: int = 100, lic: str = Form("all"),
57
                         db: Session = Depends(get_db)):
58
                         db: Session = Depends(get_db), Authorize: AuthJWT = Depends()):
58 59
    """
59 60
    Endpoint used for filtering devices by license. returns html template with only
60 61
    devices that has assigned license defined by user input
61 62
    """
63
    Authorize.jwt_optional()
64
    current_user = Authorize.get_jwt_subject()
62 65
    devices = crud.get_devices(db, skip=skip, limit=limit)
63 66
    def_devices = []
64 67
    for dev in devices:
......
72 75
    for i in range(0, len(def_devices)):
73 76
        statuses.append(def_devices[i].logs[len(def_devices[i].logs) - 1].status)
74 77
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
75
    return templates.TemplateResponse("devices.html",
76
                                      {"request": request, "devs": len(def_devices), "devices": def_devices,
77
                                       "statuses": statuses, "licenses": licenses})
78
    if current_user == "admin":
79
        return templates.TemplateResponse("devices.html",
80
                                          {"request": request, "devs": len(def_devices), "devices": def_devices,
81
                                           "statuses": statuses, "licenses": licenses, "user": current_user})
82
    else:
83
        current_user = "guest"
84
        return templates.TemplateResponse("devices_normal.html",
85
                                          {"request": request, "devs": len(def_devices), "devices": def_devices,
86
                                           "statuses": statuses, "licenses": licenses, "user": current_user})
78 87

  
79 88

  
80 89
@device_web.get("/device-license/{device_id}", response_class=HTMLResponse)
......
83 92
    Returns template with one device and all available licenses that can be assigned to it.
84 93
    """
85 94
    device = crud.get_device(db, device_id)
95
    dev_licenses = crud.get_device_licenses(db, device_id)
96
    lic_names = []
97
    dev_lics = []
98
    for dev_lic in dev_licenses:
99
        dev_lics.append(dev_lic.licenses)
100
    for dev_lic in dev_licenses:
101
        lic_names.append(dev_lic.licenses.name)
86 102
    licenses = crud.get_licenses(db, 0, 100)
103
    lic_left = []
104
    for lic in licenses:
105
        if lic.name not in lic_names and lic not in lic_left:
106
            lic_left.append(lic)
87 107
    return templates.TemplateResponse("devicelicense.html",
88
                                      {"request": request, "device": device, "licenses": licenses})
108
                                      {"request": request, "device": device, "licenses": lic_left, "dev_lic": dev_lics})
89 109

  
90 110

  
91
@device_web.post("/devices-web/{device_id}", response_class=HTMLResponse)
111
@device_web.post("/devices-web/{device_id}")
92 112
async def connect_post(device_id: int, lic: str = Form(...), db: Session = Depends(get_db)):
93 113
    """
94 114
    Endpoint called from template for connecting device with license. Adds entry to devices_licenses
95
    table and returns template with all devices in database
115
    table and redirects to devices-web endpoint
96 116
    """
97 117
    crud.create_device_license(db, device_id, int(lic), datetime.now())
98
    return RedirectResponse("/devices-web")
118
    return RedirectResponse(url=f"/devices-web", status_code=303)
119

  
120

  
121
@device_web.post("/devices-web-del/{device_id}")
122
async def delete_post(device_id: int, lic_del: str = Form(...), db: Session = Depends(get_db)):
123
    """
124
    Endpoint called from template for deleting device-license connection. Adds entry to bodydevices_licenses
125
    table and redirects to devices-web endpoint
126
    """
127
    crud.delete_device_license(db, device_id, int(lic_del))
128
    return RedirectResponse(url=f"/devices-web", status_code=303)
server/sql_app/api/ld_logs_web.py
1
from typing import List
2
from fastapi import Depends, FastAPI, HTTPException, APIRouter, Form
3
from datetime import datetime
4
from sqlalchemy.orm import Session
5
from sql_app import crud, models, schemas
6
from ..database import SessionLocal, engine
7
from fastapi import FastAPI, Request
8
from fastapi.responses import HTMLResponse
9
from fastapi_jwt_auth import AuthJWT
10
from fastapi.staticfiles import StaticFiles
11
from fastapi.templating import Jinja2Templates
12

  
13
models.Base.metadata.create_all(bind=engine)
14

  
15
# Path to html templates used in this file
16
templates = Jinja2Templates(directory="templates/ld-logs")
17

  
18
# prefix used for all endpoints in this file
19
ldlogs_web = APIRouter(prefix="")
20

  
21

  
22
# Dependency
23
def get_db():
24
    db = SessionLocal()
25
    try:
26
        yield db
27
    finally:
28
        db.close()
29

  
30

  
31
@ldlogs_web.get("/ldlogs-web", response_class=HTMLResponse)
32
async def read_logs(request: Request, skip: int = 0, limit: int = 100, db: Session = Depends(get_db),
33
                    Authorize: AuthJWT = Depends()):
34
    """
35
    Returns template with all usb logs currently saved in database with its pcs, teams and licenses.
36
    """
37
    Authorize.jwt_optional()
38
    current_user = Authorize.get_jwt_subject()
39
    logs = crud.get_ld_logs(db, skip=skip, limit=limit)
40
    pcs = []
41
    for log in logs:
42
        if log.pc_id not in pcs:
43
            pcs.append(log.pc_id)
44
    pc_obj = crud.find_pcs(db, pcs)
45
    teams = crud.get_teams(db, skip=skip, limit=limit)
46
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
47
    if current_user != "admin":
48
        current_user = "guest"
49
    return templates.TemplateResponse("ldlogs.html", {"request": request, "logs": logs, "pcs": pc_obj, "teams": teams,
50
                                                      "licenses": licenses, "user": current_user})
51

  
52

  
53
@ldlogs_web.post("/ldlogs-web", response_class=HTMLResponse)
54
async def filter_logs(request: Request, pc: str = Form("all"), team: str = Form("all"), lic: str = Form("all"),
55
                      skip: int = 0, limit: int = 100,
56
                      db: Session = Depends(get_db), Authorize: AuthJWT = Depends()):
57
    """
58
    Endpoint used for filtering ld logs by user given form inputs.
59
    """
60
    Authorize.jwt_optional()
61
    current_user = Authorize.get_jwt_subject()
62
    log = crud.get_filtered_ldlogs(db, pc, team, lic)
63
    logs_ids = []
64
    for l in log:
65
        logs_ids.append(l[0])
66
    logs = crud.find_filtered_ldlogs(db, logs_ids)
67
    pc_obj = crud.get_pcs(db, skip=skip, limit=limit)
68
    teams = crud.get_teams(db, skip=skip, limit=limit)
69
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
70
    if current_user != "admin":
71
        current_user = "guest"
72
    return templates.TemplateResponse("ldlogs.html", {"request": request, "logs": logs, "pcs": pc_obj, "teams": teams,
73
                                                      "licenses": licenses, "user": current_user})
server/sql_app/api/licenses_web.py
46 46
    return templates.TemplateResponse("licenses.html", {"request": request, "licenses": licenses})
47 47

  
48 48

  
49
@licenses_web.post("/licenses-web", response_class=HTMLResponse)
49
@licenses_web.post("/licenses-web")
50 50
def create_license(name: str = Form(...), expdate: date = Form(...), db: Session = Depends(get_db)):
51 51
    """
52
    Endpoint called from create license form. Creates new license and returns template with all licenses in database
52
    Endpoint called from create license form. Creates new license and redirects to devices-web endpoint
53 53
    """
54 54
    db_license = crud.create_license(db, name, expdate)
55 55
    if db_license is None:
56 56
        print("something went wrong")
57
    return RedirectResponse("/devices-web")
57
    return RedirectResponse(url=f"/devices-web", status_code=303)
server/sql_app/api/pcs_web.py
37 37
    current_user = Authorize.get_jwt_subject()
38 38
    pcs = crud.get_pcs(db, skip=skip, limit=limit)
39 39
    if current_user == "admin":
40
        return templates.TemplateResponse("pcs.html", {"request": request, "pcs": pcs})
40
        return templates.TemplateResponse("pcs.html", {"request": request, "pcs": pcs, "user": current_user})
41 41
    else:
42
        return templates.TemplateResponse("pcs_normal.html", {"request": request, "pcs": pcs})
42
        current_user = "guest"
43
        return templates.TemplateResponse("pcs_normal.html", {"request": request, "pcs": pcs, "user": current_user})
43 44

  
44 45

  
45 46
@pcs_web.get("/pc-team/{pc_id}", response_class=HTMLResponse)
......
53 54
                                      {"request": request, "pc": pc, "teams": teams})
54 55

  
55 56

  
56
@pcs_web.post("/pcs-web/{pc_id}", response_class=HTMLResponse)
57
@pcs_web.post("/pcs-web/{pc_id}")
57 58
async def connect_post(pc_id: int, team: str = Form(...), db: Session = Depends(get_db)):
58 59
    """
59 60
    Endpoint called from within form for connecting pc with team. Updates certain pc with new team.
60 61
    """
61 62
    old_pc = crud.update_pc(db, pc_id, team)
62
    RedirectResponse("/pcs-web")
63
    return RedirectResponse(url=f"/pcs-web", status_code=303)
server/sql_app/api/teams_web.py
38 38
    current_user = Authorize.get_jwt_subject()
39 39
    teams = crud.get_teams(db, skip=skip, limit=limit)
40 40
    if current_user == "admin":
41
        return templates.TemplateResponse("teams.html", {"request": request, "teams": teams})
41
        return templates.TemplateResponse("teams.html", {"request": request, "teams": teams, "user": current_user})
42 42
    else:
43
        return templates.TemplateResponse("teams_normal.html", {"request": request, "teams": teams})
43
        current_user = "guest"
44
        return templates.TemplateResponse("teams_normal.html", {"request": request, "teams": teams, "user": current_user})
44 45

  
45 46

  
46 47
@teams_web.get("/team-create", response_class=HTMLResponse)
......
51 52
    return templates.TemplateResponse("team_create.html", {"request": request})
52 53

  
53 54

  
54
@teams_web.post("/teams-web", response_class=HTMLResponse)
55
@teams_web.post("/teams-web-con")
55 56
def create_team(name: str = Form(...), db: Session = Depends(get_db)):
56 57
    """
57 58
    Endpoint called from within form for creating new team. Creates new team and returns all teams in database
......
59 60
    team = crud.create_team(db, name)
60 61
    if team is None:
61 62
        print("something went wrong")
62
    RedirectResponse("/teams-web")
63
    return RedirectResponse(url=f"/teams-web", status_code=303)
server/sql_app/api/usb_logs_web.py
6 6
from ..database import SessionLocal, engine
7 7
from fastapi import FastAPI, Request
8 8
from fastapi.responses import HTMLResponse
9
from fastapi_jwt_auth import AuthJWT
9 10
from fastapi.staticfiles import StaticFiles
10 11
from fastapi.templating import Jinja2Templates
11 12

  
......
28 29

  
29 30

  
30 31
@usblogs_web.get("/logs-web", response_class=HTMLResponse)
31
async def read_logs(request: Request, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
32
async def read_logs(request: Request, skip: int = 0, limit: int = 100, db: Session = Depends(get_db),
33
                    Authorize: AuthJWT = Depends()):
32 34
    """
33 35
    Returns template with all usb logs currently saved in database with its pcs, teams and licenses.
34 36
    """
37
    Authorize.jwt_optional()
38
    current_user = Authorize.get_jwt_subject()
35 39
    logs = crud.get_logs(db, skip=skip, limit=limit)
36 40
    pcs = []
37 41
    for log in logs:
......
40 44
    pc_obj = crud.find_pcs(db, pcs)
41 45
    teams = crud.get_teams(db, skip=skip, limit=limit)
42 46
    licenses = crud.get_licenses(db, skip=skip, limit=limit)
47
    if current_user != "admin":
48
        current_user = "guest"
43 49
    return templates.TemplateResponse("logs.html", {"request": request, "logs": logs, "pcs": pc_obj, "teams": teams,
44
                                                    "licenses": licenses})
50
                                                    "licenses": licenses, "user": current_user})
45 51

  
46 52

  
47 53
@usblogs_web.post("/logs-web", response_class=HTMLResponse)
server/sql_app/crud.py
74 74

  
75 75
def get_license_devices(db: Session, license_id: int):
76 76
    """
77
    returns all entries in devices_licenses table
77
    returns all entries in devices_licenses table with given license_id
78 78
    """
79 79
    return db.query(models.DeviceLicense).filter(models.DeviceLicense.license_id == license_id).all()
80 80

  
81 81

  
82
def get_device_licenses(db: Session, device_id: int):
83
    """
84
    returns all entries in devices_licenses table with given license_id
85
    """
86
    return db.query(models.DeviceLicense).filter(models.DeviceLicense.device_id == device_id).all()
87

  
88

  
89
def get_devicelicense_by_devicelicense(db: Session, device_id: int, license_id: int):
90
    """
91
    returns entry in devices_licenses table with given device id and license id
92
    """
93
    return db.query(models.DeviceLicense).filter(and_(models.DeviceLicense.device_id == device_id,
94
                                                      models.DeviceLicense.license_id == license_id)).first()
95

  
96

  
97
def get_bodydevicelicense_by_bodydevicelicense(db: Session, device_id: int, license_id: int):
98
    """
99
    returns entry in bodydevices_licenses table with given body device id and license id
100
    """
101
    return db.query(models.BodyDeviceLicense).filter(and_(models.BodyDeviceLicense.bodydevice_id == device_id,
102
                                                      models.BodyDeviceLicense.license_id == license_id)).first()
103

  
104

  
105
def get_license_bodydevice(db: Session, license_id: int):
106
    """
107
    returns all entries in bodydevices_licenses with given license_id
108
    """
109
    return db.query(models.BodyDeviceLicense).filter(models.BodyDeviceLicense.license_id == license_id).all()
110

  
111

  
112
def get_bodydevice_license(db: Session, device_id: int):
113
    """
114
    returns all entries in bodydevices_licenses with given license_id
115
    """
116
    return db.query(models.BodyDeviceLicense).filter(models.BodyDeviceLicense.bodydevice_id == device_id).all()
117

  
118

  
82 119
def create_device_license(db: Session, device: int, license: int, time: datetime):
83 120
    """
84 121
    creates new entry in devices_licenses table with device id, license id and time.
......
91 128
    return db_device_license
92 129

  
93 130

  
131
def delete_device_license(db: Session, device: int, license: int):
132
    """
133
    deletes entry in devices_licenses table with device id, license id and time.
134
    """
135
    db_device_license = get_devicelicense_by_devicelicense(db, device, license)
136
    db_lic = db.delete(db_device_license)
137
    db.commit()
138
    return db_lic
139

  
140

  
141
def delete_bodydevice_license(db: Session, device: int, license: int):
142
    """
143
    deletes entry in devices_licenses table with device id, license id and time.
144
    """
145
    db_device_license = get_bodydevicelicense_by_bodydevicelicense(db, device, license)
146
    db_lic = db.delete(db_device_license)
147
    db.commit()
148
    return db_lic
149

  
150

  
151
def create_body_device_license(db: Session, device: int, license: int, time: datetime):
152
    """
153
    creates new entry in devices_licenses table with device id, license id and time.
154
    """
155
    db_device_license = models.BodyDeviceLicense(bodydevice_id=device, license_id=license,
156
                                                 assigned_datetime=time)
157
    db.add(db_device_license)
158
    db.commit()
159
    db.refresh(db_device_license)
160
    return db_device_license
161

  
162

  
94 163
def find_pc_by_username(db: Session, name: str):
95 164
    """
96 165
    Finds one pc by given username
......
107 176

  
108 177
def update_pc(db: Session, pc_id: int, team: str):
109 178
    """
110
    Function updates team of one specific pc
179
    Updates team of one specific pc
111 180
    """
112 181
    old_pc = get_pc(db, pc_id)
113 182
    team = get_team(db, int(team))
......
274 343
    """
275 344
    Returns all ld debugger logs in database
276 345
    """
277
    return db.query(models.LDLog).offset(skip).limit(limit).all()
346
    return db.query(models.LDLog).order_by(desc(models.LDLog.timestamp)).offset(skip).limit(limit).all()
278 347

  
279 348

  
280 349
def create_ld_logs(db: Session, item: schemas.LDTempBase, head_id: int, body_id: int, pc_id: int, date: datetime):
......
309 378
    return db.query(models.USBLog).filter(models.USBLog.id.in_(logs)).order_by(desc(models.USBLog.timestamp)).all()
310 379

  
311 380

  
381
def find_filtered_ldlogs(db: Session, logs: []):
382
    """
383
    Returns all ld logs with ids in given id array.
384
    """
385
    return db.query(models.LDLog).filter(models.LDLog.id.in_(logs)).order_by(desc(models.LDLog.timestamp)).all()
386

  
387

  
388
def get_filtered_ldlogs(db: Session, pc: str, tema: str, lic: str):
389
    """
390
    Function creates query string used for filtering by pc username, team name and license name.
391
    Depending on selected filters assembles query string for database
392
    """
393
    execute_string = "SELECT * FROM ld_logs AS logs"
394
    pcs = find_pc_by_username(db, pc)
395
    if pc != "all":
396
        if pcs is not None:
397
            execute_string += "  WHERE logs.pc_id = " + str(pcs.id)
398
    if tema != "all":
399
        team = find_team(db, tema)
400
        if team is not None:
401
            pcst = get_pcs_by_team(db, team.id)
402
            pc_ids = "("
403
            for p in pcst:
404
                pc_ids += str(p.id) + ", "
405
            def_pc_ids = pc_ids[:-2] + ")"
406
            if pc != "all" and pcs is not None:
407
                if len(def_pc_ids) > 1:
408
                    execute_string += " AND logs.pc_id IN " + def_pc_ids
409
            else:
410
                if len(def_pc_ids) > 1:
411
                    execute_string += " WHERE logs.pc_id IN " + def_pc_ids
412
    if lic != "all":
413
        license = find_license(db, lic)
414
        if license is not None:
415
            device_licenses = get_license_bodydevice(db, license.id)
416
            dev_ids = "("
417
            for dev in device_licenses:
418
                dev_ids += str(dev.bodydevice_id) + ", "
419
            defin_ids = dev_ids[:-2] + ")"
420
            if pc != "all" or tema != "all":
421
                if len(defin_ids) > 1:
422
                    execute_string += " AND logs.body_id IN " + defin_ids
423
            else:
424
                if len(defin_ids) > 1:
425
                    execute_string += " WHERE logs.body_id IN " + defin_ids
426

  
427
    # executing assembled query string
428
    result = db.execute(execute_string)
429
    return result
430

  
431

  
312 432
def get_filtered_logs(db: Session, pc: str, tema: str, lic: str):
313 433
    """
314 434
    Function creates query string used for filtering by pc username, team name and license name.
315 435
    Depending on selected filters assembles query string for database
316 436
    """
317 437
    execute_string = "SELECT * FROM usb_logs AS logs"
438
    pcs = find_pc_by_username(db, pc)
318 439
    if pc != "all":
319
        pcs = find_pc_by_username(db, pc)
320
        execute_string += "  WHERE logs.pc_id = " + str(pcs.id)
440
        if pcs is not None:
441
            execute_string += "  WHERE logs.pc_id = " + str(pcs.id)
321 442
    if tema != "all":
322 443
        team = find_team(db, tema)
323
        pcs = get_pcs_by_team(db, team.id)
324
        pc_ids = "("
325
        for p in pcs:
326
            pc_ids += str(p.id) + ", "
327
        def_pc_ids = pc_ids[:-2] + ")"
328
        if pc != "all":
329
            execute_string += " AND logs.pc_id IN " + def_pc_ids
330
        else:
331
            execute_string += " WHERE logs.pc_id IN " + def_pc_ids
444
        if team is not None:
445
            pcst = get_pcs_by_team(db, team.id)
446
            pc_ids = "("
447
            for p in pcst:
448
                pc_ids += str(p.id) + ", "
449
            def_pc_ids = pc_ids[:-2] + ")"
450
            if pc != "all" and pcs is not None:
451
                if len(def_pc_ids) > 1:
452
                    execute_string += " AND logs.pc_id IN " + def_pc_ids
453
            else:
454
                if len(def_pc_ids) > 1:
455
                    execute_string += " WHERE logs.pc_id IN " + def_pc_ids
332 456
    if lic != "all":
333 457
        license = find_license(db, lic)
334
        device_licenses = get_license_devices(db, license.id)
335
        dev_ids = "("
336
        for dev in device_licenses:
337
            dev_ids += str(dev.device_id) + ", "
338
        defin_ids = dev_ids[:-2] + ")"
339
        if pc != "all" or tema != "all":
340
            execute_string += " AND logs.device_id IN " + defin_ids
341
        else:
342
            execute_string += " WHERE logs.device_id IN " + defin_ids
458
        if license is not None:
459
            device_licenses = get_license_devices(db, license.id)
460
            dev_ids = "("
461
            for dev in device_licenses:
462
                dev_ids += str(dev.device_id) + ", "
463
            defin_ids = dev_ids[:-2] + ")"
464
            if pc != "all" or tema != "all":
465
                if len(defin_ids) > 1:
466
                    execute_string += " AND logs.device_id IN " + defin_ids
467
            else:
468
                if len(defin_ids) > 1:
469
                    execute_string += " WHERE logs.device_id IN " + defin_ids
343 470

  
344 471
    # executing assembled query string
345 472
    result = db.execute(execute_string)
server/sql_app/main.py
10 10
from sql_app.api.teams import teams
11 11
from sql_app.api.teams_web import teams_web
12 12
from sql_app.api.auth import auth
13
from sql_app.api.ld_logs_web import ldlogs_web
14
from sql_app.api.bodydevices_web import body_device_web
13 15
from fastapi import FastAPI
14 16

  
15 17

  
......
28 30
app.include_router(pcs_web)
29 31
app.include_router(teams_web)
30 32
app.include_router(usblogs_web)
33
app.include_router(ldlogs_web)
34
app.include_router(body_device_web)
31 35
app.include_router(auth)
32 36

  
33 37
'''
34 38
if __name__ == "__main__":
35
    uvicorn.run(app, host="192.168.0.22", port=8000)
39
    uvicorn.run(app, host="192.168.176.1", port=8000)
36 40
'''
server/sql_app/models.py
52 52

  
53 53
    # relationships for foreign keys, thus connecting table with devices table
54 54
    devices = relationship("DeviceLicense", back_populates="licenses")
55

  
55
    body_devices = relationship("BodyDeviceLicense", back_populates="b_licenses")
56 56

  
57 57
class DeviceLicense(Base):
58 58
    """
......
71 71
    licenses = relationship("License", back_populates="devices")
72 72

  
73 73

  
74
class BodyDeviceLicense(Base):
75
    """
76
    Class defining database table bodydevices_licenses
77
    """
78
    __tablename__ = "bodydevices_licenses"
79

  
80
    id = Column(Integer, primary_key=True, index=True)
81
    bodydevice_id = Column(Integer, ForeignKey("body_devices.id"))
82
    license_id = Column(Integer, ForeignKey("licenses.id"))
83
    assigned_datetime = Column(String, index=True, nullable=False)
84

  
85
    # relationships for foreign keys, thus connecting table with devices and licenses
86
    # tables
87
    bodydevice_lic = relationship("BodyDevice", back_populates="debug_licenses")
88
    b_licenses = relationship("License", back_populates="body_devices")
89

  
90

  
74 91
class PC(Base):
75 92
    """
76 93
    Class defining database table pc
......
127 144

  
128 145
    # relationships for foreign keys, thus connecting table with ld_logs table
129 146
    b_logs = relationship("LDLog", back_populates="body_device")
147
    debug_licenses = relationship("BodyDeviceLicense", back_populates="bodydevice_lic")
130 148

  
131 149

  
132 150
class LDLog(Base):
server/templates/body-devices/body_device_license.html
1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
    <meta charset="UTF-8">
5
    <title>Body Device License Connect</title>
6
</head>
7
<body>
8
<h6><p>Vendor ID: {{device.vendor_id}}</p>
9
    <p>Serial Number: {{device.serial_number}}</p>
10
</h6>
11
<form action="/body-devices-web/{{device.id}}" method="post">
12
  <label for="lic">Licenses:</label>
13
  <select id="lic" name="lic">
14
      {% for license in licenses %}
15
    <option value={{license.id}}>{{license.name}}</option>
16
      {% endfor %}
17
  </select>
18
  <input type="submit" value="Connect">
19
</form>
20
<form action="/body-devices-web-del/{{device.id}}" method="post">
21
  <label for="b_lic">Licenses:</label>
22
  <select id="b_lic" name="b_lic">
23
      {% for license in dev_lic %}
24
    <option value={{license.id}}>{{license.name}}</option>
25
      {% endfor %}
26
  </select>
27
  <input type="submit" value="Delete">
28
</form>
29
</body>
30
</html>
server/templates/body-devices/body_devices.html
1
<html>
2
<head>
3
    <title>Devices Details</title>
4
</head>
5
<body>
6
<div style='float:left'>
7
<form action="" method="get">
8
  <label for="view">Choose view:</label>
9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10
      <option value=""></option>
11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
15
      <option value="/teams-web">Teams</option>
16
      <option value="/pcs-web">PCs</option>
17
  </select>
18
  <input type="submit" value="OK">
19
</form>
20
</div>
21
<div style='float:left'>
22
<form action="/login" method="get">
23
    <input type="submit" value="Login" />
24
</form>
25
</div>
26
<div style='float:left'>
27
<form action="/logout" method="get">
28
    <input type="submit" value="Logout" />
29
</form>
30
</div>
31
<h4>{{user}}</h4>
32
<form action="/body-devices-web" method="post">
33
    <label for="lic">License:</label>
34
    <input id="lic" name="lic" type="text" list="licenses" value="" placeholder="all">
35
    <datalist id="licenses">
36
        {% for license in licenses %}
37
        <option value="{{license.name}}"></option>
38
        {% endfor %}
39
    </datalist>
40
  <input type="submit" value="Filter">
41
</form>
42
<table>
43
    <TR>
44
        <TH>ID</TH>
45
        <TH>Serial Number</TH>
46
        <TH>Licenses</TH>
47
        <TH>Status</TH>
48
    </TR>
49
    {% for i in range(devs) %}
50
    <TR>
51
        <TD class="ID"><a href="/body-device-license/{{devices[i].id}}">{{devices[i].id}}</a></TD>
52
        <TD class="Serial Number">{{devices[i].serial_number}}</TD>
53
        <TD class="License">
54
            {% for lic in devices[i].debug_licenses %}
55
                {{lic.b_licenses.name}}<BR>
56
            {% endfor %}
57
        </TD>
58
        <TD class="Status">{{statuses[i]}}</TD>
59
    </TR>
60
    {% endfor %}
61
    <TR>
62
        <TD class="ID"></TD>
63
        <TD class="Serial Number"></TD>
64
        <TD class="License">
65
            <form action="/license-create" method="get">
66
                <input type="submit" value="Add">
67
            </form>
68
        </TD>
69
    </TR>
70
</table>
71
</body>
72
</html>
server/templates/body-devices/body_devices_normal.html
1
<html>
2
<head>
3
    <title>Devices Details</title>
4
</head>
5
<body>
6
<div style='float:left'>
7
<form action="" method="get">
8
  <label for="view">Choose view:</label>
9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10
      <option value=""></option>
11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
15
      <option value="/teams-web">Teams</option>
16
      <option value="/pcs-web">PCs</option>
17
  </select>
18
  <input type="submit" value="OK">
19
</form>
20
</div>
21
<div style='float:left'>
22
<form action="/login" method="get">
23
    <input type="submit" value="Login" />
24
</form>
25
</div>
26
<div style='float:left'>
27
<form action="/logout" method="get">
28
    <input type="submit" value="Logout" />
29
</form>
30
</div>
31
<h4>{{user}}</h4>
32
<form action="/body-devices-web" method="post">
33
    <label for="lic">License:</label>
34
    <input id="lic" name="lic" type="text" list="licenses" value="" placeholder="all">
35
    <datalist id="licenses">
36
        {% for license in licenses %}
37
        <option value="{{license.name}}"></option>
38
        {% endfor %}
39
    </datalist>
40
  <input type="submit" value="Filter">
41
</form>
42
<table>
43
    <TR>
44
        <TH>ID</TH>
45
        <TH>Serial Number</TH>
46
        <TH>Licenses</TH>
47
        <TH>Status</TH>
48
    </TR>
49
    {% for i in range(devs) %}
50
    <TR>
51
        <TD class="ID">{{devices[i].id}}</TD>
52
        <TD class="Serial Number">{{devices[i].serial_number}}</TD>
53
        <TD class="License">
54
            {% for lic in devices[i].debug_licenses %}
55
                {{lic.b_licenses.name}}<BR>
56
            {% endfor %}
57
        </TD>
58
        <TD class="Status">{{statuses[i]}}</TD>
59
    </TR>
60
    {% endfor %}
61
</table>
62
</body>
63
</html>
server/templates/devices/devicelicense.html
18 18
  </select>
19 19
  <input type="submit" value="Connect">
20 20
</form>
21
<form action="/devices-web-del/{{device.id}}" method="post">
22
  <label for="lic_del">Licenses:</label>
23
  <select id="lic_del" name="lic_del">
24
      {% for license in dev_lic %}
25
    <option value={{license.id}}>{{license.name}}</option>
26
      {% endfor %}
27
  </select>
28
  <input type="submit" value="Delete">
29
</form>
21 30
</body>
22 31
</html>
server/templates/devices/devices.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<form action="/devices-web" method="post">
28 33
    <label for="lic">License:</label>
29 34
    <input id="lic" name="lic" type="text" list="licenses" value="" placeholder="all">
server/templates/devices/devices_normal.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<form action="/devices-web" method="post">
28 33
    <label for="lic">License:</label>
29 34
    <input id="lic" name="lic" type="text" list="licenses" value="" placeholder="all">
server/templates/ld-logs/ldlogs.html
1
<html>
2
<head>
3
    <title> LD Logs Details</title>
4
</head>
5
<body>
6
<div style='float:left'>
7
<form action="" method="get">
8
  <label for="view">Choose view:</label>
9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10
      <option value=""></option>
11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
15
      <option value="/teams-web">Teams</option>
16
      <option value="/pcs-web">PCs</option>
17
  </select>
18
  <input type="submit" value="OK">
19
</form>
20
</div>
21
<div style='float:left'>
22
<form action="/login" method="get">
23
    <input type="submit" value="Login" />
24
</form>
25
</div>
26
<div style='float:left'>
27
<form action="/logout" method="get">
28
    <input type="submit" value="Logout" />
29
</form>
30
</div>
31
<h4>{{user}}</h4>
32
<form action="/ldlogs-web" method="post">
33
  <label for="pc">PC:</label>
34
  <input id="pc" name="pc" type="text" list="pcs" value="" placeholder="all">
35
    <datalist id="pcs">
36
        {% for pc in pcs %}
37
        <option value="{{pc.username}}"></option>
38
        {% endfor %}
39
    </datalist>
40
    <label for="team">Team:</label>
41
  <input id="team" name="team" type="text" list="teams" value="" placeholder="all">
42
    <datalist id="teams">
43
        {% for team in teams %}
44
        <option value="{{team.name}}"></option>
45
        {% endfor %}
46
    </datalist>
47
    <label for="lic">License:</label>
48
    <input id="lic" name="lic" type="text" list="licenses" value="" placeholder="all">
49
    <datalist id="licenses">
50
        {% for license in licenses %}
51
        <option value="{{license.name}}"></option>
52
        {% endfor %}
53
    </datalist>
54
  <input type="submit" value="Filter">
55
</form>
56
    <table>
57
    <TR>
58
        <TH>ID</TH>
59
        <TH>PC Username</TH>
60
        <TH>PC Hostname</TH>
61
        <TH>Team</TH>
62
        <TH>Timestamp</TH>
63
        <TH>Status</TH>
64
        <TH>Head Device Serial Number</TH>
65
        <TH>Body Device Serial Number</TH>
66
    </TR>
67
    {% for log in logs %}
68
    <TR>
69
        <TD class="ID">{{log.id}}</TD>
70
        <TD class="Username">{{log.ldpc.username}}</TD>
71
        <TD class="Hostname">{{log.ldpc.hostname}}</TD>
72
        {% if log.ldpc.team == None %}
73
            <TD class="Team">NONE</TD>
74
        {% else %}
75
            <TD class="Team">{{log.ldpc.team.name}}</TD>
76
        {% endif %}
77
        <TD class="Timestamp">{{log.timestamp}}</TD>
78
        <TD class="Status">{{log.status}}</TD>
79
        <TD class="HeadDeviceSerialNumber">{{log.head_device.serial_number}}</TD>
80
        <TD class="BodyDeviceSerialNumber">{{log.body_device.serial_number}}</TD>
81
    </TR>
82
    {% endfor %}
83
</table>
84
</body>
85
</html>
server/templates/licenses/licenses.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
server/templates/pcs/pcs.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<table>
28 33
    <TR>
29 34
        <TH>ID</TH>
server/templates/pcs/pcs_normal.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<table>
28 33
    <TR>
29 34
        <TH>ID</TH>
server/templates/teams/team_create.html
5 5
    <title>Create a Team</title>
6 6
</head>
7 7
<body>
8
<form action="/teams-web" method="post">
8
<form action="/teams-web-con" method="post">
9 9
  <label for="name">Name:</label><br>
10 10
  <input type="text" id="name" name="name"><br><br>
11 11
  <input type="submit" value="Submit">
server/templates/teams/teams.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<table>
28 33
    <TR>
29 34
        <TH>ID</TH>
server/templates/teams/teams_normal.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<table>
28 33
    <TR>
29 34
        <TH>ID</TH>
server/templates/usb-logs/logs.html
9 9
  <select id="view" name="view" onchange="this.form.action=this.value;">
10 10
      <option value=""></option>
11 11
      <option value="/logs-web">Logs</option>
12
      <option value="/ldlogs-web">LD Logs</option>
12 13
      <option value="/devices-web">Devices</option>
14
      <option value="/body-devices-web">Body Devices</option>
13 15
      <option value="/teams-web">Teams</option>
14 16
      <option value="/pcs-web">PCs</option>
15 17
  </select>
......
21 23
    <input type="submit" value="Login" />
22 24
</form>
23 25
</div>
26
<div style='float:left'>
24 27
<form action="/logout" method="get">
25 28
    <input type="submit" value="Logout" />
26 29
</form>
30
</div>
31
<h4>{{user}}</h4>
27 32
<form action="/logs-web" method="post">
28 33
  <label for="pc">PC:</label>
29 34
  <input id="pc" name="pc" type="text" list="pcs" value="" placeholder="all">

Také k dispozici: Unified diff