#!/usr/bin/env catnip
# Barres de progression avec tqdm
# tqdm affiche une barre de progression sur n'importe quel itérable
#
# DEPS: tqdm
tqdm = import('tqdm')
time = import('time')
random = import('random')
random.seed(42)
# Types somme : ensembles fermés de catégories, explicites et vérifiés à l'exhaustivité
# par le linter dans les match. La méthode label() rend la forme d'affichage.
union Kind {
sensor; log; metric; event
label(self): str => {
match self {
Kind.sensor => { "sensor" }
Kind.log => { "log" }
Kind.metric => { "metric" }
Kind.event => { "event" }
}
}
}
union Status {
ok; warning; critical; anomaly
label(self): str => {
match self {
Status.ok => { "ok" }
Status.warning => { "warning" }
Status.critical => { "critical" }
Status.anomaly => { "anomaly" }
}
}
}
union Tag {
high; mid; low
label(self): str => {
match self {
Tag.high => { "high" }
Tag.mid => { "mid" }
Tag.low => { "low" }
}
}
}
struct Record {
id: int; kind: Kind; value: float
display(self): str => { f"#{self.id} [{self.kind.label()}] {self.value}" }
}
struct Tagged { id: int; tag: Tag; value: float }
# Génération de données
kinds = list(Kind.sensor, Kind.log, Kind.metric, Kind.event)
make_record = (i: int): Record => {
kind = kinds[random.randint(0, len(kinds) - 1)]
val = round(random.uniform(0, 100), 2)
Record(i, kind, val)
}
records = list()
for i in range(200) {
records.append(make_record(i))
}
print(f"⇒ {len(records)} records générés")
print(f" Exemple : {records[0].display()}")
# Traitement avec barre de progression
print()
print("⇒ Traitement séquentiel")
process = (record: Record): Status => {
time.sleep(0.005)
match True {
_ if record.value > 95 => { Status.critical }
_ if record.value > 80 => { Status.warning }
_ if record.value < 5 => { Status.anomaly }
_ => { Status.ok }
}
}
results = list()
for r in tqdm.tqdm(records, desc="process", unit="rec") {
results.append(process(r))
}
# Classification des résultats
print()
print("⇒ Résultats")
count_status = (status: Status): int => {
n = 0
for r in results {
if r == status { n = n + 1 }
}
n
}
for s in list(Status.ok, Status.warning, Status.critical, Status.anomaly) {
n = count_status(s)
bar = "#" * int(n / 2)
print(f" {s.label():>10} : {n:>3} {bar}")
}
# Pipeline multi-étapes
print()
print("⇒ Pipeline multi-étapes")
struct Stage {
name: str; fn
run(self, data) => {
out = list()
for item in tqdm.tqdm(data, desc=self.name, unit="rec", leave=False) {
out.append(self.fn(item))
}
out
}
}
normalize = (r: Record): Record => {
time.sleep(0.002)
Record(r.id, r.kind, round(r.value / 100, 4))
}
classify_tag = (value: float): Tag => {
match True {
_ if value > 0.8 => { Tag.high }
_ if value > 0.2 => { Tag.mid }
_ => { Tag.low }
}
}
tag = (r: Record): Tagged => {
time.sleep(0.002)
Tagged(r.id, classify_tag(r.value), r.value)
}
pipeline = list(
Stage("normalize", normalize),
Stage("tag", tag),
)
data = records
for stage in pipeline {
data = stage.run(data)
}
# Comptage par tag
print()
print("⇒ Distribution après pipeline")
for variant in list(Tag.high, Tag.mid, Tag.low) {
n = 0
for t in data {
if t.tag == variant { n = n + 1 }
}
print(f" {variant.label():>5} : {n}")
}
# Barre manuelle (tqdm.tqdm sans itérable)
print()
print("⇒ Barre manuelle")
pbar = tqdm.tqdm(total=50, desc="upload", unit="chunk")
i = 0
while i < 50 {
time.sleep(0.01)
pbar.update(1)
i = i + 1
}
pbar.close()
print()
print("⇒ Terminé")