Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
parkpow
GitHub Repository: parkpow/deep-license-plate-recognition
Path: blob/master/docker/platerec_installer/platerec_installer.py
1093 views
1
import argparse
2
import base64
3
import logging
4
import os
5
import re
6
import sys
7
import time
8
9
import dash
10
import dash_bootstrap_components as dbc
11
import installer_helpers as helpers
12
from dash import Input, Output, State, clientside_callback, dcc, html
13
from dash.exceptions import PreventUpdate
14
15
SHARE_LINK = "https://guides.platerecognizer.com/docs/stream/manual-install#step-2"
16
STREAM_PLAN_LINK = "https://app.platerecognizer.com/accounts/plan/#stream/?utm_source=installer&utm_medium=app"
17
SDK_PLAN_LINK = "https://app.platerecognizer.com/accounts/plan/#sdk/?utm_source=installer&utm_medium=app"
18
STREAM_DOCS_LINK = "https://guides.platerecognizer.com/docs/stream/configuration"
19
STREAM_IMAGE = "platerecognizer/alpr-stream"
20
SDK_IMAGE = "platerecognizer/alpr"
21
STREAM = "stream"
22
SNAPSHOT = "snapshot"
23
USER_DATA = "/user-data/"
24
NONE = {"display": "none"}
25
BLOCK = {"display": "block"}
26
FLEX = {"display": "flex"}
27
WIDTH = "91.5%"
28
DISPLAY_CARD = {"display": "block", "width": WIDTH}
29
CONSOLE_WELCOME = """
30
############################################
31
# Thank you for choosing Plate Recognizer! #
32
############################################
33
34
- To continue open http://localhost:8050/ in your web browser. If your browser is
35
on a separate device, replace "localhost" by the host's IP.
36
37
- When you are done with the installation, you can close this window.
38
39
############################################"""
40
41
42
def get_splash_screen():
43
return html.Div(
44
[
45
html.Div(
46
[
47
html.H2("Please choose a product:", className="splash-header"),
48
dbc.Button("Stream", size="lg", id="button-choose-stream"),
49
dbc.Button(
50
"Snapshot",
51
size="lg",
52
id="button-choose-snapshot",
53
class_name="ms-3",
54
),
55
],
56
className="splash",
57
)
58
],
59
className="background",
60
)
61
62
63
def get_refresh(product):
64
docker_links = {
65
"Windows": "https://platerecognizer.com/docker/#install-SDK",
66
"Linux": "https://docs.docker.com/install/",
67
"Mac OS": "https://hub.docker.com/editions/community/docker-ce-desktop-mac/",
68
}
69
docker_link = docker_links.get(helpers.get_os())
70
docker_info = [
71
"Do you have Docker? If so, please run it now. "
72
"If not, then please go here to install Docker on your machine: ",
73
html.A(docker_link, href=docker_link, target="_blank"),
74
]
75
permission_error_info = [
76
"Got a 'permission denied error' while trying to connect to the Docker daemon. "
77
"Does the user running the installer able to execute Docker commands?"
78
]
79
if helpers.get_os() == "Windows":
80
docker_info += [
81
". If using the legacy Hyper-V backend and not WSL2, "
82
"Make sure to check the box (next to C) for ",
83
html.A("Resource File Sharing", href=SHARE_LINK, target="_blank"),
84
" and the click “Apply & Restart”.",
85
]
86
return dbc.Row(
87
[
88
dbc.Label(
89
docker_info,
90
id=f"info-docker-{product}",
91
html_for=f"refresh-docker-{product}",
92
style=BLOCK,
93
width=7,
94
),
95
dbc.Label(
96
permission_error_info,
97
id=f"permissions-docker-{product}",
98
html_for=f"refresh-docker-{product}",
99
style=NONE,
100
width=7,
101
),
102
dcc.Loading(
103
type="circle", children=html.Div(id=f"loading-refresh-{product}")
104
),
105
dbc.Col(
106
dbc.Button(
107
"Refresh", color="secondary", id=f"refresh-docker-{product}"
108
),
109
width=4,
110
),
111
],
112
style=BLOCK,
113
id=f"refresh-{product}",
114
)
115
116
117
def get_update(product):
118
return dbc.Row(
119
[
120
dbc.Col(
121
[
122
dbc.Button(
123
"Update",
124
color="secondary",
125
id=f"update-image-{product}",
126
style={"width": "100%"},
127
),
128
html.Span(
129
"Updated", id=f"span-update-{product}", className="align-middle"
130
),
131
],
132
width=2,
133
),
134
dbc.Label(
135
"Update the Docker image.",
136
html_for=f"update-image-{product}",
137
className="col-auto align-self-center",
138
),
139
dcc.Loading(
140
type="circle",
141
children=html.Div(id=f"loading-update-{product}"),
142
parent_className="col-auto",
143
),
144
],
145
style=NONE,
146
id=f"update-{product}",
147
class_name="mb-3",
148
)
149
150
151
def get_uninstall(product):
152
return dbc.Row(
153
[
154
dbc.Col(
155
[
156
dbc.Button(
157
"Uninstall",
158
color="danger",
159
id=f"uninstall-image-{product}",
160
style={"width": "100%"},
161
),
162
html.Span(
163
"",
164
id=f"span-uninstall-{product}",
165
className="align-middle",
166
style={"color": "red"},
167
),
168
dbc.Modal(
169
[
170
dbc.ModalHeader("Uninstall"),
171
dbc.ModalBody(
172
"Are you sure you want to uninstall an image?"
173
),
174
dbc.ModalFooter(
175
[
176
dbc.Button(
177
"OK",
178
color="danger",
179
id=f"ok-uninstall-{product}",
180
),
181
dbc.Button(
182
"Cancel", id=f"cancel-uninstall-{product}"
183
),
184
]
185
),
186
],
187
id=f"modal-uninstall-{product}",
188
centered=True,
189
),
190
],
191
width=2,
192
),
193
dbc.Label(
194
"Remove the Docker image and mark the product as uninstalled.",
195
html_for=f"uninstall-image-{product}",
196
className="col-auto align-self-center",
197
),
198
dcc.Loading(
199
type="circle",
200
children=html.Div(id=f"loading-uninstall-{product}"),
201
parent_className="col-auto",
202
),
203
],
204
style=NONE,
205
id=f"uninstall-{product}",
206
class_name="mb-3",
207
)
208
209
210
def get_token(product):
211
link = STREAM_PLAN_LINK if product == "stream" else SDK_PLAN_LINK
212
return dbc.Row(
213
[
214
dbc.Label(
215
[
216
"Please enter your Plate Recognizer ",
217
html.A("API Token", href=link, target="_blank"),
218
":",
219
],
220
html_for=f"input-token-{product}",
221
width=7,
222
),
223
dbc.Col(
224
dbc.Input(
225
type="text",
226
id=f"input-token-{product}",
227
placeholder="Token",
228
persistence=True,
229
),
230
width=4,
231
),
232
],
233
class_name="mb-3",
234
)
235
236
237
def get_license_key(product):
238
link = STREAM_PLAN_LINK if product == "stream" else SDK_PLAN_LINK
239
return dbc.Row(
240
[
241
dbc.Label(
242
[
243
"Please enter your ",
244
html.A(
245
f"{product.capitalize()} License Key",
246
href=link,
247
target="_blank",
248
),
249
":",
250
],
251
html_for=f"input-key-{product}",
252
width=7,
253
),
254
dbc.Col(
255
dbc.Input(
256
type="text",
257
id=f"input-key-{product}",
258
placeholder="License Key",
259
persistence=True,
260
),
261
width=4,
262
),
263
],
264
class_name="mb-3",
265
)
266
267
268
def get_directory(product):
269
return dbc.Row(
270
[
271
dbc.Label(
272
f"Path to your {product.capitalize()} installation directory:",
273
html_for=f"input-home-{product}",
274
width=7,
275
),
276
dbc.Col(
277
dbc.Input(
278
value=helpers.get_home(),
279
type="text",
280
id=f"input-home-{product}",
281
placeholder="Path to directory",
282
persistence=True,
283
),
284
width=4,
285
),
286
],
287
class_name="mb-3",
288
)
289
290
291
def get_boot(product):
292
return dbc.Row(
293
[
294
dbc.Label(
295
[
296
f"Do you want {product.capitalize()} to automatically start on system startup?"
297
],
298
html_for=f"check-boot-{product}",
299
width=7,
300
),
301
dbc.Col(
302
dbc.Checkbox(id=f"check-boot-{product}", className="align-bottom"),
303
width=4,
304
),
305
],
306
class_name="mb-3",
307
)
308
309
310
def get_local_config():
311
return dbc.Row(
312
[
313
dbc.Label(
314
["Do you want to modify your stream configuration locally?"],
315
html_for="check-config-local",
316
width=7,
317
),
318
dbc.Col(
319
dbc.Checkbox(id="check-config-local", className="align-bottom"), width=4
320
),
321
],
322
class_name="mb-3",
323
)
324
325
326
def get_port(product):
327
return dbc.Row(
328
[
329
dbc.Label(
330
["Set the container port (default is 8080):"],
331
html_for=f"input-port-{product}",
332
width=7,
333
),
334
dbc.Col(
335
dbc.Input(
336
type="text",
337
id=f"input-port-{product}",
338
value="8080",
339
placeholder="Port",
340
persistence=True,
341
),
342
width=4,
343
),
344
],
345
class_name="mb-3",
346
)
347
348
349
def get_hardware_dropdown(product):
350
return dbc.Row(
351
[
352
dbc.Label(
353
"Docker image to use:", html_for=f"dropdown-hardware-{product}", width=7
354
),
355
dbc.Col(
356
dcc.Dropdown(
357
options=[
358
{"label": "Intel CPU", "value": "platerecognizer/alpr:latest"},
359
{
360
"label": "Raspberry",
361
"value": "platerecognizer/alpr-raspberry-pi:latest",
362
},
363
{
364
"label": "GPU (Nvidia Only)",
365
"value": "platerecognizer/alpr-gpu:latest",
366
},
367
{
368
"label": "Jetson Nano",
369
"value": "platerecognizer/alpr-jetson:latest",
370
},
371
{
372
"label": "ZCU104",
373
"value": "platerecognizer/alpr-zcu104:latest",
374
},
375
{"label": "Thailand", "value": "platerecognizer/alpr:thailand"},
376
],
377
value="platerecognizer/alpr:latest",
378
clearable=False,
379
id=f"dropdown-hardware-{product}",
380
style={"borderRadius": "0"},
381
persistence=True,
382
),
383
width=4,
384
),
385
],
386
class_name="mb-3",
387
)
388
389
390
def get_video_checkbox(product):
391
return dbc.Row(
392
[
393
dbc.Label(
394
[f"Use {product.capitalize()} on a local video file."],
395
html_for=f"check-video-{product}",
396
id=f"label-video-{product}",
397
width=7,
398
),
399
dbc.Col(
400
dbc.Checkbox(
401
id=f"check-video-{product}",
402
className="align-bottom",
403
persistence=True,
404
),
405
width=4,
406
),
407
],
408
class_name="mb-3",
409
)
410
411
412
def get_video_picker(product):
413
return dbc.Row(
414
[
415
dbc.Label(
416
[
417
f"Select a video file. If it is not inside your {product.capitalize()} folder, we will copy it there. Big files (~400Mb) may slow down your system."
418
],
419
html_for=f"pickup-video-{product}",
420
id=f"label-pickup-{product}",
421
width=7,
422
),
423
dcc.Loading(
424
type="circle", children=html.Div(id=f"loading-upload-{product}")
425
),
426
dbc.Col(
427
[
428
dcc.Upload(
429
[
430
dbc.Button("Upload File"),
431
html.Span(
432
"", id=f"span-videopath-{product}", className="me-2"
433
),
434
],
435
id=f"pickup-video-{product}",
436
accept="video/*",
437
)
438
],
439
width=4,
440
),
441
],
442
id=f"pickup-{product}",
443
class_name="mb-3",
444
)
445
446
447
def get_config_label(product):
448
return html.P(
449
[
450
"Edit your Stream configuration file. See the ",
451
html.A("documentation", href=STREAM_DOCS_LINK, target="_blank"),
452
" for details.",
453
],
454
id=f"label-config-{product}",
455
)
456
457
458
def get_config_body(product):
459
return dbc.Textarea(
460
size="sm",
461
class_name="mb-3",
462
id=f"area-config-{product}",
463
style={"width": WIDTH, "height": "300px"},
464
)
465
466
467
def get_status(product):
468
return html.P(
469
children="", style={"color": "red"}, className="mb-0", id=f"p-status-{product}"
470
)
471
472
473
def get_config_status():
474
return html.P(
475
children="", style={"color": "red"}, className="mb-0", id="p-status-config"
476
)
477
478
479
def get_success_card(product):
480
sdk_endpoint = ""
481
if product == SNAPSHOT:
482
sdk_endpoint = [
483
html.P(
484
" To use the SDK endpoint call: ",
485
className="card-title mt-3 mb-0",
486
style={"display": "inline-block"},
487
),
488
html.Code(id="curl-snapshot"),
489
]
490
return dbc.CardBody(
491
[
492
html.P(
493
f"You can now start {product.capitalize()}. Open a terminal and type the command below. You can save this command for future use.",
494
className="card-title",
495
),
496
html.Code(className="card-text d-block", id=f"command-{product}"),
497
html.Div(
498
[
499
html.Button(
500
"copy to clipboard",
501
id=f"copy-{product}",
502
**{"data-clipboard-target": f"#command-{product}"},
503
className="btn btn-sm btn-warning",
504
style={"borderRadius": "15px"},
505
),
506
html.Span(
507
id=f"copy-status-{product}",
508
className="ms-2",
509
style={"fontSize": "13px", "color": "green"},
510
),
511
],
512
className="mt-3",
513
),
514
html.P(sdk_endpoint, className="my-0"),
515
]
516
)
517
518
519
def get_continue(product):
520
return dbc.Row(
521
[
522
dbc.Col(
523
[
524
dbc.Button(
525
"Show Docker Command",
526
color="primary",
527
id=f"button-submit-{product}",
528
style={"width": "100%"},
529
)
530
],
531
width=2,
532
),
533
dbc.Label(
534
"Confirm settings and show docker command.",
535
html_for=f"button-submit-{product}",
536
className="col-auto align-self-center",
537
),
538
],
539
class_name="mt-3 mb-3",
540
)
541
542
543
def get_loading_submit(product):
544
return dcc.Loading(type="circle", children=html.Div(id=f"loading-submit-{product}"))
545
546
547
def get_confirm(product):
548
return (
549
dcc.ConfirmDialogProvider(
550
children=html.Button("Click Me"),
551
id="danger-danger-provider",
552
message="Danger danger! Are you sure you want to continue?",
553
),
554
)
555
556
557
def edit_config():
558
return dbc.Row(
559
[
560
dbc.Col(
561
[
562
dbc.Button(
563
"Configure",
564
color="success",
565
id="button-stream-config",
566
style={"width": "100%"},
567
)
568
],
569
width=2,
570
),
571
dbc.Label(
572
"Edit configurations for your Stream license",
573
html_for="button-stream-config",
574
className="col-auto align-self-center",
575
),
576
],
577
class_name="mt-3 mb-3",
578
)
579
580
581
############
582
# Dash App #
583
############
584
585
app = dash.Dash(
586
__name__,
587
title="Plate Recognizer Installer",
588
assets_folder=helpers.resource_path("assets"),
589
external_stylesheets=[dbc.themes.YETI],
590
external_scripts=[
591
"https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"
592
],
593
)
594
595
app.layout = dbc.Container(
596
[
597
get_splash_screen(),
598
html.H2(children="Plate Recognizer Installer", className="text-center my-3"),
599
dbc.Tabs(
600
[
601
dbc.Tab(
602
[
603
dbc.Form([get_refresh(STREAM)], class_name="mt-3"),
604
dbc.Form(
605
[
606
get_token(STREAM),
607
get_license_key(STREAM),
608
get_directory(STREAM),
609
get_boot(STREAM),
610
get_local_config(),
611
get_video_checkbox(STREAM),
612
get_video_picker(STREAM),
613
],
614
style=NONE,
615
class_name="mt-3",
616
id=f"form-{STREAM}",
617
),
618
html.Div(
619
[
620
get_config_label(STREAM),
621
get_config_body(STREAM),
622
get_config_status(),
623
get_status(STREAM),
624
dbc.Card(
625
[get_success_card(STREAM)],
626
id=f"card-{STREAM}",
627
className="mt-3",
628
style=NONE,
629
),
630
get_loading_submit(STREAM),
631
],
632
id=f"footer-{STREAM}",
633
className="mt-3",
634
style=NONE,
635
),
636
dbc.Form(
637
[
638
edit_config(),
639
get_continue(STREAM),
640
get_update(STREAM),
641
get_uninstall(STREAM),
642
],
643
style=NONE,
644
id=f"form-update-{STREAM}",
645
),
646
],
647
label=STREAM.capitalize(),
648
tab_id=STREAM,
649
className="offset-md-1 stream-tab",
650
),
651
dbc.Tab(
652
[
653
dbc.Form([get_refresh(SNAPSHOT)], class_name="mt-3"),
654
dbc.Form(
655
[
656
get_token(SNAPSHOT),
657
get_license_key(SNAPSHOT),
658
get_boot(SNAPSHOT),
659
get_port(SNAPSHOT),
660
get_hardware_dropdown(SNAPSHOT),
661
],
662
style=NONE,
663
class_name="mt-3",
664
id=f"form-{SNAPSHOT}",
665
),
666
html.Div(
667
[
668
get_status(SNAPSHOT),
669
dbc.Card(
670
[get_success_card(SNAPSHOT)],
671
id=f"card-{SNAPSHOT}",
672
className="mt-3",
673
style=NONE,
674
),
675
get_loading_submit(SNAPSHOT),
676
],
677
id=f"footer-{SNAPSHOT}",
678
className="mt-3",
679
style=NONE,
680
),
681
dbc.Form(
682
[
683
get_continue(SNAPSHOT),
684
get_update(SNAPSHOT),
685
get_uninstall(SNAPSHOT),
686
],
687
style=NONE,
688
id=f"form-update-{SNAPSHOT}",
689
),
690
],
691
label=SNAPSHOT.capitalize(),
692
tab_id=SNAPSHOT,
693
className="offset-md-1 snapshot-tab",
694
),
695
],
696
id="tabs",
697
active_tab=STREAM,
698
style={"width": "84%"},
699
class_name="offset-md-1 justify-content-center",
700
),
701
]
702
)
703
704
705
@app.callback(
706
[
707
Output("refresh-stream", "style"),
708
Output("update-stream", "style"),
709
Output("form-stream", "style"),
710
Output("form-update-stream", "style"),
711
Output("footer-stream", "style"),
712
Output("loading-refresh-stream", "children"),
713
Output("info-docker-stream", "style"),
714
Output("permissions-docker-stream", "style"),
715
],
716
[
717
Input("refresh-docker-stream", "n_clicks"),
718
Input("ok-uninstall-stream", "n_clicks"),
719
],
720
)
721
def refresh_docker_stream(n_clicks, uninstall):
722
try:
723
docker_is_ok = helpers.verify_docker_install()
724
except helpers.DockerPermissionError:
725
return FLEX, NONE, NONE, NONE, NONE, None, NONE, BLOCK
726
if docker_is_ok:
727
if (
728
dash.callback_context.triggered[0]["prop_id"]
729
== "ok-uninstall-stream.n_clicks"
730
):
731
time.sleep(2)
732
return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
733
if helpers.get_image(STREAM_IMAGE):
734
return NONE, FLEX, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
735
else:
736
return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
737
else:
738
return FLEX, NONE, NONE, NONE, NONE, None, BLOCK, NONE
739
740
741
@app.callback(
742
[
743
Output("refresh-snapshot", "style"),
744
Output("update-snapshot", "style"),
745
Output("form-snapshot", "style"),
746
Output("form-update-snapshot", "style"),
747
Output("footer-snapshot", "style"),
748
Output("loading-refresh-snapshot", "children"),
749
Output("info-docker-snapshot", "style"),
750
Output("permissions-docker-snapshot", "style"),
751
],
752
[
753
Input("refresh-docker-snapshot", "n_clicks"),
754
Input("dropdown-hardware-snapshot", "value"),
755
Input("ok-uninstall-snapshot", "n_clicks"),
756
],
757
)
758
def refresh_docker_snapshot(n_clicks, hardware, uninstall):
759
try:
760
docker_is_ok = helpers.verify_docker_install()
761
except helpers.DockerPermissionError:
762
return FLEX, NONE, NONE, NONE, NONE, None, NONE, BLOCK
763
if docker_is_ok:
764
if (
765
dash.callback_context.triggered[0]["prop_id"]
766
== "ok-uninstall-snapshot.n_clicks"
767
):
768
time.sleep(2)
769
return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
770
if helpers.get_image(hardware):
771
return NONE, FLEX, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
772
else:
773
return NONE, NONE, BLOCK, BLOCK, BLOCK, None, BLOCK, NONE
774
else:
775
return FLEX, NONE, NONE, NONE, NONE, None, BLOCK, NONE
776
777
778
@app.callback(
779
[Output("pickup-stream", "style")], [Input("check-video-stream", "value")]
780
)
781
def select_video(checked):
782
if checked:
783
return [FLEX]
784
else:
785
return [NONE]
786
787
788
@app.callback(
789
[
790
Output("pickup-video-stream", "style"),
791
Output("label-pickup-stream", "style"),
792
Output("check-video-stream", "style"),
793
Output("label-video-stream", "style"),
794
Output("area-config-stream", "style"),
795
Output("label-config-stream", "style"),
796
],
797
[Input("check-config-local", "value")],
798
)
799
def local_config(checked):
800
if checked:
801
config = {"display": "block", "width": WIDTH, "height": "300px"}
802
return [FLEX, FLEX, FLEX, FLEX, config, FLEX]
803
else:
804
return [NONE, NONE, NONE, NONE, NONE, NONE]
805
806
807
@app.callback(
808
[
809
Output("span-videopath-stream", "children"),
810
Output("loading-upload-stream", "children"),
811
],
812
[
813
Input("pickup-video-stream", "contents"),
814
State("pickup-video-stream", "filename"),
815
State("input-home-stream", "value"),
816
],
817
)
818
def set_videopath(content, name, path):
819
if content and name and path:
820
return [name, None]
821
elif name and path:
822
return ["File error", None]
823
else:
824
raise PreventUpdate
825
826
827
@app.callback(
828
[
829
Output("update-image-stream", "disabled"),
830
Output("span-update-stream", "style"),
831
Output("loading-update-stream", "children"),
832
],
833
[Input("update-image-stream", "n_clicks"), Input("tabs", "active_tab")],
834
)
835
def update_image_stream(n_clicks, tab):
836
if dash.callback_context.triggered[0]["prop_id"] == "update-image-stream.n_clicks":
837
helpers.pull_docker(STREAM_IMAGE)
838
return False, {"display": "inline", "color": "green"}, None
839
return False, NONE, None
840
841
842
@app.callback(
843
[
844
Output("update-image-snapshot", "disabled"),
845
Output("span-update-snapshot", "style"),
846
Output("loading-update-snapshot", "children"),
847
],
848
[
849
Input("update-image-snapshot", "n_clicks"),
850
Input("tabs", "active_tab"),
851
Input("dropdown-hardware-snapshot", "value"),
852
],
853
)
854
def update_image_snapshot(n_clicks, tab, hardware):
855
if (
856
dash.callback_context.triggered[0]["prop_id"]
857
== "update-image-snapshot.n_clicks"
858
):
859
helpers.pull_docker(hardware)
860
return False, {"display": "inline", "color": "green"}, None
861
return False, NONE, None
862
863
864
@app.callback(
865
Output("modal-uninstall-stream", "is_open"),
866
[
867
Input("uninstall-image-stream", "n_clicks"),
868
Input("ok-uninstall-stream", "n_clicks"),
869
Input("cancel-uninstall-stream", "n_clicks"),
870
],
871
[State("modal-uninstall-stream", "is_open")],
872
)
873
def toggle_modal_stream(n1, n2, n3, is_open):
874
if n1 or n2 or n3:
875
return not is_open
876
return is_open
877
878
879
@app.callback(
880
Output("modal-uninstall-snapshot", "is_open"),
881
[
882
Input("uninstall-image-snapshot", "n_clicks"),
883
Input("ok-uninstall-snapshot", "n_clicks"),
884
Input("cancel-uninstall-snapshot", "n_clicks"),
885
],
886
[State("modal-uninstall-snapshot", "is_open")],
887
)
888
def toggle_modal_snapshot(n1, n2, n3, is_open):
889
if n1 or n2 or n3:
890
return not is_open
891
return is_open
892
893
894
@app.callback(
895
[Output("uninstall-stream", "style")], [Input("ok-uninstall-stream", "n_clicks")]
896
)
897
def uninstall_button_stream(n_clicks):
898
if dash.callback_context.triggered[0]["prop_id"] == "ok-uninstall-stream.n_clicks":
899
time.sleep(2)
900
return [NONE]
901
if not helpers.verify_docker_install():
902
return [NONE]
903
if helpers.get_image(STREAM_IMAGE):
904
return [FLEX]
905
else:
906
return [NONE]
907
908
909
@app.callback(
910
[Output("uninstall-snapshot", "style")],
911
[
912
Input("ok-uninstall-snapshot", "n_clicks"),
913
Input("dropdown-hardware-snapshot", "value"),
914
],
915
)
916
def uninstall_button_snapshot(n_clicks, hardware):
917
if (
918
dash.callback_context.triggered[0]["prop_id"]
919
== "ok-uninstall-snapshot.n_clicks"
920
):
921
time.sleep(2)
922
return [NONE]
923
if not helpers.verify_docker_install():
924
return [NONE]
925
if helpers.get_image(hardware):
926
return [FLEX]
927
else:
928
return [NONE]
929
930
931
clientside_callback(
932
"""
933
function(n_clicks) {
934
if (n_clicks > 0) {
935
const key = document.getElementById("input-key-stream").value;
936
if (key) {
937
const url = "https://app.platerecognizer.com/stream-config/" + key;
938
window.open(url, "_blank");
939
} else {
940
const statusConfig = document.getElementById("p-status-config");
941
statusConfig.innerHTML = "License key is required.";
942
}
943
}
944
}
945
""",
946
[Output("button-stream-config", "style"), Output("p-status-config", "children")],
947
[Input("button-stream-config", "n_clicks"), State("input-key-stream", "value")],
948
prevent_initial_call=True,
949
)
950
951
952
@app.callback(
953
[
954
Output("loading-uninstall-stream", "children"),
955
Output("span-uninstall-stream", "children"),
956
],
957
[
958
Input("ok-uninstall-stream", "n_clicks"),
959
State("input-token-stream", "value"),
960
State("input-key-stream", "value"),
961
],
962
)
963
def uninstall_stream(n_clicks, token, key):
964
if dash.callback_context.triggered[0]["prop_id"] == "ok-uninstall-stream.n_clicks":
965
if not helpers.get_image(STREAM_IMAGE):
966
return [None, "Image already uninstalled."]
967
helpers.stop_container(STREAM_IMAGE)
968
return helpers.uninstall_docker_image(STREAM_IMAGE)
969
raise PreventUpdate
970
971
972
@app.callback(
973
[
974
Output("loading-uninstall-snapshot", "children"),
975
Output("span-uninstall-snapshot", "children"),
976
],
977
[
978
Input("ok-uninstall-snapshot", "n_clicks"),
979
Input("dropdown-hardware-snapshot", "value"),
980
State("input-token-snapshot", "value"),
981
State("input-key-snapshot", "value"),
982
],
983
)
984
def uninstall_snapshot(n_clicks, hardware, token, key):
985
if (
986
dash.callback_context.triggered[0]["prop_id"]
987
== "dropdown-hardware-snapshot.value"
988
):
989
return [None, ""]
990
if (
991
dash.callback_context.triggered[0]["prop_id"]
992
== "ok-uninstall-snapshot.n_clicks"
993
):
994
if not helpers.get_image(hardware):
995
return [None, "Image already uninstalled."]
996
verification = helpers.verify_token(token, key, product=SNAPSHOT)
997
if verification[0]:
998
helpers.stop_container(hardware)
999
cmd = f"docker run --rm -t -v license:/license -e TOKEN={token} -e LICENSE_KEY={key} -e UNINSTALL=1 {hardware}"
1000
os.system(cmd)
1001
return helpers.uninstall_docker_image(hardware)
1002
else:
1003
return [None, verification[1]]
1004
raise PreventUpdate
1005
1006
1007
@app.callback(
1008
Output("area-config-stream", "value"),
1009
[
1010
Input("input-home-stream", "value"),
1011
Input("pickup-video-stream", "filename"),
1012
State("check-video-stream", "value"),
1013
],
1014
)
1015
def change_path(home, videofile, videocheck):
1016
config = helpers.read_config(home)
1017
if videofile and videocheck: # replace url with a path to video
1018
url = re.search("url = (.*)\n", config).group(1)
1019
config = re.sub(url, f"{USER_DATA}{videofile}", config)
1020
return config
1021
1022
1023
@app.callback(
1024
[
1025
Output("p-status-stream", "children"),
1026
Output("card-stream", "style"),
1027
Output("command-stream", "children"),
1028
Output("loading-submit-stream", "children"),
1029
],
1030
[
1031
Input("area-config-stream", "value"),
1032
Input("button-submit-stream", "n_clicks"),
1033
Input("input-token-stream", "value"),
1034
Input("input-key-stream", "value"),
1035
Input("input-home-stream", "value"),
1036
Input("check-boot-stream", "value"),
1037
State("pickup-video-stream", "contents"),
1038
State("pickup-video-stream", "filename"),
1039
State("check-video-stream", "value"),
1040
],
1041
)
1042
def submit_stream(
1043
config, n_clicks, token, key, home, boot, videocontent, videofile, videocheck
1044
):
1045
if dash.callback_context.triggered[0]["prop_id"] == "button-submit-stream.n_clicks":
1046
is_valid, error = helpers.verify_token(token, key, product="stream")
1047
if is_valid:
1048
write_result, error = helpers.write_config(home, config)
1049
if not write_result:
1050
return error, NONE, "", None
1051
if videocontent and videofile and videocheck:
1052
content_type, content_string = videocontent.split(",")
1053
decoded = base64.b64decode(content_string)
1054
with open(os.path.join(home, videofile), "wb") as video:
1055
video.write(decoded)
1056
user_info = ""
1057
nvidia = ""
1058
image_tag = ""
1059
autoboot = "--rm"
1060
if helpers.get_os() != "Windows":
1061
user_info = "--user `id -u`:`id -g`"
1062
if os.path.exists("/etc/nv_tegra_release"):
1063
nvidia = "--runtime nvidia --privileged --group-add video"
1064
image_tag = ":jetson"
1065
if boot:
1066
autoboot = "--restart unless-stopped"
1067
if not helpers.get_image(STREAM_IMAGE):
1068
helpers.pull_docker(STREAM_IMAGE)
1069
command = (
1070
f"docker run {autoboot} -t "
1071
f"{nvidia} --name stream "
1072
f'-v "{home}:{USER_DATA}" '
1073
f"{user_info} "
1074
f"-e LICENSE_KEY={key} "
1075
f"-e TOKEN={token} "
1076
f"{STREAM_IMAGE}{image_tag}"
1077
)
1078
return "", DISPLAY_CARD, command, None
1079
else:
1080
return error, NONE, "", None
1081
else:
1082
return "", NONE, "", None
1083
1084
1085
@app.callback(
1086
[
1087
Output("p-status-snapshot", "children"),
1088
Output("card-snapshot", "style"),
1089
Output("command-snapshot", "children"),
1090
Output("curl-snapshot", "children"),
1091
Output("loading-submit-snapshot", "children"),
1092
],
1093
[
1094
Input("button-submit-snapshot", "n_clicks"),
1095
Input("input-token-snapshot", "value"),
1096
Input("input-key-snapshot", "value"),
1097
Input("check-boot-snapshot", "value"),
1098
Input("input-port-snapshot", "value"),
1099
Input("dropdown-hardware-snapshot", "value"),
1100
],
1101
)
1102
def submit_snapshot(n_clicks, token, key, boot, port, hardware):
1103
if (
1104
dash.callback_context.triggered[0]["prop_id"]
1105
== "button-submit-snapshot.n_clicks"
1106
):
1107
is_valid, error = helpers.verify_token(token, key, product="snapshot")
1108
if is_valid:
1109
autoboot = "--restart unless-stopped" if boot else "--rm"
1110
if not helpers.is_valid_port(port):
1111
return "Wrong port", NONE, "", None, None
1112
if not helpers.get_image(hardware):
1113
helpers.pull_docker(hardware)
1114
gpus = "--gpus all" if "gpu" in hardware else ""
1115
nvidia = "--runtime nvidia" if "jetson" in hardware else ""
1116
command = (
1117
f"docker run {gpus} {nvidia} {autoboot} "
1118
f"-t -p {port}:8080 "
1119
f"-v license:/license "
1120
f"-e LICENSE_KEY={key} "
1121
f"-e TOKEN={token} "
1122
f"{hardware}"
1123
)
1124
curl = f' curl -F "upload=@my_file.jpg" http://localhost:{port}/v1/plate-reader/'
1125
return "", DISPLAY_CARD, command, curl, None
1126
else:
1127
return error, NONE, "", None, None
1128
else:
1129
return "", NONE, "", None, None
1130
1131
1132
@app.callback(
1133
Output("copy-status-stream", "children"),
1134
[Input("copy-stream", "n_clicks"), Input("button-submit-stream", "n_clicks")],
1135
)
1136
def copy_to_clipboard_stream(n_clicks, n_clicks_submit):
1137
if dash.callback_context.triggered[0]["prop_id"] == "copy-stream.n_clicks":
1138
return "Item copied to clipboard."
1139
else:
1140
return ""
1141
1142
1143
@app.callback(
1144
Output("copy-status-snapshot", "children"),
1145
[Input("copy-snapshot", "n_clicks"), Input("button-submit-snapshot", "n_clicks")],
1146
)
1147
def copy_to_clipboard_snapshot(n_clicks, n_clicks_submit):
1148
if dash.callback_context.triggered[0]["prop_id"] == "copy-snapshot.n_clicks":
1149
return "Item copied to clipboard."
1150
else:
1151
return ""
1152
1153
1154
def parse_arguments():
1155
parser = argparse.ArgumentParser(description="")
1156
parser.add_argument("--debug", action="store_true")
1157
return parser.parse_args()
1158
1159
1160
if __name__ == "__main__":
1161
args = parse_arguments()
1162
print(CONSOLE_WELCOME)
1163
if args.debug:
1164
app.run_server(debug=True, host="0.0.0.0")
1165
else:
1166
helpers.launch_browser("http://127.0.0.1:8050/")
1167
1168
# Update log levels
1169
app.logger.setLevel(logging.ERROR)
1170
log = logging.getLogger("werkzeug")
1171
log.setLevel(logging.ERROR)
1172
cli = sys.modules["flask.cli"]
1173
cli.show_server_banner = lambda *_: None # type: ignore
1174
1175
# Start server
1176
app.run_server(
1177
debug=False, host="0.0.0.0", dev_tools_silence_routes_logging=True
1178
)
1179
1180