info field. ability to go wwithout defaults

This commit is contained in:
2022-05-27 17:14:31 +03:00
parent 8391ad9850
commit 93705f758f
7 changed files with 194 additions and 95 deletions

View File

@@ -18,41 +18,69 @@ Setup:
{ {
"type": "checkbox", "type": "checkbox",
"name": "my_check", "name": "my_check",
"default": "off" "title": "Click to enable",
"default": "on"
},
{
"type": "info",
"title": "Piece of text shown here."
}, },
{ {
"type": "number", "type": "number",
"name": "my_number", "name": "my_number",
"default": "1.0" "default": "1.0",
"title": "Only numbers allowed."
}, },
{ {
"type": "text", "type": "text",
"name": "my_text", "name": "my_text",
"title": "Any text here",
"default": "some text" "default": "some text"
}, },
{ {
"type": "range", "type": "range",
"name": "my_slider", "name": "my_slider",
"title": "Slide away!",
"default": "75", "default": "75",
"min": "0", "min": "0",
"max": "100" "max": "100"
}, },
{ {
"type": "select", "type": "select",
"title": "Select any from these",
"name": "my_selection", "name": "my_selection",
"default": "select2", "default": "select2",
"options": [ "options": [
"option1", "option1",
"select2" "select2",
"select4",
"select5",
"select9"
] ]
} }
] ]
} }
``` ```
- Copy some images under data/images/ - Copy some images under data/images/
- Start the docker instance - Start the docker instance
- Open the URL in http://localhost:$EXPOSE - Open the URL in http://localhost:$EXPOSE
## Label types
Label entries require "type" and "name". All labels can include
a "title" field, which is added as a hover-on text, and "default" for
the default value.
In some cases more fields required.
- checkbox: If "default": "on", checkbox is selected. Otherwise it is unselected.
- text: Any string, if no default: ""
- number: Any number entry, if no default: ""
- range: Requires "min" and "max" values. if no default, default = min.
- select: Requires a list of "options".
- info: Not a selection. Add "title" field to show text instead.
## With nginx: ## With nginx:
``` ```

View File

