Working on acquisition
This commit is contained in:
parent
2691df360f
commit
586c585980
92
app.py
92
app.py
|
|
@ -6,6 +6,7 @@ import os
|
|||
from os.path import join
|
||||
import sqlite3
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from . import db, config, scanner, calibration
|
||||
|
||||
app = Flask(__name__)
|
||||
|
|
@ -33,7 +34,7 @@ def get_calibration(conn: sqlite3.Connection) -> db.Calibration:
|
|||
@app.context_processor
|
||||
def inject_stage_and_region():
|
||||
conn = db.get()
|
||||
return dict(calibration=get_calibration(conn), CalibrationState=db.CalibrationState)
|
||||
return dict(calibration=get_calibration(conn), leds=config.LEDS_UUIDS, CalibrationState=db.CalibrationState)
|
||||
|
||||
|
||||
@app.before_request
|
||||
|
|
@ -63,15 +64,33 @@ def create_object():
|
|||
@app.route('/object/<id>')
|
||||
def object(id: int):
|
||||
conn = db.get()
|
||||
object = db.Object.get_from_id(id, conn)
|
||||
object = db.Object.get_from_id(id, conn).full(conn)
|
||||
return render_template('object.html', object=object)
|
||||
|
||||
|
||||
@app.route('/scan/<id>')
|
||||
def scan(id: int):
|
||||
conn = db.get()
|
||||
calibration_id = session.get('calibration_id', None)
|
||||
object = db.Object.get_from_id(id, conn)
|
||||
return render_template('object.html', object=object)
|
||||
|
||||
if calibration_id is None:
|
||||
raise RuntimeError("Impossible de faire l'acquisition sans étalonnage")
|
||||
|
||||
return render_template('scan.html', object=object)
|
||||
|
||||
|
||||
@app.route('/scan-acquisition/<id>')
|
||||
def scan_existing(id: int):
|
||||
conn = db.get()
|
||||
calibration_id = session.get('calibration_id', None)
|
||||
acquisition = db.Acquisition.get_from_id(id, conn)
|
||||
object = acquisition.object(conn)
|
||||
|
||||
if calibration_id is None:
|
||||
raise RuntimeError("Impossible de faire l'acquisition sans étalonnage")
|
||||
|
||||
return render_template('scan.html', object=object, acquisition=acquisition)
|
||||
|
||||
|
||||
@app.route("/calibrate/")
|
||||
|
|
@ -85,7 +104,7 @@ def calibrate():
|
|||
calibration = db.Calibration.get_from_id(session['calibration_id'], conn)
|
||||
|
||||
if calibration.state in [db.CalibrationState.Empty, db.CalibrationState.HasData]:
|
||||
return render_template('calibrate.html', leds=config.LEDS_UUIDS)
|
||||
return render_template('calibrate.html')
|
||||
else:
|
||||
return render_template('calibration.html', calibration=calibration)
|
||||
|
||||
|
|
@ -146,6 +165,71 @@ def scan_calibration():
|
|||
return app.response_class(generate(), mimetype='text/plain')
|
||||
|
||||
|
||||
@app.route("/api/scan-for-object/<object_id>")
|
||||
def scan_object(object_id: int):
|
||||
conn = db.get()
|
||||
calibration_id = session.get('calibration_id', None)
|
||||
|
||||
if calibration_id is None:
|
||||
raise RuntimeError("Impossible de faire l'acquisition sans étalonnage")
|
||||
|
||||
object = db.Object.get_from_id(object_id, conn)
|
||||
|
||||
if object is None:
|
||||
raise RuntimeError(f"Aucun objet d'id {object_id}")
|
||||
|
||||
with conn:
|
||||
acquisition = object.add_acquisition(calibration_id, conn)
|
||||
|
||||
def generate():
|
||||
yield str(acquisition.id)
|
||||
length = len(config.LEDS_UUIDS)
|
||||
for index, led_uuid in enumerate(scanner.scan(join(config.OBJECT_DIR, str(object.id), str(acquisition.id)))):
|
||||
yield f"{led_uuid},{(index+1)/length}\n"
|
||||
|
||||
return app.response_class(generate(), mimetype='text/plain')
|
||||
|
||||
|
||||
@app.route("/api/scan-for-acquisition/<acquisition_id>")
|
||||
def scan_acquisition(acquisition_id: int):
|
||||
conn = db.get()
|
||||
calibration_id = session.get('calibration_id', None)
|
||||
|
||||
if calibration_id is None:
|
||||
raise RuntimeError("Impossible de faire l'acquisition sans étalonnage")
|
||||
|
||||
acquisition = db.Acquisition.get_from_id(acquisition_id, conn)
|
||||
|
||||
if acquisition is None:
|
||||
raise RuntimeError(f"Aucun acquisition d'id {acquisition_id}")
|
||||
|
||||
object = acquisition.object(conn)
|
||||
|
||||
def generate():
|
||||
length = len(config.LEDS_UUIDS)
|
||||
for index, led_uuid in enumerate(scanner.scan(join(config.OBJECT_DIR, str(object.id), str(acquisition.id)))):
|
||||
yield f"{led_uuid},{(index+1)/length}\n"
|
||||
|
||||
return app.response_class(generate(), mimetype='text/plain')
|
||||
|
||||
|
||||
@app.route("/validate-acquisition/<acquisition_id>")
|
||||
def validate_acquisition(acquisition_id: int):
|
||||
conn = db.get()
|
||||
acquisition = db.Acquisition.get_from_id(acquisition_id, conn)
|
||||
|
||||
if acquisition is None:
|
||||
raise f"Aucune acquisition d'id {acquisition_id}"
|
||||
|
||||
object = acquisition.object(conn)
|
||||
|
||||
acquisition.validated = True
|
||||
with conn:
|
||||
acquisition.save(conn)
|
||||
|
||||
return redirect(f'/object/{object.id}')
|
||||
|
||||
|
||||
@app.route('/api/use-last-calibration')
|
||||
def use_last_calibration():
|
||||
conn = db.get()
|
||||
|
|
|
|||
77
db.py
77
db.py
|
|
@ -128,18 +128,20 @@ Calibration.Dummy = Calibration(-1, CalibrationState.Empty, None)
|
|||
class Acquisition:
|
||||
@staticmethod
|
||||
def select_args() -> str:
|
||||
return 'id, calibration_id, object_id'
|
||||
return 'id, calibration_id, object_id, date, validated'
|
||||
|
||||
def __init__(self, acquisition_id: int, calibration_id: int, object_id: int):
|
||||
self.id = object_id
|
||||
def __init__(self, acquisition_id: int, calibration_id: int, object_id: int, date: int, validated: int):
|
||||
self.id = acquisition_id
|
||||
self.calibration_id = calibration_id
|
||||
self.object_id = object_id
|
||||
self.date = datetime.fromtimestamp(date)
|
||||
self.validated = validated != 0
|
||||
|
||||
def save(self, db: sqlite3.Connection):
|
||||
cur = db.cursor()
|
||||
cur.execute(
|
||||
'UPDATE acquisition SET calibration_id = ?, object_id = ? WHERE id = ?',
|
||||
[self.calibration_id, self.object_id, self.id]
|
||||
'UPDATE acquisition SET calibration_id = ?, object_id = ?, date = ?, validated = ? WHERE id = ?',
|
||||
[self.calibration_id, self.object_id, self.date.timestamp(), 1 if self.validated else 0, self.id]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -163,6 +165,19 @@ class Acquisition:
|
|||
|
||||
return Calibration.get_from_id(self.calibration_id, db)
|
||||
|
||||
def object(self, db: sqlite3.Connection) -> 'Object':
|
||||
return Object.get_from_id(self.object_id, db)
|
||||
|
||||
def get_pretty_date(self) -> str:
|
||||
return dateutils.format(self.date)
|
||||
|
||||
|
||||
class FullObject:
|
||||
def __init__(self, object_id: int, name: str, acquisitions: list[Acquisition]):
|
||||
self.id = object_id
|
||||
self.name = name
|
||||
self.acquisitions = acquisitions
|
||||
|
||||
|
||||
class Object:
|
||||
@staticmethod
|
||||
|
|
@ -218,11 +233,21 @@ class Object:
|
|||
def add_acquisition(self, calibration_id: int, db: sqlite3.Connection) -> Acquisition:
|
||||
cur = db.cursor()
|
||||
response = cur.execute(
|
||||
'INSERT INTO acquisition(calibration_id, object_id) VALUES (?, ?) RETURNING ' + Acquisition.select_args() + ';',
|
||||
[calibration_id, self.id]
|
||||
'INSERT INTO acquisition(calibration_id, object_id, date, validated) VALUES (?, ?, ?, ?) RETURNING ' + Acquisition.select_args() + ';',
|
||||
[calibration_id, self.id, datetime.now().timestamp(), 0]
|
||||
)
|
||||
return Acquisition.from_row(response.fetchone())
|
||||
|
||||
def full(self, db: sqlite3.Connection) -> FullObject:
|
||||
cur = db.cursor()
|
||||
response = cur.execute(
|
||||
'SELECT ' + Acquisition.select_args() + ' FROM acquisition WHERE object_id = ? ORDER BY date DESC;',
|
||||
[self.id]
|
||||
)
|
||||
acquisitions = list(map(lambda x: Acquisition.from_row(x), response.fetchall()))
|
||||
print(acquisitions)
|
||||
return FullObject(self.id, self.name, acquisitions)
|
||||
|
||||
|
||||
def main():
|
||||
# Move current data to backup dir
|
||||
|
|
@ -245,26 +270,26 @@ def main():
|
|||
init(db)
|
||||
|
||||
# Create a new object
|
||||
with db:
|
||||
Object.create('Mon premier objet', db)
|
||||
# calibration = Calibration.create(db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# calibration = Calibration.create(db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# with db:
|
||||
# Object.create('Mon premier objet', db)
|
||||
# # calibration = Calibration.create(db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # calibration = Calibration.create(db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
|
||||
Object.create('Mon deuxième objet', db)
|
||||
# calibration = Calibration.create(db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# calibration = Calibration.create(db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# object.add_acquisition(calibration.id, db)
|
||||
# Object.create('Mon deuxième objet', db)
|
||||
# # calibration = Calibration.create(db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # calibration = Calibration.create(db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
# # object.add_acquisition(calibration.id, db)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ CREATE TABLE acquisition (
|
|||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
calibration_id INTEGER NOT NULL,
|
||||
object_id INTEGER NOT NULL,
|
||||
date INTEGER NOT NULL,
|
||||
validated INT NOT NULL,
|
||||
CONSTRAINT fk_calibration FOREIGN KEY(calibration_id) REFERENCES calibration(id),
|
||||
CONSTRAINT fk_object FOREIGN KEY(object_id) REFERENCES object(id)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
<div id="calibrate" {% if calibration.state == 0 %}style="display: none;" {% endif %}>
|
||||
<p>Si les données d'étalonnage conviennent, appuyez sur le bouton ci-dessous pour procéder à l'étalonnage du scanner.</p>
|
||||
<button id="calibrate-button" class="button is-link">Étalonner le scanner</button>
|
||||
<p id="calibration-info"></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -53,7 +52,6 @@
|
|||
let errorContent = document.getElementById('error-content');
|
||||
let calibrateDiv = document.getElementById('calibrate');
|
||||
let calibrateButton = document.getElementById('calibrate-button');
|
||||
let calibrationInfo = document.getElementById('calibration-info');
|
||||
|
||||
previewButton.addEventListener('click', async () => {
|
||||
buttons.forEach(x => x.setAttribute('disabled', 'disabled'));
|
||||
|
|
|
|||
|
|
@ -2,8 +2,27 @@
|
|||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="container content">
|
||||
<h1 class="title">{{ object.name }}</h1>
|
||||
{% if object.acquisitions %}
|
||||
<div class="fixed-grid has-6-cols">
|
||||
<div class="grid">
|
||||
{% for acquisition in object.acquisitions %}
|
||||
<a class="cell" href="/scan-acquisition/{{ acquisition.id }}">
|
||||
<div class="has-text-centered p-3" style="border-radius: 15px; border-width: 1px; border-color: {% if acquisition.validated %}green{% else %}red{% endif %}; border-style: solid;">
|
||||
<div>
|
||||
<img src="/data/objects/{{ object.id }}/{{ acquisition.id }}/{{ leds[0] }}.jpg">
|
||||
</div>
|
||||
<p>
|
||||
{{ acquisition.get_pretty_date() }}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if calibration.state == CalibrationState.IsValidated %}
|
||||
<a href="/scan/{{ object.id }}" class="button is-link">Faire un scan</a>
|
||||
{% else %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">Faire une acquisition</h1>
|
||||
<div class="mb-2">
|
||||
<p>Placez l'objet devant le scanner puis appuyez sur le bouton pour lancer l'acquisition.</p>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<button {% if acquisition and acquisition.validated %}disabled{% endif %} id="scan-button" class="button is-link">Lancer l'acquisition</button>
|
||||
</div>
|
||||
</div>
|
||||
<article id="error-container" class="message is-danger" style="display: none;">
|
||||
<div id="error-content" class="message-body">
|
||||
</div>
|
||||
</article>
|
||||
<div class="fixed-grid has-8-cols">
|
||||
<div id="grid" class="grid">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<progress id="progress-bar" class="progress is-link" style="display: none;" value="0" max="1000"></progress>
|
||||
<div id="calibrate" {% if not acquisition %}style="display: none;"{% endif %}>
|
||||
<p>Si les données acquises conviennent, appuyez sur le bouton ci-dessous pour valider l'acquisition.</p>
|
||||
{% if acquisition %}
|
||||
{% if acquisition.validated %}
|
||||
<button disabled id="calibrate-button" class="button is-link">Cette acquisition a déjà été validée</button>
|
||||
{% else %}
|
||||
<a href="/validate-acquisition/{{ acquisition.id }}" id="calibrate-button" class="button is-link">Valider l'acquisition</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a id="calibrate-button" class="button is-link">Valider l'acquisition</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
||||
{% block extrajs %}
|
||||
<script>
|
||||
let acquisitionId = {% if acquisition %}{{ acquisition.id }}{% else %}null{% endif %};
|
||||
let scanIndex = 1;
|
||||
let progress = 0;
|
||||
let scanButton = document.getElementById('scan-button');
|
||||
let progressBar = document.getElementById('progress-bar');
|
||||
let grid = document.getElementById('grid');
|
||||
let buttons = [scanButton];
|
||||
let errorContainer = document.getElementById('error-container');
|
||||
let errorContent = document.getElementById('error-content');
|
||||
let calibrateDiv = document.getElementById('calibrate');
|
||||
let calibrateButton = document.getElementById('calibrate-button');
|
||||
|
||||
// If we already have calibration images, we show them right now
|
||||
if (acquisitionId !== null) {
|
||||
let cell, img;
|
||||
{% for led in leds %}
|
||||
cell = document.createElement('div');
|
||||
cell.classList.add('cell');
|
||||
img = document.createElement('img');
|
||||
img.classList.add('is-loading');
|
||||
img.src = '/data/objects/{{ object.id }}/' + acquisitionId + '/{{ led }}.jpg?v=0';
|
||||
cell.appendChild(img);
|
||||
grid.appendChild(cell);
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
scanButton.addEventListener('click', async () => {
|
||||
scanIndex++;
|
||||
progress = 0;
|
||||
progressBar.value = 0;
|
||||
buttons.forEach(x => x.setAttribute('disabled', 'disabled'));
|
||||
scanButton.classList.add('is-loading');
|
||||
progressBar.style.display = "block";
|
||||
grid.innerHTML = '';
|
||||
|
||||
let response;
|
||||
if (acquisitionId === null) {
|
||||
response = await fetch('/api/scan-for-object/{{ object.id }}');
|
||||
} else {
|
||||
response = await fetch('/api/scan-for-acquisition/' + acquisitionId);
|
||||
}
|
||||
|
||||
let reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
||||
|
||||
while (true) {
|
||||
let { value, done } = await reader.read();
|
||||
|
||||
if (done) {
|
||||
buttons.forEach(x => x.removeAttribute('disabled'));
|
||||
scanButton.classList.remove('is-loading');
|
||||
calibrateDiv.style.display = "block";
|
||||
break;
|
||||
}
|
||||
|
||||
if (acquisitionId === null) {
|
||||
acquisitionId = parseInt(value, 10);
|
||||
calibrateButton.setAttribute('href', '/validate-acquisition/' + acquisitionId);
|
||||
continue;
|
||||
}
|
||||
|
||||
let [ uuid, ratio ] = value.trim().split(',');
|
||||
progress = Math.ceil(1000 * parseFloat(ratio));
|
||||
let cell = document.createElement('div');
|
||||
cell.classList.add('cell');
|
||||
let img = document.createElement('img');
|
||||
img.classList.add('is-loading');
|
||||
img.src = '/data/objects/{{ object.id }}/' + acquisitionId + '/' + uuid + '.jpg?v=' + scanIndex;
|
||||
cell.appendChild(img);
|
||||
grid.appendChild(cell);
|
||||
}
|
||||
});
|
||||
|
||||
function refreshProgressBar() {
|
||||
if (progress !== progressBar.value) {
|
||||
progressBar.value = Math.min(progressBar.value + 5, progress);
|
||||
}
|
||||
requestAnimationFrame(refreshProgressBar);
|
||||
}
|
||||
|
||||
refreshProgressBar();
|
||||
</script>
|
||||
{% endblock extrajs %}
|
||||
Loading…
Reference in New Issue