scanning
This commit is contained in:
@@ -26,10 +26,10 @@ class ConfigurationAdmin(admin.ModelAdmin):
|
|||||||
"classes": ("collapse",),
|
"classes": ("collapse",),
|
||||||
}),
|
}),
|
||||||
(_("Camera"), {
|
(_("Camera"), {
|
||||||
"fields": ("capture_type", "webcam_device_index", "image_quality", "video_jpeg_quality", "video_frame_rate", "video_width_capture", "video_height_capture"),
|
"fields": ("scan_simulation", "capture_type", "webcam_device_index", "image_quality", "video_jpeg_quality", "video_frame_rate", "video_width_capture", "video_height_capture"),
|
||||||
"classes": ("collapse",),
|
"classes": ("collapse",),
|
||||||
}),
|
}),
|
||||||
(_("Calibration"), {
|
(_("Calibration / Balayage"), {
|
||||||
"fields": ("calibration_crop_radius", "calibration_default_multiwell", "calibration_default_feed", "calibration_default_step", "calibration_default_duration"),
|
"fields": ("calibration_crop_radius", "calibration_default_multiwell", "calibration_default_feed", "calibration_default_step", "calibration_default_duration"),
|
||||||
"classes": ("collapse",),
|
"classes": ("collapse",),
|
||||||
}),
|
}),
|
||||||
@@ -69,6 +69,7 @@ class WellPositionAdmin(admin.ModelAdmin):
|
|||||||
class ExperimentWellInline(admin.TabularInline):
|
class ExperimentWellInline(admin.TabularInline):
|
||||||
model = models.ExperimentWell
|
model = models.ExperimentWell
|
||||||
extra = 0
|
extra = 0
|
||||||
|
#ordering = ('experiment__multiwell__wellposition__order',)
|
||||||
|
|
||||||
|
|
||||||
class ExperimentAdmin(admin.ModelAdmin):
|
class ExperimentAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class DefaultConfig:
|
|||||||
video_frame_rate: int = 5.0
|
video_frame_rate: int = 5.0
|
||||||
video_width_capture: int = 2028
|
video_width_capture: int = 2028
|
||||||
video_height_capture: int = 1520
|
video_height_capture: int = 1520
|
||||||
|
scan_simulation: bool = False
|
||||||
calibration_crop_radius: int = 500
|
calibration_crop_radius: int = 500
|
||||||
calibration_default_multiwell: str = 'HD'
|
calibration_default_multiwell: str = 'HD'
|
||||||
calibration_default_feed: int = 1000
|
calibration_default_feed: int = 1000
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class Configuration(models.Model):
|
|||||||
video_width_capture = models.PositiveSmallIntegerField(_("Largeur de capture vidéo"), help_text=_("Largeur de capture vidéo en pixels"), default=1280)
|
video_width_capture = models.PositiveSmallIntegerField(_("Largeur de capture vidéo"), help_text=_("Largeur de capture vidéo en pixels"), default=1280)
|
||||||
video_height_capture = models.PositiveSmallIntegerField(_("Hauteur de capture vidéo"), help_text=_("Hauteur de capture vidéo en pixels"), default=720)
|
video_height_capture = models.PositiveSmallIntegerField(_("Hauteur de capture vidéo"), help_text=_("Hauteur de capture vidéo en pixels"), default=720)
|
||||||
# Calibration
|
# Calibration
|
||||||
|
scan_simulation = models.BooleanField(_("Simuler balayage"), help_text=_("Autorise la simulation du balayage"), default=False)
|
||||||
calibration_crop_radius = models.PositiveSmallIntegerField(_("Rayon de découpe pour la calibration"), help_text=_("Rayon en pixels pour découper les images de calibration en px"), default=150)
|
calibration_crop_radius = models.PositiveSmallIntegerField(_("Rayon de découpe pour la calibration"), help_text=_("Rayon en pixels pour découper les images de calibration en px"), default=150)
|
||||||
calibration_default_multiwell = models.CharField(_("Multi-puits de calibration par défaut"), help_text=_("Position du multi-puits de calibration par défaut"), max_length=8, choices=MULTIWELL_POSITION, default='HG')
|
calibration_default_multiwell = models.CharField(_("Multi-puits de calibration par défaut"), help_text=_("Position du multi-puits de calibration par défaut"), max_length=8, choices=MULTIWELL_POSITION, default='HG')
|
||||||
calibration_default_feed = models.PositiveIntegerField(_("Vitesse de calibration"), help_text=_("Vitesse de déplacement pour la calibration en mm/mn"), default=1000)
|
calibration_default_feed = models.PositiveIntegerField(_("Vitesse de calibration"), help_text=_("Vitesse de déplacement pour la calibration en mm/mn"), default=1000)
|
||||||
@@ -308,7 +309,7 @@ class Session(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
state = _("Terminée") if not self.active else _("Active")
|
state = _("Terminée") if not self.active else _("Active")
|
||||||
return f'{self.name}: {state}'
|
return f'{self.id}: {self.name} {state}'
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Session)
|
@receiver(post_save, sender=Session)
|
||||||
@@ -409,10 +410,10 @@ class ExperimentWell(models.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def wellname_by_experiment(cls, experiment_id):
|
def wellname_by_experiment(cls, experiment_id):
|
||||||
return [ ew.well.name for ew in ExperimentWell.objects.filter(experiment__id=experiment_id, active=True).order_by('well__order') ]
|
return [ ew.well.name for ew in ExperimentWell.objects.filter(experiment__id=experiment_id, active=True).order_by('well__name') ]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['experiment',]
|
ordering = ['experiment', 'well']
|
||||||
unique_together = ["experiment", "well", ]
|
unique_together = ["experiment", "well", ]
|
||||||
verbose_name = _("Expérience puit")
|
verbose_name = _("Expérience puit")
|
||||||
verbose_name_plural = _("Expériences puitd")
|
verbose_name_plural = _("Expériences puitd")
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ class MultiWellManager:
|
|||||||
|
|
||||||
self.process = process
|
self.process = process
|
||||||
self.cnc_controller = process.grbl
|
self.cnc_controller = process.grbl
|
||||||
|
|
||||||
logger.info(f"MultiWellManager initialized with CNC controller: {self.cnc_controller}")
|
logger.info(f"MultiWellManager initialized with CNC controller: {self.cnc_controller}")
|
||||||
|
|
||||||
self.stop_playing = Event()
|
self.stop_playing = Event()
|
||||||
@@ -99,8 +98,6 @@ class MultiWellManager:
|
|||||||
min_contour_dist_px = self.process.conf.min_contour_dist_px,
|
min_contour_dist_px = self.process.conf.min_contour_dist_px,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_tracker_config(self):
|
def set_tracker_config(self):
|
||||||
self.tracker_config = dict(
|
self.tracker_config = dict(
|
||||||
tube_axis = settings.TRACKER_TUBE_AXIS,
|
tube_axis = settings.TRACKER_TUBE_AXIS,
|
||||||
@@ -169,6 +166,8 @@ class MultiWellManager:
|
|||||||
self.process.data.record = True
|
self.process.data.record = True
|
||||||
self.process._send(current=well_position.order)
|
self.process._send(current=well_position.order)
|
||||||
|
|
||||||
|
logger.info(f"Starting capture for {uuid} ordre: {well_position.order}")
|
||||||
|
|
||||||
start = time.monotonic()
|
start = time.monotonic()
|
||||||
while not self.stop_playing.is_set():
|
while not self.stop_playing.is_set():
|
||||||
## stop after duration in experiemnt now
|
## stop after duration in experiemnt now
|
||||||
@@ -196,8 +195,8 @@ class MultiWellManager:
|
|||||||
logger.info(f"Simulating capture with file {vf}")
|
logger.info(f"Simulating capture with file {vf}")
|
||||||
|
|
||||||
|
|
||||||
def _is_well_valid(self, welposition):
|
def _is_well_valid(self, welposition, experiment):
|
||||||
names = models.ExperimentWell.wellname_by_experiment(welposition.experiment.id)
|
names = models.ExperimentWell.wellname_by_experiment(experiment.id)
|
||||||
if welposition.well.name not in names:
|
if welposition.well.name not in names:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -206,13 +205,14 @@ class MultiWellManager:
|
|||||||
try:
|
try:
|
||||||
multiwell = experiment.multiwell
|
multiwell = experiment.multiwell
|
||||||
wellpositions = models.WellPosition.objects.filter(multiwell_id=multiwell.id).order_by('order').all()
|
wellpositions = models.WellPosition.objects.filter(multiwell_id=multiwell.id).order_by('order').all()
|
||||||
|
|
||||||
cam = self.process.cam
|
cam = self.process.cam
|
||||||
cam._aligner.set_tube_diameter(multiwell.diameter)
|
cam._aligner.set_tube_diameter(multiwell.diameter)
|
||||||
|
|
||||||
for wl in wellpositions:
|
for wl in wellpositions:
|
||||||
if self.stop_playing.is_set():
|
if self.stop_playing.is_set():
|
||||||
break
|
break
|
||||||
if not self._is_well_valid(wl):
|
if not self._is_well_valid(wl, experiment):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.cnc_controller.move_to(wl.x, wl.y, feed=wl.multiwell.feed)
|
self.cnc_controller.move_to(wl.x, wl.y, feed=wl.multiwell.feed)
|
||||||
@@ -223,17 +223,18 @@ class MultiWellManager:
|
|||||||
msg =f"Scan terminé — retour à l'origine (X={xnext:.1f} Y={ynext:.1f})"
|
msg =f"Scan terminé — retour à l'origine (X={xnext:.1f} Y={ynext:.1f})"
|
||||||
logger.info(msg)
|
logger.info(msg)
|
||||||
self.process._send(state='scan_finished', msg=msg)
|
self.process._send(state='scan_finished', msg=msg)
|
||||||
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"Error during grid scanning - {e}"
|
msg = f"Error during grid scanning - {e}"
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
self.process._send(state='error', msg=msg)
|
self.process._send(state='error', msg=msg)
|
||||||
|
return False
|
||||||
finally:
|
finally:
|
||||||
self.cnc_controller.move_to(xnext, ynext, feed=multiwell.feed*2)
|
self.cnc_controller.move_to(xnext, ynext, feed=multiwell.feed*2)
|
||||||
|
|
||||||
|
|
||||||
def _start_scanning(self, session, experiments, simulate=False):
|
def _start_scanning(self, session, experiments, simulate=False):
|
||||||
|
result = False
|
||||||
try:
|
try:
|
||||||
self.process.cam._aligner.debug = False
|
self.process.cam._aligner.debug = False
|
||||||
self.stop_playing.clear()
|
self.stop_playing.clear()
|
||||||
@@ -247,18 +248,21 @@ class MultiWellManager:
|
|||||||
self.process.data.session = session.id
|
self.process.data.session = session.id
|
||||||
started = timezone.now()
|
started = timezone.now()
|
||||||
for obs in experiments:
|
for obs in experiments:
|
||||||
|
|
||||||
|
logger.warning(f"Starting scan for {obs} (well {pos}/{len(experiments)})")
|
||||||
|
|
||||||
if self.stop_playing.is_set():
|
if self.stop_playing.is_set():
|
||||||
break
|
break
|
||||||
obs.started = timezone.now()
|
obs.started = timezone.now()
|
||||||
obs.save()
|
obs.save()
|
||||||
xnext, ynext = xynext[pos]
|
xnext, ynext = xynext[pos]
|
||||||
pos +=1
|
pos +=1
|
||||||
self._grid_scanning(obs, xnext=xnext, ynext=ynext, simulate=simulate)
|
result = self._grid_scanning(obs, xnext=xnext, ynext=ynext, simulate=simulate)
|
||||||
obs.finished = timezone.now()
|
obs.finished = timezone.now()
|
||||||
obs.save()
|
obs.save()
|
||||||
|
|
||||||
session.finished = timezone.now()
|
session.finished = timezone.now()
|
||||||
if self.stop_playing.is_set():
|
if self.stop_playing.is_set() or not result:
|
||||||
msg = f"Session {session.name} abandonnée à {session.finished} après {session.finished - started} secondes."
|
msg = f"Session {session.name} abandonnée à {session.finished} après {session.finished - started} secondes."
|
||||||
else:
|
else:
|
||||||
if not simulate:
|
if not simulate:
|
||||||
@@ -273,6 +277,8 @@ class MultiWellManager:
|
|||||||
logger.error("Error during scanning process", e)
|
logger.error("Error during scanning process", e)
|
||||||
finally:
|
finally:
|
||||||
self.scan_thread = None
|
self.scan_thread = None
|
||||||
|
self.goto_0()
|
||||||
|
self.process._send(current=self.get_well_order())
|
||||||
|
|
||||||
|
|
||||||
def halt_scanning(self):
|
def halt_scanning(self):
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ class ScannerProcess(Task):
|
|||||||
self.cam._active_median = False
|
self.cam._active_median = False
|
||||||
self.grbl.go_origin(feed=self.manager.feed)
|
self.grbl.go_origin(feed=self.manager.feed)
|
||||||
self.cam.set_draw_contours(False)
|
self.cam.set_draw_contours(False)
|
||||||
buttons = self.manager.multiwell_buttons(btn_class="w3-btn", onclick="")
|
buttons = self.manager.multiwell_buttons(btn_class="w3-btn well", onclick="")
|
||||||
self._send(buttons=buttons)
|
self._send(buttons=buttons)
|
||||||
|
|
||||||
elif topic == 'scan' or topic == 'simulate':
|
elif topic == 'scan' or topic == 'simulate':
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ScannerManager {
|
|||||||
this.area_px = options.area_px;
|
this.area_px = options.area_px;
|
||||||
this.frame_count = options.frame_count;
|
this.frame_count = options.frame_count;
|
||||||
this.scan_state = options.scan_state;
|
this.scan_state = options.scan_state;
|
||||||
this.sim_bt = options.simulate;
|
this.sim_bt = options.simulate || null;
|
||||||
this.well_btn = options.well_btn;
|
this.well_btn = options.well_btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,9 @@ class ScannerManager {
|
|||||||
this.crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop" }); });
|
this.crop.addEventListener('click', (e) => { this._send({ type: 'calibrate', topic: "crop" }); });
|
||||||
this.scan_bt.addEventListener('click', (e) => { this.scan(); });
|
this.scan_bt.addEventListener('click', (e) => { this.scan(); });
|
||||||
this.halt_bt.addEventListener('click', (e) => { this.halt(); });
|
this.halt_bt.addEventListener('click', (e) => { this.halt(); });
|
||||||
this.sim_bt.addEventListener('click', (e) => { this.simulate(); });
|
|
||||||
|
if (this.sim_bt)
|
||||||
|
this.sim_bt.addEventListener('click', (e) => { this.simulate(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSocket(socket) {
|
registerSocket(socket) {
|
||||||
@@ -44,18 +46,29 @@ class ScannerManager {
|
|||||||
try {
|
try {
|
||||||
if (payload.jpeg) { this.container.src = `data:image/jpeg;base64,${payload.jpeg}`; }
|
if (payload.jpeg) { this.container.src = `data:image/jpeg;base64,${payload.jpeg}`; }
|
||||||
if (payload.xy) { this.x.textContent=payload.x.toFixed(2); this.y.textContent=payload.y.toFixed(2); }
|
if (payload.xy) { this.x.textContent=payload.x.toFixed(2); this.y.textContent=payload.y.toFixed(2); }
|
||||||
if (payload.state) { this.debug.insertAdjacentHTML('afterbegin', `<li>[ ${++this.debug_count} - ${payload.state} ]: ${payload.msg}</li>`); }
|
if (payload.state) {
|
||||||
|
if (payload.state == 'median') {
|
||||||
|
const span = this.median.querySelector("span.median"); span.style.color = payload.value ? '#f0f' : '#fff';
|
||||||
|
} else if (payload.state == 'crop') {
|
||||||
|
const span = this.crop.querySelector("span.crop"); span.style.color = payload.value ? '#f0f' : '#fff';
|
||||||
|
}
|
||||||
|
this.debug.insertAdjacentHTML('afterbegin', `<li>[ ${++this.debug_count} - ${payload.state} ]: ${payload.msg}</li>`);
|
||||||
|
}
|
||||||
if (payload.ts) { this.ts.textContent = timestampToLocalISOString(payload.ts); }
|
if (payload.ts) { this.ts.textContent = timestampToLocalISOString(payload.ts); }
|
||||||
if (payload.scan_state) { this.scan_state.textContent=payload.scan_state;}
|
if (payload.scan_state) { this.scan_state.textContent=payload.scan_state;}
|
||||||
|
|
||||||
if (payload.buttons) { this.well_btn.innerHTML = payload.buttons; }
|
if (payload.buttons) {
|
||||||
|
this.well_btn.innerHTML = payload.buttons;
|
||||||
|
const span = this.crop.querySelector("span.crop"); span.style.color = '#f0f';
|
||||||
|
}
|
||||||
if (payload.current >= 0) {
|
if (payload.current >= 0) {
|
||||||
document.querySelectorAll('button.w3-button.well').forEach(btn => {
|
document.querySelectorAll('button.w3-btn.well').forEach(btn => {
|
||||||
if (btn.value==payload.current) { btn.classList.add('w3-green'); return; }
|
if (btn.value==payload.current) { btn.classList.add('w3-green'); return; }
|
||||||
btn.classList.remove('w3-green');
|
btn.classList.remove('w3-green');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch(e) { console.log(e); }
|
} catch(e) { console.log(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()" title="{% trans "Ensemble d'expériences" %}">
|
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()" title="{% trans "Ensemble d'expériences" %}">
|
||||||
<option value="0">---- {% trans "Session" %}</option>
|
<option value="0">---- {% trans "Session" %}</option>
|
||||||
{% for s in sessions %}
|
{% for s in sessions %}
|
||||||
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s.name }}</option>
|
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div class="w3-margin-left w3-margin-bottom">
|
<div class="w3-margin-left w3-margin-bottom">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()">
|
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()">
|
||||||
<option value="0">---- {% trans "Session" %}</option>
|
<option value="0">---- {% trans "Session" %}</option>
|
||||||
{% for s in sessions %}
|
{% for s in sessions %}
|
||||||
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s.name }}</option>
|
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div class="w3-margin-left w3-margin-bottom">
|
<div class="w3-margin-left w3-margin-bottom">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div>{% trans "Choix de la session" %}</div>
|
<div>{% trans "Choix de la session" %}</div>
|
||||||
<select id="_session" class="w3-select">
|
<select id="_session" class="w3-select">
|
||||||
{% for s in sessions %}
|
{% for s in sessions %}
|
||||||
<option value="{{ s.id }}">{{ s.name }}</option>
|
<option value="{{ s.id }}">{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,26 +28,28 @@
|
|||||||
<div class="w3-row w3-row-padding">
|
<div class="w3-row w3-row-padding">
|
||||||
<div class="w3-half">
|
<div class="w3-half">
|
||||||
<button id="_median" class="w3-button w3-dark-xlight w3-round-large w3-block">
|
<button id="_median" class="w3-button w3-dark-xlight w3-round-large w3-block">
|
||||||
{% trans 'Axes' %} <br><i class="fa-solid fa-crosshairs w3-text-amber w3-xlarge"></i>
|
<span class="median">{% trans 'Axes' %}</span><br><i class="fa-solid fa-crosshairs w3-text-amber w3-xlarge"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-half">
|
<div class="w3-half">
|
||||||
<button id="_crop" class="w3-button w3-dark-xlight w3-round-large w3-block">
|
<button id="_crop" class="w3-button w3-dark-xlight w3-round-large w3-block">
|
||||||
{% trans 'Recadrer' %}<br><i class="fa-solid fa-crop w3-text-amber w3-xlarge"></i>
|
<span class="crop" style="color: red">{% trans 'Recadrer' %}</span><br><i class="fa-solid fa-crop w3-text-amber w3-xlarge"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-row w3-row-padding">
|
<div class="w3-row w3-row-padding">
|
||||||
<div class="w3-twothird w3-center">
|
<div class="{% if conf.scan_simulation %}w3-twothird{% else %}w3-col{% endif %} w3-center">
|
||||||
<button id="_scan" class="w3-button w3-light-blue w3-round-large w3-margin-top w3-block">
|
<button id="_scan" class="w3-button w3-light-blue w3-round-large w3-margin-top w3-block">
|
||||||
{% trans 'Lancer' %}<br>{% trans 'le balayage' %}<br><i class="fa-solid fa-circle-check w3-text-blue w3-xlarge"></i>
|
<span class="w3-bold">{% trans 'Lancer' %}<br>{% trans 'le balayage' %}</span><br><i class="fa-solid fa-circle-check w3-text-blue w3-xlarge"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% if conf.scan_simulation %}
|
||||||
<div class="w3-third w3-center">
|
<div class="w3-third w3-center">
|
||||||
<button id="_simulate" class="w3-button w3-dark-xlight w3-round-large w3-margin-top w3-block">
|
<button id="_simulate" class="w3-button w3-dark-xlight w3-round-large w3-margin-top w3-block">
|
||||||
{% trans 'Simuler' %}<br>{% trans 'le balayage' %}<br><i class="fa-solid fa-triangle-exclamation w3-text-orange w3-xlarge"></i>
|
<span class="w3-bold">{% trans 'Simuler' %}<br>{% trans 'le balayage' %}</span><br><i class="fa-solid fa-triangle-exclamation w3-text-orange w3-xlarge"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="w3-col">
|
<div class="w3-col">
|
||||||
<div id="_well_btn" class="w3-margin-top"></div>
|
<div id="_well_btn" class="w3-margin-top"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,10 +60,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-col">
|
<div class="w3-col">
|
||||||
<div class="w3-margin-top w3-padding w3-small">
|
<div class="w3-margin-top w3-padding-small w3-small">
|
||||||
<span id="_ts"></span>
|
<span id="_ts"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-margin-top w3-small">
|
<div class="w3-margin-top w3-small w3-center w3-justify">
|
||||||
<span id="_scan_state" class="w3-text-amber w3-padding"></span>
|
<span id="_scan_state" class="w3-text-amber w3-padding"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.http import JsonResponse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.decorators.http import require_GET, require_POST
|
from django.views.decorators.http import require_GET, require_POST
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from reduct.time import unix_timestamp_to_iso
|
from reduct.time import unix_timestamp_to_iso
|
||||||
from modules.system_stats import get_cached_stats, start_background_updater
|
from modules.system_stats import get_cached_stats, start_background_updater
|
||||||
@@ -21,6 +21,10 @@ record_manager = CameraRecordManager(cameraDB)
|
|||||||
start_background_updater()
|
start_background_updater()
|
||||||
|
|
||||||
|
|
||||||
|
def is_staff_or_admin(user):
|
||||||
|
return user.is_staff or user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
def stats_view(request):
|
def stats_view(request):
|
||||||
"""
|
"""
|
||||||
@@ -48,10 +52,12 @@ def global_context(request, **ctx):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def admin_view(request):
|
def admin_view(request):
|
||||||
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/'))
|
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
||||||
def admin_session_view(request):
|
def admin_session_view(request):
|
||||||
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/scanner/session/'))
|
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/scanner/session/'))
|
||||||
|
|
||||||
@@ -68,26 +74,33 @@ def admin_periodictask_view(request):
|
|||||||
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/django_celery_beat/periodictask/'))
|
return render(request, "scanner/iframe.html", context=global_context(request, link='/admin/django_celery_beat/periodictask/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def reductstore_view(request):
|
def reductstore_view(request):
|
||||||
|
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:8383/'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:8383/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def adminer_view(request):
|
def adminer_view(request):
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}/adminer/'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}/adminer/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def portainer_view(request):
|
def portainer_view(request):
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9000/'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9000/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def supervisor_view(request):
|
def supervisor_view(request):
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def supervisor_worker(request):
|
def supervisor_worker(request):
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:services'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:services'))
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@user_passes_test(is_staff_or_admin)
|
||||||
def supervisor_scheduler(request):
|
def supervisor_scheduler(request):
|
||||||
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:planification'))
|
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:planification'))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user