@@ -15,6 +15,7 @@ from flask import (
from revprox import ReverseProxied from revprox import ReverseProxied
# configuration # configuration
VERSION = "2022.05.27"
IMAGEDIR = "/data/images/" IMAGEDIR = "/data/images/"
LABELDIR = "/data/labels/" LABELDIR = "/data/labels/"
CONFIG_FILE = "/data/config.json" CONFIG_FILE = "/data/config.json"
@@ -33,6 +34,7 @@ app.wsgi_app = ReverseProxied(app.wsgi_app)
@app.before_request @app.before_request
def before_request_func(): def before_request_func():
g.version = app.config["VERSION"]
try: try:
with open(app.config["CONFIG_FILE"], "rt") as fp: with open(app.config["CONFIG_FILE"], "rt") as fp:
g.config = json.load(fp) g.config = json.load(fp)
@@ -43,7 +45,7 @@ def before_request_func():
logging.warning("config.json could not be read. using defaults.") logging.warning("config.json could not be read. using defaults.")
g.labels = [ g.labels = [
{"type": "checkbox", "name": "my_check", "default": "off"}, {"type": "checkbox", "name": "my_check", "default": "off"},
{"type": "text", "name": "my_text", "default": "1.0"}, {"type": "text", "name": "my_text", "default": "Some text"},
{ {
"type": "range", "type": "range",
"name": "my_slider", "name": "my_slider",
@@ -55,8 +57,14 @@ def before_request_func():
g.config = {"title": "Labeler", "labels": g.labels} g.config = {"title": "Labeler", "labels": g.labels}
g.users = ["user"] g.users = ["user"]
if not "title" in g.config:
g.config["title"] = "Labeler"
for label in g.labels: for label in g.labels:
label["value"] = label["default"] if label["type"] == "range":
label["value"] = label.get("default", label.get("min", ""))
else:
label["value"] = label.get("default", "")
def natural_key(string_): def natural_key(string_):
@@ -152,12 +160,7 @@ def main(user=None):
user_name = get_user() user_name = get_user()
return render_template( return render_template("main.html", current_user=user_name)
"main.html",
current_user=user_name,
users=g.users,
title=g.config.get("title", "Labeler"),
)
@app.route("/image", methods=["GET", "POST"]) @app.route("/image", methods=["GET", "POST"])
@@ -194,5 +197,4 @@ def show_image(id=None):
labels=labels, labels=labels,
id_minus=id_minus, id_minus=id_minus,
id_plus=id_plus, id_plus=id_plus,
title=g.config.get("title", "Labeler"),
) )

View File

@@ -1,5 +1,5 @@
class ReverseProxied(object): class ReverseProxied(object):
'''Wrap the application in this middleware and configure the """Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is this to a URL other than / and to an HTTP scheme that is
different than what is used locally. different than what is used locally.
@@ -14,19 +14,20 @@ class ReverseProxied(object):
} }
:param app: the WSGI application :param app: the WSGI application
''' """
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
script_name = environ.get('HTTP_X_SCRIPT_NAME', '') script_name = environ.get("HTTP_X_SCRIPT_NAME", "")
if script_name: if script_name:
environ['SCRIPT_NAME'] = script_name environ["SCRIPT_NAME"] = script_name
path_info = environ['PATH_INFO'] path_info = environ["PATH_INFO"]
if path_info.startswith(script_name): if path_info.startswith(script_name):
environ['PATH_INFO'] = path_info[len(script_name):] environ["PATH_INFO"] = path_info[len(script_name) :]
scheme = environ.get('HTTP_X_SCHEME', '') scheme = environ.get("HTTP_X_SCHEME", "")
if scheme: if scheme:
environ['wsgi.url_scheme'] = scheme environ["wsgi.url_scheme"] = scheme
return self.app(environ, start_response) return self.app(environ, start_response)

View File

@@ -1,37 +1,87 @@
body { font-family: sans-serif; background: #888; margin: 0px; body {
min-height: 100vh; width: 100vw; overflow-x: hidden; } font-family: sans-serif;
a, h1, h2 { color: #377ba8; } background: #888;
h1, h2 { font-family: 'Georgia', serif; margin: 0; } margin: 0px;
h1 { border-bottom: 2px solid #eee; } min-height: 100vh;
h2 { font-size: 1.2em; } width: 100vw;
overflow-x: hidden;
}
tr,td,tbody { margin: 0px; } .page {
margin: 0em;
padding: 0em;
background: #888;
min-height: 100vh;
width: 100vw;
}
.page { margin: 0em; .entries {
padding: 0em; background: #888; min-height: 100vh; width: 100vw;} list-style: none;
.entries { list-style: none; margin: 0; padding: 0; width:100%; min-height: 95vh; } margin: 0;
padding: 0;
width: 100%;
min-height: 95vh;
}
.large { font-size: 3em; } .large {
.right { text-align: right; } font-size: 3em;
.center { text-align: center; } }
#image { position: absolute; .right {
left:0px; top:0px; text-align: right;
width: calc(100vw - 220px); /*height:95vh;*/ } }
#img { max-width: calc(100vw - 220px); max-height:95vh;
width: auto; height: auto;
display: block; margin-left: auto;
margin-right: auto; }
#img_title { overflow-x: hidden; .center {
text-align: center;
}
#home {
position: absolute;
left: 0px;
bottom: 0px;
}
#home a {
color: black;
font-weight: bold;
font-size: large;
background-color: #aaa;
border-radius: 5px;
padding: 2px;
}
#image {
position: absolute;
left: 0px;
top: 0px;
width: calc(100vw - 220px);
/*height:95vh;*/
}
#img {
max-width: calc(100vw - 220px);
max-height: 95vh;
width: auto;
height: auto;
display: block;
margin-left: auto;
margin-right: auto;
}
#img_title {
overflow-x: hidden;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
#topright { position: absolute; #topright {
right:0px; top:0px; position: absolute;
right: 0px;
top: 0px;
/*width: 20vw;*/ /*width: 20vw;*/
height: 95vh; height: 95vh;
font-size: large; } font-size: large;
}
.inputcontainer { .inputcontainer {
z-index: 1; z-index: 1;
background-color: #aaa; background-color: #aaa;
@@ -42,24 +92,34 @@ tr,td,tbody { margin: 0px; }
} }
input, select { input,
select {
width: 180px; width: 180px;
font-size: large; font-size: large;
} }
input[type="text"] { input[type="text"] {}
} input[type="submit"] {}
input[type="submit"] {
}
input[type="checkbox"] { input[type="checkbox"] {
width: 3em; width: 3em;
height: 1.5em; height: 1.5em;
} }
label { display: block; }
output { display: block; font-weight: bold; } label {
display: block;
}
output {
display: block;
font-weight: bold;
}
.info {
font-size: initial;
}
.button_next { .button_next {
width: 2em; width: 2em;
@@ -67,12 +127,15 @@ output { display: block; font-weight: bold; }
font-size: large; font-size: large;
margin-top: 1em; margin-top: 1em;
} }
.float_left { .float_left {
float: left; float: left;
} }
.float_right { .float_right {
float: right; float: right;
} }
.button_save { .button_save {
height: 3em; height: 3em;
margin-top: 1em; margin-top: 1em;
@@ -80,6 +143,7 @@ output { display: block; font-weight: bold; }
margin-right: 1em; margin-right: 1em;
clear: both; clear: both;
} }
.button_continue { .button_continue {
height: 4em; height: 4em;
margin-top: 3em; margin-top: 3em;

View File

@@ -1,14 +1,15 @@
<!doctype html> <!DOCTYPE html>
<html>
<head> <head>
<title>{{ title }}</title> <title>{{ g.config.title }}</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<meta name="viewport" content="width=440" /> <meta name="viewport" content="width=440">
<meta name="VERSION" content="{{ g.version }}">
<script language="javascript" src="{{ url_for('static', filename='script.js') }}"></script> <script language="javascript" src="{{ url_for('static', filename='script.js') }}"></script>
</head> </head>
<body> <body>
<div class=page> <div class="page">
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div> </div>
</body> </body>
</html>

View File

@@ -1,12 +1,11 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
<div class="entries"> <div class="entries">
<div class="center inputcontainer"> <div class="center inputcontainer">
<form action="{{ url_for('main') }}" method=post class=add-entry> <form action="{{ url_for('main') }}" method=post class=add-entry>
<label>Choose user:</label> <label>Choose user:</label>
<select name="user_name"> <select name="user_name">
{% for user in users %} {% for user in g.users %}
<option value="{{user}}" {% if user == current_user %}SELECTED{% endif %} >{{user}}</option> <option value="{{user}}" {% if user == current_user %}SELECTED{% endif %} >{{user}}</option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -11,29 +11,33 @@
<input type=hidden value="{{ image_name }}" name=image_name> <input type=hidden value="{{ image_name }}" name=image_name>
{% for label in labels %} {% for label in labels %}
<div class=inputcontainer> <div class=inputcontainer>
{% if label.type == "info" %}
<label class=info>{{ label.title }}</label>
{% else %}
<label>{{ label.name }}:</label> <label>{{ label.name }}:</label>
<div class=center> <div class=center>
{% if label.type == "checkbox" %} {% if label.type == "checkbox" %}
<input class=center type="checkbox" name="label_{{ label.name }}" {% if label.value == "on" %}checked{% endif %}> <input class=center type="checkbox" name="label_{{ label.name }}" {% if label.value == "on" %}checked{% endif %} title="{{label.title}}">
{% endif %} {% endif %}
{% if label.type == "text" %} {% if label.type == "text" %}
<input type="text" name="label_{{ label.name }}" value="{{ label.value }}"> <input type="text" name="label_{{ label.name }}" value="{{ label.value }}" title="{{label.title}}">
{% endif %} {% endif %}
{% if label.type == "number" %} {% if label.type == "number" %}
<input type="number" step="any" name="label_{{ label.name }}" value="{{ label.value }}"> <input type="number" step="any" name="label_{{ label.name }}" value="{{ label.value }}" title="{{label.title}}">
{% endif %} {% endif %}
{% if label.type == "range" %} {% if label.type == "range" %}
<input type="range" name="label_{{ label.name }}" value="{{ label.value }}" min="{{ label.min }}" max="{{ label.max }}" oninput="this.nextElementSibling.value = this.value"> <input type="range" name="label_{{ label.name }}" value="{{ label.value }}" min="{{ label.min }}" max="{{ label.max }}" oninput="this.nextElementSibling.value = this.value" title="{{label.title}}">
<output>{{label.value}}</output> <output>{{label.value}}</output>
{% endif %} {% endif %}
{% if label.type == "select" %} {% if label.type == "select" %}
<select name="label_{{ label.name }}"> <select name="label_{{ label.name }}" title="{{label.title}}">
{% for opt in label.options %} {% for opt in label.options %}
<option value="{{opt}}" {% if opt == label.value %}SELECTED{% endif %}>{{opt}}</option> <option value="{{opt}}" {% if opt == label.value %}SELECTED{% endif %}>{{opt}}</option>
{% endfor %} {% endfor %}
</select> </select>
{% endif %} {% endif %}
</div> </div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
<div class=center> <div class=center>
@@ -45,8 +49,8 @@
<button class="button_next float_right" onclick="location.href='{{ url_for('show_image', id = id_plus) }}';">&rarr;</button> <button class="button_next float_right" onclick="location.href='{{ url_for('show_image', id = id_plus) }}';">&rarr;</button>
<button class="button_continue" onclick="location.href='{{ url_for('show_image') }}';">continue</button> <button class="button_continue" onclick="location.href='{{ url_for('show_image') }}';">continue</button>
</div> </div>
</div> </div>
<div id="home"><a href="{{ url_for('main') }}" title="Back to user selection">🏠</a></div>
</div> </div>
<img class=preload src="{{ url_for('static', filename=image_plus) }}" > <img class=preload src="{{ url_for('static', filename=image_plus) }}" >