feat(snapshots): reconnait les formats manuels SANEF + toggle UI 'Tous formats'
- backend: nouveau regex SNAP_MANUAL_RE qui capture le premier token avant espace/underscore comme auteur. Permet de classer les snaps style 'kmoad-ext avant maj' ou 'kmoad-ext s1' en origin='manual' avec auteur extrait, au lieu de origin=None. - frontend: checkbox 'Tous formats' (cochée par défaut) qui inclut les snaps manual/slpm/patchcenter. Decoche pour PatchCenter only (ancien comportement). - frontend: filtre intervenant elargi - match aussi sur nom du snap (contains) en plus de l'auteur extrait, pour couvrir les snaps dont l'auteur est concatene avec d'autres mots. Resout le cas ou un utilisateur ne voyait qu'1 seul snap PatchCenter alors qu'il avait des dizaines de snaps crees manuellement (format '<user> avant maj').
This commit is contained in:
parent
69ea7aa09a
commit
720b0789e6
@ -29,6 +29,7 @@ except ImportError:
|
||||
# PatchCenter v2 : `<user>_YYYY-MM-DD_HH-MM_avant_patch` (user = login JWT, depuis 2026-05-07)
|
||||
# PatchCenter v1 : `<auteur>_YYYY-MM-DD_avant_patch` (legacy, basé sur intervenant)
|
||||
# .exe SLPM : `SLPM_<auteur>_YYYYMMDD_HHMM`
|
||||
# manuel SANEF : `<auteur>[ _]...` — fallback, premier token = auteur
|
||||
SNAP_PATCHCENTER_V2_RE = re.compile(
|
||||
r"^(?P<author>[A-Za-z0-9_\-\.]+)_(?P<date>\d{4}-\d{2}-\d{2})_\d{2}-\d{2}_avant_patch$"
|
||||
)
|
||||
@ -38,12 +39,16 @@ SNAP_PATCHCENTER_V1_RE = re.compile(
|
||||
SNAP_SLPM_RE = re.compile(
|
||||
r"^SLPM_(?P<author>[A-Za-z0-9_\-\.]+)_\d{8}_\d{4}$"
|
||||
)
|
||||
SNAP_MANUAL_RE = re.compile(
|
||||
r"^(?P<author>[A-Za-z0-9][A-Za-z0-9_\-\.]{1,})[\s_]"
|
||||
)
|
||||
|
||||
|
||||
def _detect_snap_origin(name: str):
|
||||
"""Renvoie (origin, author) ou (None, None) si format inconnu.
|
||||
origin in {'patchcenter', 'slpm'} ; author = préfixe utilisateur.
|
||||
PatchCenter v2 (avec heure) testé en premier pour ne pas matcher v1."""
|
||||
origin in {'patchcenter', 'slpm', 'manual'} ; author = préfixe utilisateur.
|
||||
PatchCenter v2 (avec heure) testé en premier pour ne pas matcher v1.
|
||||
'manual' = fallback : premier token (avant espace ou underscore) = auteur."""
|
||||
n = name or ""
|
||||
m = SNAP_PATCHCENTER_V2_RE.match(n)
|
||||
if m:
|
||||
@ -54,6 +59,9 @@ def _detect_snap_origin(name: str):
|
||||
m = SNAP_SLPM_RE.match(n)
|
||||
if m:
|
||||
return "slpm", m.group("author")
|
||||
m = SNAP_MANUAL_RE.match(n)
|
||||
if m:
|
||||
return "manual", m.group("author")
|
||||
return None, None
|
||||
|
||||
|
||||
@ -114,7 +122,7 @@ def _walk_snapshots(snapshot_list, vm, vcenter_name, vcenter_id, vm_moid, parent
|
||||
"created_at": created_iso,
|
||||
"age_days": round(age_days, 2) if age_days is not None else None,
|
||||
"author": author,
|
||||
"origin": origin, # 'patchcenter' | 'slpm' | None
|
||||
"origin": origin, # 'patchcenter' | 'slpm' | 'manual' | None
|
||||
"is_managed_format": is_managed, # any des 2 formats SecOps
|
||||
"is_patchcenter_format": origin == "patchcenter",
|
||||
"is_current": bool(getattr(s, "id", None) and vm.snapshot and vm.snapshot.currentSnapshot
|
||||
|
||||
@ -66,10 +66,16 @@
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-1">Users actifs (hors admins) — défaut = toi.</p>
|
||||
</div>
|
||||
<div class="col-span-3">
|
||||
<div class="col-span-2">
|
||||
<label class="text-xs text-gray-500">Âge minimum (jours)</label>
|
||||
<input type="number" id="f-min-age" value="3" min="0" class="w-full">
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<label class="text-xs text-gray-500" title="Inclure les snapshots créés hors PatchCenter (formats manuels SANEF)">
|
||||
<input type="checkbox" id="f-include-manual" checked>
|
||||
Tous formats
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-span-3 flex gap-2 items-end">
|
||||
<button id="btn-refresh" class="btn-action btn-pre" type="button" style="
|
||||
padding: 6px 14px; font-size: 0.8rem; font-weight: 700;
|
||||
@ -118,6 +124,7 @@
|
||||
const fVc = document.getElementById('f-vcenter');
|
||||
const fIntervenant = document.getElementById('f-intervenant');
|
||||
const fMinAge = document.getElementById('f-min-age');
|
||||
const fIncludeManual = document.getElementById('f-include-manual');
|
||||
const intervenantDefault = "{{ intervenant_default|e }}";
|
||||
const btnRefresh = document.getElementById('btn-refresh');
|
||||
const btnDelete = document.getElementById('btn-delete');
|
||||
@ -141,14 +148,20 @@
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
// Toujours: snapshots PatchCenter uniquement (origin === 'patchcenter')
|
||||
// + intervenant choisi dans le dropdown
|
||||
// + age minimum
|
||||
// - Si "Tous formats" décoché : snapshots PatchCenter uniquement (origin === 'patchcenter')
|
||||
// - Sinon : tous formats reconnus (patchcenter / slpm / manual)
|
||||
// - Filtre intervenant : author extrait du regex OU nom du snap contient l'intervenant
|
||||
// - Filtre âge minimum
|
||||
const intervenant = (fIntervenant.value || '').trim().toLowerCase();
|
||||
const minAge = parseFloat(fMinAge.value) || 0;
|
||||
const includeManual = fIncludeManual ? fIncludeManual.checked : false;
|
||||
return allSnaps.filter(s => {
|
||||
if (s.origin !== 'patchcenter') return false;
|
||||
if (intervenant && (!s.author || s.author.toLowerCase() !== intervenant)) return false;
|
||||
if (!includeManual && s.origin !== 'patchcenter') return false;
|
||||
if (intervenant) {
|
||||
const authorOk = s.author && s.author.toLowerCase() === intervenant;
|
||||
const nameOk = s.snap_name && s.snap_name.toLowerCase().includes(intervenant);
|
||||
if (!authorOk && !nameOk) return false;
|
||||
}
|
||||
if (minAge > 0 && (s.age_days === null || s.age_days < minAge)) return false;
|
||||
return true;
|
||||
});
|
||||
@ -289,6 +302,7 @@
|
||||
|
||||
[fIntervenant, fMinAge].forEach(el => el.addEventListener('input', render));
|
||||
fIntervenant.addEventListener('change', render);
|
||||
if (fIncludeManual) fIncludeManual.addEventListener('change', render);
|
||||
|
||||
selAll.addEventListener('change', () => {
|
||||
tbody.querySelectorAll('.row-cb').forEach(cb => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user