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
|
from os.path import join
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import uuid
|
import uuid
|
||||||
|
from typing import Optional
|
||||||
from . import db, config, scanner, calibration
|
from . import db, config, scanner, calibration
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
@ -33,7 +34,7 @@ def get_calibration(conn: sqlite3.Connection) -> db.Calibration:
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_stage_and_region():
|
def inject_stage_and_region():
|
||||||
conn = db.get()
|
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
|
@app.before_request
|
||||||
|
|
@ -63,15 +64,33 @@ def create_object():
|
||||||
@app.route('/object/<id>')
|
@app.route('/object/<id>')
|
||||||
def object(id: int):
|
def object(id: int):
|
||||||
conn = db.get()
|
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)
|
return render_template('object.html', object=object)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/scan/<id>')
|
@app.route('/scan/<id>')
|
||||||
def scan(id: int):
|
def scan(id: int):
|
||||||
conn = db.get()
|
conn = db.get()
|
||||||
|
calibration_id = session.get('calibration_id', None)
|
||||||
object = db.Object.get_from_id(id, conn)
|
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/")
|
@app.route("/calibrate/")
|
||||||
|
|
@ -85,7 +104,7 @@ def calibrate():
|
||||||
calibration = db.Calibration.get_from_id(session['calibration_id'], conn)
|
calibration = db.Calibration.get_from_id(session['calibration_id'], conn)
|
||||||
|
|
||||||
if calibration.state in [db.CalibrationState.Empty, db.CalibrationState.HasData]:
|
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:
|
else:
|
||||||
return render_template('calibration.html', calibration=calibration)
|
return render_template('calibration.html', calibration=calibration)
|
||||||
|
|
||||||
|
|
@ -146,6 +165,71 @@ def scan_calibration():
|
||||||
return app.response_class(generate(), mimetype='text/plain')
|
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')
|
@app.route('/api/use-last-calibration')
|
||||||
def use_last_calibration():
|
def use_last_calibration():
|
||||||
conn = db.get()
|
conn = db.get()
|
||||||
|
|
|
||||||
77
db.py
77
db.py
|
|
@ -128,18 +128,20 @@ Calibration.Dummy = Calibration(-1, CalibrationState.Empty, None)
|
||||||
class Acquisition:
|
class Acquisition:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def select_args() -> str:
|
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):
|
def __init__(self, acquisition_id: int, calibration_id: int, object_id: int, date: int, validated: int):
|
||||||
self.id = object_id
|
self.id = acquisition_id
|
||||||
self.calibration_id = calibration_id
|
self.calibration_id = calibration_id
|
||||||
self.object_id = object_id
|
self.object_id = object_id
|
||||||
|
self.date = datetime.fromtimestamp(date)
|
||||||
|
self.validated = validated != 0
|
||||||
|
|
||||||
def save(self, db: sqlite3.Connection):
|
def save(self, db: sqlite3.Connection):
|
||||||
cur = db.cursor()
|
cur = db.cursor()
|
||||||
cur.execute(
|
cur.execute(
|
||||||
'UPDATE acquisition SET calibration_id = ?, object_id = ? WHERE id = ?',
|
'UPDATE acquisition SET calibration_id = ?, object_id = ?, date = ?, validated = ? WHERE id = ?',
|
||||||
[self.calibration_id, self.object_id, self.id]
|
[self.calibration_id, self.object_id, self.date.timestamp(), 1 if self.validated else 0, self.id]
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -163,6 +165,19 @@ class Acquisition:
|
||||||
|
|
||||||
return Calibration.get_from_id(self.calibration_id, db)
|
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:
|
class Object:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -218,11 +233,21 @@ class Object:
|
||||||
def add_acquisition(self, calibration_id: int, db: sqlite3.Connection) -> Acquisition:
|
def add_acquisition(self, calibration_id: int, db: sqlite3.Connection) -> Acquisition:
|
||||||
cur = db.cursor()
|
cur = db.cursor()
|
||||||
response = cur.execute(
|
response = cur.execute(
|
||||||
'INSERT INTO acquisition(calibration_id, object_id) VALUES (?, ?) RETURNING ' + Acquisition.select_args() + ';',
|
'INSERT INTO acquisition(calibration_id, object_id, date, validated) VALUES (?, ?, ?, ?) RETURNING ' + Acquisition.select_args() + ';',
|
||||||
[calibration_id, self.id]
|
[calibration_id, self.id, datetime.now().timestamp(), 0]
|
||||||
)
|
)
|
||||||
return Acquisition.from_row(response.fetchone())
|
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():
|
def main():
|
||||||
# Move current data to backup dir
|
# Move current data to backup dir
|
||||||
|
|
@ -245,26 +270,26 @@ def main():
|
||||||
init(db)
|
init(db)
|
||||||
|
|
||||||
# Create a new object
|
# Create a new object
|
||||||
with db:
|
# with db:
|
||||||
Object.create('Mon premier objet', db)
|
# Object.create('Mon premier objet', db)
|
||||||
# calibration = Calibration.create(db)
|
# # calibration = Calibration.create(db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# calibration = Calibration.create(db)
|
# # calibration = Calibration.create(db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, 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)
|
# Object.create('Mon deuxième objet', db)
|
||||||
# calibration = Calibration.create(db)
|
# # calibration = Calibration.create(db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# calibration = Calibration.create(db)
|
# # calibration = Calibration.create(db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
# object.add_acquisition(calibration.id, db)
|
# # object.add_acquisition(calibration.id, db)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ CREATE TABLE acquisition (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
calibration_id INTEGER NOT NULL,
|
calibration_id INTEGER NOT NULL,
|
||||||
object_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_calibration FOREIGN KEY(calibration_id) REFERENCES calibration(id),
|
||||||
CONSTRAINT fk_object FOREIGN KEY(object_id) REFERENCES object(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 %}>
|
<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>
|
<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>
|
<button id="calibrate-button" class="button is-link">Étalonner le scanner</button>
|
||||||
<p id="calibration-info"></p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -53,7 +52,6 @@
|
||||||
let errorContent = document.getElementById('error-content');
|
let errorContent = document.getElementById('error-content');
|
||||||
let calibrateDiv = document.getElementById('calibrate');
|
let calibrateDiv = document.getElementById('calibrate');
|
||||||
let calibrateButton = document.getElementById('calibrate-button');
|
let calibrateButton = document.getElementById('calibrate-button');
|
||||||
let calibrationInfo = document.getElementById('calibration-info');
|
|
||||||
|
|
||||||
previewButton.addEventListener('click', async () => {
|
previewButton.addEventListener('click', async () => {
|
||||||
buttons.forEach(x => x.setAttribute('disabled', 'disabled'));
|
buttons.forEach(x => x.setAttribute('disabled', 'disabled'));
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,27 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container content">
|
||||||
<h1 class="title">{{ object.name }}</h1>
|
<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 %}
|
{% if calibration.state == CalibrationState.IsValidated %}
|
||||||
<a href="/scan/{{ object.id }}" class="button is-link">Faire un scan</a>
|
<a href="/scan/{{ object.id }}" class="button is-link">Faire un scan</a>
|
||||||
{% else %}
|
{% 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