Compare commits
11 Commits
2ff413efd3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5921d27f1e | |||
|
|
6095bd26ce | ||
|
|
34154c7c4d | ||
|
|
23d5368f40 | ||
| 8d011bd818 | |||
| c718ddfd7c | |||
| 6a2ccb5d2b | |||
| ad09c3ec72 | |||
| 8d65ab03cf | |||
| 9159d2a082 | |||
| faedb4c80c |
11
LISEZMOI.md
11
LISEZMOI.md
@@ -29,7 +29,7 @@ C'est la description de wikipedia https://fr.wikipedia.org/wiki/Jugement_majorit
|
||||
|
||||
(https://en.wikipedia.org/wiki/Majority_judgment)
|
||||
|
||||
L'implémentaion est très basique et miminale :
|
||||
L'implémentation est très basique et miminale :
|
||||
|
||||
|langage|code|
|
||||
|-----|---------------------|
|
||||
@@ -170,3 +170,12 @@ https://framagit.org/framasoft/framadate
|
||||
|
||||
C'est un intégration de framadate dans yunohost.
|
||||
|
||||
## Mieux Voter
|
||||
|
||||
https://app.mieuxvoter.fr/
|
||||
|
||||
Il est possible d'importer un csv.
|
||||
|
||||
https://github.com/MieuxVoter/majority-judgment-web-app
|
||||
|
||||
exemples sous samples/mieuxvoter
|
||||
12
README.md
12
README.md
@@ -1,4 +1,4 @@
|
||||
# Glue to use majortiy judgement algorithm in various polls applications
|
||||
# Glue to use majority judgement algorithm in various polls applications
|
||||
|
||||
Initial trigger of this project is implementation of this in Nextcloud poll app https://github.com/nextcloud/polls/issues/3472, project pushed by https://www.astrolabe.coop/
|
||||
|
||||
@@ -14,7 +14,6 @@ https://en.wikipedia.org/wiki/Majority_judgment
|
||||
|
||||
After some research most of implementation is done in https://github.com/MieuxVoter
|
||||
|
||||
|
||||
Sorry for inconvenience but currently only french version is available.
|
||||
|
||||
LISEZMOI.md
|
||||
@@ -50,4 +49,11 @@ dev done on Ubuntu 24.04.2 LTS
|
||||
```
|
||||
cd code
|
||||
./check_csv.sh ../samples/nextcloud_poll_export/poll1.csv
|
||||
```
|
||||
```
|
||||
|
||||
```
|
||||
./check_csv.sh ../samples/nextcloud_poll_export/poll1.csv >../samples/0.json
|
||||
```
|
||||
|
||||
et tester
|
||||
|
||||
|
||||
@@ -1,4 +1,37 @@
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 artlog@l0g.eu
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
|
||||
csv_file="$1"
|
||||
python3 convert_nextcloud_poll_csv.py "$csv_file" 'Très Bien' 'Bien' 'Assez Bien' 'Passable' 'Insuffisant' 'A Rejeter'
|
||||
toolsdir=../lib
|
||||
pushd $toolsdir >/dev/null
|
||||
toolsdir=$(pwd)
|
||||
source metascript.sh
|
||||
popd >/dev/null
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
case "$1" in
|
||||
*)
|
||||
if [[ -z $csv_file ]]
|
||||
then
|
||||
csv_file="$1"
|
||||
else
|
||||
parsemetaarg "$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
metarun=metarun
|
||||
|
||||
if [[ -z $csv_file ]]
|
||||
then
|
||||
log_error "Missing csv_file argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enforcefile "$csv_file" exists
|
||||
|
||||
$metarun python3 convert_nextcloud_poll_csv.py "$csv_file" 'Très Bien' 'Bien' 'Assez Bien' 'Passable' 'Insuffisant' 'A Rejeter'
|
||||
|
||||
@@ -3,41 +3,41 @@ import sys
|
||||
import csv
|
||||
|
||||
# using non modified nextcloud standard text poll
|
||||
nom_mentions=['yes','maybe','','no']
|
||||
mentions_name=['yes','maybe','','no']
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
csv_file=sys.argv[1]
|
||||
if len(sys.argv) > 2:
|
||||
nom_mentions=[]
|
||||
mentions_name=[]
|
||||
for i in range(2,len(sys.argv)):
|
||||
nom_mentions.append(sys.argv[i])
|
||||
mentions_name.append(sys.argv[i])
|
||||
else:
|
||||
print('missing file argument')
|
||||
exit(1)
|
||||
|
||||
def mention_index(mention):
|
||||
return nom_mentions.index(mention)
|
||||
return mentions_name.index(mention)
|
||||
|
||||
verbose=False
|
||||
|
||||
mentions=len(nom_mentions)
|
||||
mentions=len(mentions_name)
|
||||
default_mention=mentions-1
|
||||
|
||||
warnings=[]
|
||||
candidats={}
|
||||
candidats_alias=[]
|
||||
candidates={}
|
||||
candidates_alias=[]
|
||||
a_vote={}
|
||||
votes=[]
|
||||
with open(csv_file,'r') as csv_fd:
|
||||
vote_reader = csv.reader(csv_fd, delimiter=',')
|
||||
for row in vote_reader:
|
||||
if len(candidats_alias) == 0:
|
||||
if len(candidates_alias) == 0:
|
||||
for i in range(2,len(row)):
|
||||
candidat_alias=row[i]
|
||||
candidats_alias.append(candidat_alias)
|
||||
candidate_alias=row[i]
|
||||
candidates_alias.append(candidate_alias)
|
||||
# no other name given
|
||||
if not candidat_alias in candidats:
|
||||
candidats[candidat_alias]=candidat_alias
|
||||
if not candidate_alias in candidates:
|
||||
candidates[candidate_alias]=candidate_alias
|
||||
else:
|
||||
# check if not duplicated
|
||||
participant=row[0]
|
||||
@@ -47,20 +47,20 @@ with open(csv_file,'r') as csv_fd:
|
||||
a_vote[participant]=row[1]
|
||||
vote={}
|
||||
for i in range(2,len(row)):
|
||||
candidat_index=i-2
|
||||
candidat_alias=candidats_alias[candidat_index]
|
||||
candidate_index=i-2
|
||||
candidate_alias=candidates_alias[candidate_index]
|
||||
mention=row[i]
|
||||
vote[candidat_alias]=mention_index(mention)
|
||||
vote[candidate_alias]=mention_index(mention)
|
||||
votes.append(vote)
|
||||
|
||||
|
||||
default_vote={}
|
||||
|
||||
if verbose:
|
||||
print(','.join(candidats))
|
||||
print(','.join(nom_mentions))
|
||||
print(','.join(candidates))
|
||||
print(','.join(mentions_name))
|
||||
|
||||
result = {'candidats':candidats,'votants':{'decompte':len(a_vote)},'mentions':nom_mentions,'votation':{'votes':votes}}
|
||||
result = {'candidates':candidates,'voters':{'count':len(a_vote)},'mentions':mentions_name,'votation':{'votes':votes}}
|
||||
print(json.dumps(result))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import json
|
||||
import sys
|
||||
from majority_judgment import majority_judgment
|
||||
|
||||
verbose=False
|
||||
|
||||
def collect_votes(votes,candidats,default_mention,warnings):
|
||||
collect={}
|
||||
for vote in votes:
|
||||
# missing candidat in vote picks the worse one
|
||||
for candidat in candidats:
|
||||
if not candidat in vote:
|
||||
vote[candidat]=default_mention
|
||||
|
||||
for candidat,mention in vote.items():
|
||||
if mention > default_mention or mention < 0:
|
||||
mention = default_mention
|
||||
warnings.append("index de mention invalide, soit la liste des mentions est erronnée soit la mention dans le vote est erronée")
|
||||
if candidat in collect:
|
||||
if mention in collect[candidat]:
|
||||
collect[candidat][mention]=collect[candidat][mention]+1
|
||||
else:
|
||||
collect[candidat][mention]=1
|
||||
else:
|
||||
collect[candidat]={mention:1}
|
||||
return collect
|
||||
|
||||
def arrange_votes_par_candidat(votes,candidats,default_mention):
|
||||
vpc={}
|
||||
for candidat in candidats:
|
||||
vpc[candidat]=[vote[candidat] if candidat in vote else default_mention for vote in votes]
|
||||
return vpc
|
||||
|
||||
def jugement_majoritaire(poll):
|
||||
|
||||
verbose=False
|
||||
crosscheck=True
|
||||
|
||||
votants=poll['votants']['decompte']
|
||||
candidats=poll['candidats']
|
||||
nombre_candidats=len(candidats)
|
||||
|
||||
# l'ordre des mentions est de la meilleure à la pire (reject)
|
||||
nom_mentions=poll['mentions']
|
||||
mentions=len(nom_mentions)
|
||||
default_mention=mentions-1
|
||||
|
||||
votes=poll["votation"]["votes"]
|
||||
if verbose:
|
||||
print('candidats:' + str(candidats))
|
||||
print(votes)
|
||||
|
||||
warnings=[]
|
||||
|
||||
collect=collect_votes(votes,list(candidats),default_mention,warnings)
|
||||
if verbose:
|
||||
print(collect)
|
||||
|
||||
votant_median_check= votants / 2 if votants % 2 == 0 else ((votants-1) / 2) +1
|
||||
|
||||
votant_median=votants // 2 + votants % 2
|
||||
|
||||
if votant_median_check != votant_median:
|
||||
print('[ERROR] le nombre median de votants semble erroné. contactez le developpeur de ce code.' + str(votant_median_check))
|
||||
|
||||
merite={}
|
||||
mention_mediane={}
|
||||
merite_pourcent={}
|
||||
|
||||
votes_par_candidat=arrange_votes_par_candidat(votes,candidats,mentions-1)
|
||||
|
||||
# cumul : du meilleur au pire
|
||||
# range_mentions = range(len(mentions))
|
||||
# cumul du pire au meilleur
|
||||
range_mentions = range(mentions-1,-1,-1)
|
||||
|
||||
for candidat in list(candidats):
|
||||
vote=collect[candidat]
|
||||
cumul=0
|
||||
m=[0 for i in range(mentions)]
|
||||
mention_m=None
|
||||
pourcent=[0 for i in range(mentions)]
|
||||
for mention in range_mentions:
|
||||
if mention in vote:
|
||||
cumul=cumul+vote[mention]
|
||||
pourcent[mention]= ( 100 * vote[mention] ) / votants
|
||||
if cumul >= votant_median and mention_m is None:
|
||||
mention_m=mention
|
||||
m[mention]=cumul
|
||||
if cumul < votants:
|
||||
print('[ERROR] le cumul des votes doit correspondre au nombre de votants. contactez le developpeur de ce code.')
|
||||
mention_mediane[candidat]=mention_m
|
||||
merite[candidat]=m
|
||||
merite_pourcent[candidat]=pourcent
|
||||
|
||||
if verbose:
|
||||
print(merite)
|
||||
print(mention_mediane)
|
||||
|
||||
# les gagnants sont ceux qui ont la mention mediane minimale ( la meilleure )
|
||||
found=[]
|
||||
for mention in range(0,mentions):
|
||||
for candidat,merite_median in mention_mediane.items():
|
||||
if merite_median == mention:
|
||||
found.append({candidat:candidats[candidat]})
|
||||
if len(found) > 0:
|
||||
break
|
||||
|
||||
if verbose:
|
||||
print(found)
|
||||
print(nom_mentions[mention])
|
||||
|
||||
# expanded=expand_collect(collect,list(candidats),mentions)
|
||||
if verbose:
|
||||
print(votes_par_candidat)
|
||||
|
||||
result = {'resultat':{'mention':nom_mentions[mention],'candidats':found},'merite':merite,'profil':merite_pourcent,"warnings":warnings}
|
||||
|
||||
if crosscheck:
|
||||
# cross check with a well known implementation
|
||||
result["crosscheck"]=majority_judgment(votes_par_candidat, reverse=True)
|
||||
|
||||
return result
|
||||
|
||||
124
code/majority_judgment_method/__init__.py
Normal file
124
code/majority_judgment_method/__init__.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import json
|
||||
import sys
|
||||
from majority_judgment import majority_judgment
|
||||
|
||||
verbose=False
|
||||
|
||||
def collect_votes(votes,candidates,default_mention,warnings):
|
||||
collect={}
|
||||
for vote in votes:
|
||||
# missing candidate in vote picks the worse one
|
||||
for candidate in candidates:
|
||||
if not candidate in vote:
|
||||
vote[candidate]=default_mention
|
||||
|
||||
for candidate,mention in vote.items():
|
||||
if mention > default_mention or mention < 0:
|
||||
mention = default_mention
|
||||
warnings.append("index de mention invalide, soit la liste des mentions est erronnée soit la mention dans le vote est erronée")
|
||||
if candidate in collect:
|
||||
if mention in collect[candidate]:
|
||||
collect[candidate][mention]=collect[candidate][mention]+1
|
||||
else:
|
||||
collect[candidate][mention]=1
|
||||
else:
|
||||
collect[candidate]={mention:1}
|
||||
return collect
|
||||
|
||||
def arrange_votes_by_candidate(votes,candidates,default_mention):
|
||||
vpc={}
|
||||
for candidate in candidates:
|
||||
vpc[candidate]=[vote[candidate] if candidate in vote else default_mention for vote in votes]
|
||||
return vpc
|
||||
|
||||
def majority_judgment_run(poll):
|
||||
|
||||
verbose=False
|
||||
crosscheck=True
|
||||
|
||||
voters=poll['voters']['count']
|
||||
candidates=poll['candidates']
|
||||
nombre_candidates=len(candidates)
|
||||
|
||||
# l'ordre des mentions est de la meilleure à la pire (reject)
|
||||
nom_mentions=poll['mentions']
|
||||
mentions=len(nom_mentions)
|
||||
default_mention=mentions-1
|
||||
|
||||
votes=poll["votation"]["votes"]
|
||||
if verbose:
|
||||
print('candidates:' + str(candidates))
|
||||
print(votes)
|
||||
|
||||
warnings=[]
|
||||
|
||||
collect=collect_votes(votes,list(candidates),default_mention,warnings)
|
||||
if verbose:
|
||||
print(collect)
|
||||
|
||||
voter_median_check= voters / 2 if voters % 2 == 0 else ((voters-1) / 2) +1
|
||||
|
||||
voter_median=voters // 2 + voters % 2
|
||||
|
||||
if voter_median_check != voter_median:
|
||||
print('[ERROR] le nombre median de voters semble erroné. contactez le developpeur de ce code.' + str(voter_median_check))
|
||||
|
||||
merit={}
|
||||
mention_mediane={}
|
||||
merit_pourcent={}
|
||||
|
||||
votes_by_candidate=arrange_votes_by_candidate(votes,candidates,mentions-1)
|
||||
|
||||
# cumul : du meilleur au pire
|
||||
# range_mentions = range(len(mentions))
|
||||
# cumul du pire au meilleur
|
||||
range_mentions = range(mentions-1,-1,-1)
|
||||
|
||||
for candidate in list(candidates):
|
||||
vote=collect[candidate]
|
||||
cumul=0
|
||||
m=[0 for i in range(mentions)]
|
||||
mention_m=None
|
||||
pourcent=[0 for i in range(mentions)]
|
||||
for mention in range_mentions:
|
||||
if mention in vote:
|
||||
cumul=cumul+vote[mention]
|
||||
pourcent[mention]= ( 100 * vote[mention] ) / voters
|
||||
if cumul >= voter_median and mention_m is None:
|
||||
mention_m=mention
|
||||
m[mention]=cumul
|
||||
if cumul < voters:
|
||||
print('[ERROR] cumulated votes should match voters. Contact this code developer.')
|
||||
mention_mediane[candidate]=mention_m
|
||||
merit[candidate]=m
|
||||
merit_pourcent[candidate]=pourcent
|
||||
|
||||
if verbose:
|
||||
print(merit)
|
||||
print(mention_mediane)
|
||||
|
||||
# les gagnants sont ceux qui ont la mention mediane minimale ( la meilleure )
|
||||
found=[]
|
||||
for mention in range(0,mentions):
|
||||
for candidate,merit_median in mention_mediane.items():
|
||||
if merit_median == mention:
|
||||
found.append({candidate:candidates[candidate]})
|
||||
if len(found) > 0:
|
||||
break
|
||||
|
||||
if verbose:
|
||||
print(found)
|
||||
print(nom_mentions[mention])
|
||||
|
||||
# expanded=expand_collect(collect,list(candidates),mentions)
|
||||
if verbose:
|
||||
print(votes_by_candidatee)
|
||||
|
||||
result = {'result':{'mention':nom_mentions[mention],'candidates':found},'merit':merit,'profil':merit_pourcent,"warnings":warnings}
|
||||
|
||||
if crosscheck:
|
||||
# cross check with a well known implementation
|
||||
result["crosscheck"]=majority_judgment(votes_by_candidate, reverse=True)
|
||||
|
||||
return result
|
||||
|
||||
@@ -21,9 +21,9 @@ if ($verbose) {
|
||||
var_dump($poll);
|
||||
}
|
||||
|
||||
$votants=$poll['votants']['decompte'];
|
||||
$candidats=$poll['candidats'];
|
||||
$nombre_candidats=count($candidats);
|
||||
$voters=$poll['voters']['count'];
|
||||
$candidates=$poll['candidates'];
|
||||
$nombre_candidates=count($candidates);
|
||||
|
||||
# l'ordre des mentions est de la meilleure à la pire (reject)
|
||||
$nom_mentions=$poll['mentions'];
|
||||
@@ -33,22 +33,22 @@ $default_mention=$mentions-1;
|
||||
|
||||
$votes=$poll["votation"]["votes"];
|
||||
if ($verbose) {
|
||||
print("candidats\n");
|
||||
var_dump($candidats);
|
||||
print("candidates\n");
|
||||
var_dump($candidates);
|
||||
print("votes:\n");
|
||||
var_dump($votes);
|
||||
}
|
||||
|
||||
$warnings=[];
|
||||
$default_vote=[];
|
||||
foreach ($candidats as $candidat => $nom_candidat ) {
|
||||
foreach ($candidates as $candidat => $nom_candidat ) {
|
||||
$default_vote[$candidat]=$default_mention;
|
||||
}
|
||||
|
||||
$collect=[];
|
||||
foreach ($votes as $vote) {
|
||||
# missing candidat in vote is the worts one
|
||||
foreach ($candidats as $candidat => $nom_candidat ) {
|
||||
foreach ($candidates as $candidat => $nom_candidat ) {
|
||||
if (! array_key_exists($candidat,$vote)) {
|
||||
$vote[$candidat]=$default_mention;
|
||||
}
|
||||
@@ -78,24 +78,24 @@ if ($verbose) {
|
||||
print(json_encode($collect));
|
||||
}
|
||||
|
||||
$votant_median_check= ($votants % 2 == 0) ? intdiv($votants,2) : intdiv(($votants-1),2) + 1;
|
||||
$voter_median_check= ($voters % 2 == 0) ? intdiv($voters,2) : intdiv(($voters-1),2) + 1;
|
||||
|
||||
$votant_median=intdiv($votants, 2) + $votants % 2;
|
||||
$voter_median=intdiv($voters, 2) + $voters % 2;
|
||||
|
||||
if ( $votant_median_check != $votant_median ) {
|
||||
print('[ERROR] le nombre median de votants (' . $votant_median_check . '/' . $votant_median . ') semble erroné. contactez le developpeur de ce code.');
|
||||
if ( $voter_median_check != $voter_median ) {
|
||||
print('[ERROR] le nombre median de voters (' . $voter_median_check . '/' . $voter_median . ') semble erroné. contactez le developpeur de ce code.');
|
||||
}
|
||||
|
||||
$merite=[];
|
||||
$merit=[];
|
||||
$mention_mediane=[];
|
||||
$merite_pourcent=[];
|
||||
$merit_pourcent=[];
|
||||
|
||||
# cumul : du meilleur au pire
|
||||
# range_mentions = range(count(mentions))
|
||||
# cumul du pire au meilleur
|
||||
$range_mentions = $mentions;
|
||||
|
||||
foreach ($candidats as $candidat => $nom_candidat) {
|
||||
foreach ($candidates as $candidat => $nom_candidat) {
|
||||
$vote=$collect[$candidat];
|
||||
$cumul=0;
|
||||
for ($i=0; $i < $range_mentions; $i ++) {
|
||||
@@ -106,24 +106,24 @@ foreach ($candidats as $candidat => $nom_candidat) {
|
||||
for ($mention=$range_mentions -1; $mention >=0; $mention --) {
|
||||
if ( array_key_exists($mention,$vote) ) {
|
||||
$cumul=$cumul+$vote[$mention];
|
||||
$pourcent[$mention]= ( 100 * $vote[$mention] ) / $votants;
|
||||
$pourcent[$mention]= ( 100 * $vote[$mention] ) / $voters;
|
||||
}
|
||||
if ($cumul >= $votant_median and $mention_m == null ) {
|
||||
if ($cumul >= $voter_median and $mention_m == null ) {
|
||||
$mention_m=$mention;
|
||||
}
|
||||
$m[$mention]=$cumul;
|
||||
}
|
||||
if ($cumul < $votants) {
|
||||
print("[ERROR] le cumul des votes " . $cumul . " doit correspondre au nombre de votants. contactez le developpeur de ce code.");
|
||||
if ($cumul < $voters) {
|
||||
print("[ERROR] votes cumulated " . $cumul . " should match voters. Contact this code developer.");
|
||||
}
|
||||
$mention_mediane[$candidat]=$mention_m;
|
||||
$merite[$candidat]=$m;
|
||||
$merite_pourcent[$candidat]=$pourcent;
|
||||
$merit[$candidat]=$m;
|
||||
$merit_pourcent[$candidat]=$pourcent;
|
||||
}
|
||||
|
||||
if ($verbose) {
|
||||
print("\nmerite:\n");
|
||||
print(json_encode($merite));
|
||||
print("\nmerit:\n");
|
||||
print(json_encode($merit));
|
||||
print("\nmention_mediane:\n");
|
||||
print(json_encode($mention_mediane));
|
||||
}
|
||||
@@ -132,10 +132,10 @@ if ($verbose) {
|
||||
$found=[];
|
||||
|
||||
for ($mention = 0; $mention < $mentions; $mention ++) {
|
||||
foreach ($mention_mediane as $candidat => $merite_median)
|
||||
if ($merite_median == $mention) {
|
||||
foreach ($mention_mediane as $candidat => $merit_median)
|
||||
if ($merit_median == $mention) {
|
||||
$item=[];
|
||||
$item[$candidat]=$candidats[$candidat];
|
||||
$item[$candidat]=$candidates[$candidat];
|
||||
array_push($found,$item);
|
||||
}
|
||||
if (count($found) > 0) {
|
||||
@@ -148,7 +148,7 @@ if ($verbose) {
|
||||
print($nom_mentions[$mention]);
|
||||
}
|
||||
|
||||
$result = ['resultat'=> ['mention'=>$nom_mentions[$mention],'candidats' => $found],'merite' => $merite,'profil' => $merite_pourcent,"warnings" => $warnings];
|
||||
$result = ['result'=> ['mention'=>$nom_mentions[$mention],'candidates' => $found],'merit' => $merit,'profil' => $merit_pourcent,"warnings" => $warnings];
|
||||
|
||||
print(json_encode($result));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
import sys
|
||||
from jugement_majoritaire import jugement_majoritaire
|
||||
from majority_judgment_method import majority_judgment_run
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
poll_file=sys.argv[1]
|
||||
@@ -16,6 +16,6 @@ with open(poll_file,'r') as poll_fd:
|
||||
if verbose:
|
||||
print(poll)
|
||||
|
||||
result = jugement_majoritaire(poll)
|
||||
result = majority_judgment_run(poll)
|
||||
|
||||
print(json.dumps(result))
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
|
||||
# contexte
|
||||
|
||||
Choix des noms de mentions
|
||||
|
||||
|
||||
https://fr.wikipedia.org/wiki/Jugement_majoritaire
|
||||
vs
|
||||
https://en.wikipedia.org/wiki/Jugement_majoritaire
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# Nextcloud Dev En
|
||||
# Nextcloud Dev Environment for Poll
|
||||
|
||||
Two options
|
||||
|
||||
one created manualy with a simple podman and a local database
|
||||
|
||||
one fully under podman, done on nextcloud conference
|
||||
|
||||
# Option 1 / simple podman
|
||||
|
||||
Create a dev env for nextcloud
|
||||
|
||||
setup databse :
|
||||
setup database :
|
||||
|
||||
```
|
||||
./setupdatabase.sh
|
||||
@@ -29,7 +37,7 @@ puis
|
||||
./sync_poll.sh
|
||||
```
|
||||
|
||||
il reste encore a compiler avec les dependances composer et le build npm vuejs
|
||||
il reste encore a compiler avec les dépendances composer et le build npm vuejs
|
||||
|
||||
```
|
||||
./enter_www_data.sh
|
||||
@@ -63,3 +71,50 @@ utiliser la base de donnee postgres et non le SQLLite ?
|
||||
|
||||
./migrate_db_sqlite2postgres.sh
|
||||
|
||||
|
||||
# Option 2 : nextcloud-docker-dev
|
||||
|
||||
setup
|
||||
|
||||
```
|
||||
git clone git@github.com:juliusknorr/nextcloud-docker-dev.git
|
||||
git git@github.com:nextcloud/server.git
|
||||
```
|
||||
|
||||
check nextcloud-docker-dev.env then
|
||||
|
||||
```
|
||||
cp nextcloud-docker-dev.env nextcloud-docker-dev.git/.env
|
||||
```
|
||||
|
||||
https setup, local private PKI.
|
||||
|
||||
missing setup cert and setupcert host ... mkcert compilation + install
|
||||
|
||||
update-certs update-hosts
|
||||
|
||||
https://nextcloud-dev.l0g.eu
|
||||
|
||||
|
||||
then will be done as sudo ( RootLess or podman limitation ? )
|
||||
|
||||
```
|
||||
./runit.sh dryrun up
|
||||
```
|
||||
|
||||
start
|
||||
|
||||
```
|
||||
./runit.sh up
|
||||
```
|
||||
|
||||
stop
|
||||
|
||||
```
|
||||
./runit.sh down
|
||||
```
|
||||
|
||||
|
||||
Pour les mails : https://mail-dev.l0g.eu/#
|
||||
|
||||
un MailHog est installé !
|
||||
@@ -11,5 +11,13 @@ flavor=dev
|
||||
|
||||
database_name=nextcloud_$flavor
|
||||
username=nextcloud_$flavor
|
||||
|
||||
# postgres
|
||||
db_port=5432
|
||||
db_hostname=127.0.0.1
|
||||
dbtype_nextcloude=pgsql
|
||||
|
||||
# mariadb/mysql
|
||||
db_port=3306
|
||||
db_hostname=127.0.0.1
|
||||
dbtype_nextcloud=mysql
|
||||
|
||||
@@ -5,25 +5,44 @@
|
||||
# [[ -f $log_functions ]] || { echo "[FATAL] Missing $log_functions" >&2 ; exit 1 ;}
|
||||
# source $log_functions
|
||||
|
||||
metalog_color_start() {
|
||||
if [[ -n $metalog_color ]]
|
||||
then
|
||||
echo -en "${metalog_color}"
|
||||
fi
|
||||
}
|
||||
|
||||
metalog_color_stop() {
|
||||
if [[ -n $metalog_color ]]
|
||||
then
|
||||
echo -en "\033[0m"
|
||||
fi
|
||||
}
|
||||
|
||||
log_any()
|
||||
{
|
||||
priority=$1
|
||||
shift
|
||||
metalog_color_start
|
||||
echo "[$priority] $@" >&2
|
||||
metalog_color_stop
|
||||
}
|
||||
|
||||
log_fatal()
|
||||
{
|
||||
local metalog_color=$metalog_color_error
|
||||
log_any FATAL "$*"
|
||||
}
|
||||
|
||||
log_error()
|
||||
{
|
||||
local metalog_color=$metalog_color_error
|
||||
log_any ERROR "$*"
|
||||
}
|
||||
|
||||
log_warn()
|
||||
{
|
||||
local metalog_color=$metalog_color_warning
|
||||
log_any WARN "$*"
|
||||
}
|
||||
|
||||
@@ -32,59 +51,36 @@ log_info()
|
||||
log_any INFO "$*"
|
||||
}
|
||||
|
||||
log_success()
|
||||
{
|
||||
local metalog_color=$metalog_color_success
|
||||
log_info "$*"
|
||||
}
|
||||
|
||||
log_debug()
|
||||
{
|
||||
local metalog_color=$metalog_color_info
|
||||
[[ -n $debug ]] && log_any DEBUG "$*"
|
||||
}
|
||||
|
||||
|
||||
deferpipe()
|
||||
{
|
||||
cat
|
||||
echo "# <previous line> | $@"
|
||||
}
|
||||
|
||||
autoquoteargs()
|
||||
{
|
||||
echo -n "$1"
|
||||
shift
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
if [[ "$1" =~ [\ \$] ]]
|
||||
then
|
||||
echo -n " '$1'"
|
||||
else
|
||||
echo -n " $1"
|
||||
fi
|
||||
shift
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
echoarray()
|
||||
{
|
||||
declare -a arr=("${@}")
|
||||
declare -i len=${#arr[@]}
|
||||
# Show passed array
|
||||
for ((n = 0; n < len; n++))
|
||||
do
|
||||
echo -en " \"${arr[$n]}\""
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
include_source()
|
||||
{
|
||||
if [[ -f $1 ]]
|
||||
then
|
||||
source $1
|
||||
else
|
||||
log_fatal "Missing $1 script"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verbose()
|
||||
{
|
||||
[[ -n $verbose ]] && log_any $verbose $@
|
||||
}
|
||||
|
||||
metalog_no_colors()
|
||||
{
|
||||
metalog_color_info=
|
||||
metalog_color_success=
|
||||
metalog_color_error=
|
||||
metalog_color_warning=
|
||||
}
|
||||
|
||||
# default colors
|
||||
metalog_default_colors()
|
||||
{
|
||||
metalog_color_info="\033[38;5;79m"
|
||||
metalog_color_success="\033[1;32m"
|
||||
metalog_color_error="\033[1;31m"
|
||||
metalog_color_warning="\033[1;34m"
|
||||
}
|
||||
|
||||
@@ -1,30 +1,83 @@
|
||||
#!/bin/bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2025 artlog@l0g.eu
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# metascript to include
|
||||
#
|
||||
# usual way if no parameters used by outer script :
|
||||
#
|
||||
# source $(dirname "$0")/metascript.sh
|
||||
#
|
||||
# defaultmetainit $@
|
||||
#
|
||||
# to generate file header for caller script do :
|
||||
# bash <relative_path_to>/metascript.sh header
|
||||
# ex:
|
||||
# bash lib/metascript.sh header
|
||||
|
||||
metascript_version=v1.1.0
|
||||
|
||||
# if called directly
|
||||
if [[ "$0" =~ ^(.*)/metascript.sh$ ]]
|
||||
then
|
||||
prefix=${BASH_REMATCH[1]}
|
||||
case $1 in
|
||||
header)
|
||||
cat <<EOF
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 artlog@l0g.eu
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# header generated by $0 $1
|
||||
# metascript_version=$metascript_version
|
||||
|
||||
toolsdir=$prefix
|
||||
pushd \$toolsdir >/dev/null
|
||||
toolsdir=\$(pwd)
|
||||
source metascript.sh
|
||||
popd >/dev/null
|
||||
|
||||
metarun=metarun
|
||||
# change default to sudo if needed
|
||||
# metasudo=sudo
|
||||
|
||||
while [[ \$# > 0 ]]
|
||||
do
|
||||
case "\$1" in
|
||||
*)
|
||||
parsemetaarg "\$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
usage
|
||||
EOF
|
||||
;;
|
||||
version)
|
||||
echo $metascript_version
|
||||
;;
|
||||
*)
|
||||
echo "[WARNING] only header and version arguments are supported. (prefix=$prefix), this script is intended to be included not to be directly called." >&2
|
||||
exit 1
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$metascript_included" == "yes" ]]
|
||||
then
|
||||
echo "[WARNING] metascript already included ($0)" >&2
|
||||
else
|
||||
|
||||
metascript_included=yes
|
||||
|
||||
# assume all tools are in lib/
|
||||
toolsdir=$(dirname $(readlink -f $0))/lib
|
||||
# all tools resources are relative to this directory
|
||||
# at this step $(pwd) should be this of metascript.sh
|
||||
# while $0 is this of caller script using metascript
|
||||
|
||||
# project directory
|
||||
# relative
|
||||
# toolsparentdir=$(realpath --relative-to "$(pwd)" $(readlink -f $0))/
|
||||
# absolute
|
||||
toolsparentdir=$(realpath $(readlink -f $0))/
|
||||
if [[ -z $toolsdir ]]
|
||||
then
|
||||
# assume all tools are in lib/
|
||||
toolsdir=$(dirname $(readlink -f $0))/lib
|
||||
# all tools resources are relative to this directory
|
||||
fi
|
||||
|
||||
if [[ -z $log_functions ]]
|
||||
then
|
||||
log_functions=$toolsdir/log_functions.sh
|
||||
[[ -f $log_functions ]] || { echo "[FATAL] Missing $log_functions" >&2 ; exit 1 ;}
|
||||
[[ -f $log_functions ]] || { echo "[FATAL] Missing $log_functions , toolsdir=$toolsdir script=$0" >&2 ; exit 1 ;}
|
||||
source $log_functions
|
||||
fi
|
||||
|
||||
@@ -33,28 +86,100 @@ metascript_usage()
|
||||
cat <<EOF >&2
|
||||
metascript commands :
|
||||
|
||||
help|usage help or usage of this {$0} tool
|
||||
help|usage help or usage of this {$0} tool. stop after usage display.
|
||||
dryrun|show|showdoc display what should/will be done
|
||||
|
||||
defersource= script file defining defer() non sandard function
|
||||
defersource= script file defining defer() non standard function
|
||||
defer= defer function to use, default is showdoc
|
||||
toolsresourcesdir= where to pick resource
|
||||
default to parent of script $toolsresourcesdir
|
||||
mostly used with dryrun
|
||||
apply default : will actual do work without defer
|
||||
text_format=md set text format output to markdown
|
||||
EOF
|
||||
}
|
||||
|
||||
md_output_init() {
|
||||
md_quote='```'
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "[WARNING} no specific usage function for {$0}, to improve by developer" >&2
|
||||
log_warn "no specific usage function for {$0}, to improve by developer"
|
||||
echo >&2
|
||||
metascript_usage
|
||||
}
|
||||
|
||||
showinfo()
|
||||
{
|
||||
echo $@
|
||||
echo "$@"
|
||||
}
|
||||
|
||||
metarun()
|
||||
{
|
||||
$defer $metasudo "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
metasudo_auto()
|
||||
{
|
||||
if [[ $EUID -eq 0 ]]
|
||||
then
|
||||
log_info "already effective user id as root, don't require sudo"
|
||||
else
|
||||
metasudo=sudo
|
||||
fi
|
||||
}
|
||||
|
||||
autoquoteargs()
|
||||
{
|
||||
echo -n "$1"
|
||||
shift
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
if [[ "$1" =~ [\ \$] ]]
|
||||
then
|
||||
echo -n " '$1'"
|
||||
else
|
||||
echo -n " $1"
|
||||
fi
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
echoarray()
|
||||
{
|
||||
declare -a arr=("${@}")
|
||||
declare -i len=${#arr[@]}
|
||||
# Show passed array
|
||||
for ((n = 0; n < len; n++))
|
||||
do
|
||||
echo -en " \"${arr[$n]}\""
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
start_quote() {
|
||||
[[ -n $md_quote ]] && echo "$md_quote"
|
||||
}
|
||||
|
||||
end_quote() {
|
||||
[[ -n $md_quote ]] && echo "$md_quote"
|
||||
}
|
||||
|
||||
deferpipe()
|
||||
{
|
||||
cat
|
||||
echo "# <previous line> | $@"
|
||||
}
|
||||
|
||||
include_source()
|
||||
{
|
||||
local script="$1"
|
||||
|
||||
enforce var script
|
||||
|
||||
source "$script"
|
||||
}
|
||||
|
||||
showdoc()
|
||||
@@ -62,12 +187,12 @@ showdoc()
|
||||
if [[ $1 =~ ^tools/ ]]
|
||||
then
|
||||
# assumes it handles ENV_METASCRIPT_DEFER
|
||||
$@
|
||||
"$@"
|
||||
else
|
||||
echo '```'
|
||||
autoquoteargs $@
|
||||
start_quote
|
||||
autoquoteargs "$@"
|
||||
echo
|
||||
echo '```'
|
||||
end_quote
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -77,57 +202,149 @@ redirectto()
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
echo "Copy to $tofile"
|
||||
echo '```'
|
||||
start_quote
|
||||
cat
|
||||
echo '```'
|
||||
end_quote
|
||||
else
|
||||
cat > $tofile
|
||||
fi
|
||||
}
|
||||
|
||||
execredirectfrom()
|
||||
redirectappendto()
|
||||
{
|
||||
tofile=$1
|
||||
shift
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
echo '```'
|
||||
autoquoteargs $@
|
||||
echo ' < '"$tofile"
|
||||
echo '```'
|
||||
echo "Append to $tofile"
|
||||
start_quote
|
||||
cat
|
||||
end_quote
|
||||
else
|
||||
$@ < $tofile
|
||||
cat >> $tofile
|
||||
fi
|
||||
}
|
||||
|
||||
execredirectfrom()
|
||||
{
|
||||
fromfile="$1"
|
||||
shift
|
||||
enforcefile "$fromfile" exists
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
start_quote
|
||||
autoquoteargs "$@"
|
||||
echo ' < '"$fromfile"
|
||||
end_quote
|
||||
else
|
||||
"$@" < $fromfile
|
||||
fi
|
||||
}
|
||||
|
||||
execredirectto()
|
||||
{
|
||||
tofile=$1
|
||||
tofile="$1"
|
||||
shift
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
echo '```'
|
||||
autoquoteargs $@
|
||||
start_quote
|
||||
autoquoteargs "$@"
|
||||
echo ' > '"$tofile"
|
||||
echo '```'
|
||||
end_quote
|
||||
else
|
||||
$@ > $tofile
|
||||
"$@" > $tofile
|
||||
fi
|
||||
}
|
||||
|
||||
defer_exec_set_var()
|
||||
{
|
||||
tovar="$1"
|
||||
shift
|
||||
cat <<EOF
|
||||
$md_quote
|
||||
$tovar=\$($(autoquoteargs "$@"))
|
||||
$md_quote
|
||||
EOF
|
||||
}
|
||||
|
||||
execredirecttoroot()
|
||||
{
|
||||
tofile="$1"
|
||||
shift
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
start_quote
|
||||
autoquoteargs "$@"
|
||||
echo ' | sudo tee '"$tofile"' >/dev/null'
|
||||
end_quote
|
||||
else
|
||||
"$@" | sudo tee $tofile >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
execredirectas_to()
|
||||
{
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
user=*)
|
||||
if [[ "$1" =~ ^user=([a-z]+[-_a-z0-9]*)$ ]]
|
||||
then
|
||||
asuser=${BASH_REMATCH[1]}
|
||||
else
|
||||
log_fatal "execredirecttouser missing user= in '$@'"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
;;
|
||||
chroot=*)
|
||||
aschroot=${1/chroot=}
|
||||
;;
|
||||
*)
|
||||
tofile=$1
|
||||
shift
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
local command=()
|
||||
if [[ -n $aschroot ]]
|
||||
then
|
||||
command=(sudo chroot)
|
||||
if [[ -n $asuser ]]
|
||||
then
|
||||
# no group yet
|
||||
command+=(--userspec=$asuser:$asuser)
|
||||
fi
|
||||
command+=($aschroot)
|
||||
elif [[ -n $asuser ]]
|
||||
then
|
||||
command=(sudo -u $asuser)
|
||||
fi
|
||||
command+=(tee $tofile)
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
start_quote
|
||||
autoquoteargs "$@"
|
||||
echo ' | '"${command[@]}"' >/dev/null'
|
||||
end_quote
|
||||
else
|
||||
"$@" | "${command[@]}" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
pipeto()
|
||||
{
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
echo '```'
|
||||
echo "$md_quote"
|
||||
echo -n 'cat << EOF| '
|
||||
autoquoteargs $@
|
||||
autoquoteargs "$@"
|
||||
echo
|
||||
cat
|
||||
echo "EOF"
|
||||
echo '```'
|
||||
echo "$md_quote"
|
||||
else
|
||||
cat | $@
|
||||
cat | "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -217,129 +434,6 @@ check_missing_dest_dir()
|
||||
fi
|
||||
}
|
||||
|
||||
sed_substitute_expr()
|
||||
{
|
||||
local from="$1"
|
||||
shift
|
||||
local to="$1"
|
||||
shift
|
||||
local s='/'
|
||||
if [[ $from =~ [\\] ]]
|
||||
then
|
||||
# escape char \ should be doubled
|
||||
from=${from//\\/\\\\}
|
||||
fi
|
||||
if [[ $from =~ $s ]]
|
||||
then
|
||||
# echo "[ERROR] character $s is prohibited due to sed usage" >&2
|
||||
from=${from//$s/\\$s}
|
||||
fi
|
||||
if [[ $from =~ \[ ]]
|
||||
then
|
||||
from=${from//\[/\\\[}
|
||||
fi
|
||||
if [[ $from =~ \* ]]
|
||||
then
|
||||
from=${from//\*/\\\*}
|
||||
fi
|
||||
if [[ $from =~ ^(.*)\$$ ]]
|
||||
then
|
||||
from=${BASH_REMATCH[1]}'\$'
|
||||
fi
|
||||
if [[ $from =~ ^\^(.*)$ ]]
|
||||
then
|
||||
from='\^'${BASH_REMATCH[1]}
|
||||
fi
|
||||
|
||||
if [[ $to =~ [\\] ]]
|
||||
then
|
||||
# escape char \ should be doubled
|
||||
to=${to//\\/\\\\}
|
||||
fi
|
||||
if [[ $to =~ $s ]]
|
||||
then
|
||||
# echo "[ERROR] character $s is prohibited due to sed usage" >&2
|
||||
# echo "This is a limitation of metascript.sh script, replaced by \$s" >&2
|
||||
to=${to//$s/\\$s}
|
||||
fi
|
||||
if [[ $to =~ [\&] ]]
|
||||
then
|
||||
# echo "[ERROR] character & is prohibited due to sed usage" >&2
|
||||
to=${to//\&/\\\&}
|
||||
fi
|
||||
|
||||
# replace it globaly
|
||||
echo "s$s$from$s$to${s}g"
|
||||
|
||||
}
|
||||
|
||||
sedreplacefromto()
|
||||
{
|
||||
local from="$1"
|
||||
local to="$2"
|
||||
shift 2
|
||||
local sedexpr="$1"
|
||||
execredirectto $to sed "$sedexpr" $from
|
||||
shift
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
sedexpr="$1"
|
||||
$defer sed -i "$sedexpr" $to
|
||||
shift
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
replacefromto()
|
||||
{
|
||||
local from="$1"
|
||||
local to="$2"
|
||||
shift 2
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
$defer "replace $@ from '$from' into '$to'"
|
||||
else
|
||||
local sedexpr=$(sed_substitute_expr "$1" "$2")
|
||||
execredirectto $to sed "$sedexpr" $from
|
||||
shift 2
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
sedexpr=$(sed_substitute_expr "$1" "$2")
|
||||
$defer sed -i "$sedexpr" $to
|
||||
shift 2
|
||||
done
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
sedreplacein()
|
||||
{
|
||||
local file=$1
|
||||
shift
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
$defer sed -i "$1" $file
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
replacein()
|
||||
{
|
||||
local infile=$1
|
||||
shift
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
$defer "replace $@ into '$infile'"
|
||||
else
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
sedexpr=$(sed_substitute_expr "$1" "$2")
|
||||
$defer sed -i "$sedexpr" $infile
|
||||
shift 2
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
parsemetaarg()
|
||||
{
|
||||
case $1 in
|
||||
@@ -355,8 +449,15 @@ parsemetaarg()
|
||||
dryrun|show|showdoc)
|
||||
defer=showdoc
|
||||
;;
|
||||
debug)
|
||||
set -ex
|
||||
;;
|
||||
metasudo=*)
|
||||
metasudo=${1/metasudo=}
|
||||
;;
|
||||
help|usage)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
toolsresourcesdir=*)
|
||||
toolsresourcesdir=${1/toolsresourcesdir=/}
|
||||
@@ -364,6 +465,9 @@ parsemetaarg()
|
||||
scl_enable=*)
|
||||
scl_args=(scl enable ${1/scl_enable=/} --)
|
||||
;;
|
||||
text_format=md)
|
||||
md_output_init
|
||||
;;
|
||||
*)
|
||||
log_error "unrecognized argument '$1'"
|
||||
usage
|
||||
@@ -380,28 +484,82 @@ enforcearg()
|
||||
|
||||
if [[ -z $value ]]
|
||||
then
|
||||
log_error "{0} expect '$var' to be set ex $var=$default"
|
||||
usage
|
||||
exit 1
|
||||
log_error "{$0} expect '$var' to be set ex $var=$default"
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
[[ -z $default ]] && default="DEFAULT"
|
||||
log_warn "in defer/dryrun force $var=$default"
|
||||
eval "$var=$default"
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
enforce()
|
||||
{
|
||||
objecttype=$1
|
||||
shift
|
||||
case $objecttype in
|
||||
file|dir)
|
||||
object=$1
|
||||
shift
|
||||
constraint=$1
|
||||
case $constraint in
|
||||
exists|does_not_exist|create_if_needed|well_named)
|
||||
enforce${objecttype} "$object" "$@"
|
||||
;;
|
||||
*)
|
||||
log_error "enforce $objecttype '$object' $@"
|
||||
log_fatal "constraint '$constraint' unsupported. currently known (exists|does_not_exist|create_if_needed) Please fix the code."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
var|arg)
|
||||
enforcearg "$@"
|
||||
;;
|
||||
user)
|
||||
enforceuser "$@"
|
||||
;;
|
||||
*)
|
||||
log_error "enforce $objecttype $@"
|
||||
log_fatal "objectttype $objecttype unsupported"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
enforcefile()
|
||||
{
|
||||
file="$1"
|
||||
constraint="$2"
|
||||
local file="$1"
|
||||
local constraint="$2"
|
||||
|
||||
if [[ $constraint == well_named ]]
|
||||
then
|
||||
if [[ "$file" =~ /$ ]]
|
||||
then
|
||||
log_error "'$file' as a file should not end with a '/'"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "$file" ]]
|
||||
then
|
||||
if [[ $constraint = exists ]]
|
||||
then
|
||||
log_error "[ERROR] Missing expected $file"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
case $constraint in
|
||||
exists)
|
||||
log_error "Missing expected $file"
|
||||
[[ -n $defer ]] || exit 1
|
||||
;;
|
||||
create_if_needed)
|
||||
$metarun touch $file
|
||||
;;
|
||||
esac
|
||||
else
|
||||
if [[ $constraint = does_not_exists ]]
|
||||
if [[ $constraint = does_not_exist ]]
|
||||
then
|
||||
log_error "[ERROR] '$file' already exists. Move it away"
|
||||
log_error "'$file' already exists. Move it away"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
fi
|
||||
@@ -409,33 +567,53 @@ enforcefile()
|
||||
|
||||
enforcedir()
|
||||
{
|
||||
dir="$1"
|
||||
constraint="$2"
|
||||
local dir="$1"
|
||||
local constraint="$2"
|
||||
|
||||
if [[ $constraint = does_not_exist ]]
|
||||
if [[ $constraint == well_named ]]
|
||||
then
|
||||
if [[ -e $dir ]]
|
||||
then
|
||||
log_error "'$dir' already exists. Move it away"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
if [[ ! "$dir" =~ /$ ]]
|
||||
then
|
||||
log_error "'$dir' should end with a '/'"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $constraint = exists ]]
|
||||
if [[ -e $dir ]]
|
||||
then
|
||||
if [[ ! -d "$dir" ]]
|
||||
then
|
||||
if [[ -e "$dir" ]]
|
||||
then
|
||||
log_error "'$dir' already exists but is not a directory as expected"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
log_error "[ERROR] Missing expected directory '$dir'"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
|
||||
case $constraint in
|
||||
does_not_exist)
|
||||
if [[ -d "$dir" ]]
|
||||
then
|
||||
log_error "'$dir' already exists"
|
||||
[[ -n $defer ]] || exit 1
|
||||
else
|
||||
log_error "'$dir' already exists and is not a directory as expected"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
;;
|
||||
create_if_needed|exists)
|
||||
if [[ ! -d "$dir" ]]
|
||||
then
|
||||
log_error "'$dir' already exists and is not a directory as expected"
|
||||
[[ -n $defer ]] || exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
case $constraint in
|
||||
exists)
|
||||
log_error "Missing expected directory $dir"
|
||||
[[ -n $defer ]] || exit 1
|
||||
;;
|
||||
create_if_needed)
|
||||
if [[ ! -d "$dir" ]]
|
||||
then
|
||||
$metarun mkdir -p "$dir"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
applymetaargs()
|
||||
@@ -528,7 +706,7 @@ check_variable_in()
|
||||
shift 2
|
||||
|
||||
local value=""
|
||||
local values=$@
|
||||
local values="$@"
|
||||
|
||||
eval value='$'"$var"
|
||||
|
||||
@@ -570,17 +748,222 @@ get_timestamp_second()
|
||||
echo "$(date +"%Y%m%d%H%M%S")"
|
||||
}
|
||||
|
||||
if [[ -z $ENV_METASCRIPT_RESOURCESDIR ]]
|
||||
todo()
|
||||
{
|
||||
log_any TODO "$@"
|
||||
}
|
||||
|
||||
get_resource_var() {
|
||||
local varname="$1"
|
||||
local default_value="$2"
|
||||
|
||||
eval value='$'"$varname"
|
||||
if [[ -z $value ]]
|
||||
then
|
||||
echo "# ($0:metascript.sh:$LINENO) $(date)" >>$collect_context
|
||||
if [[ -z $default_value ]]
|
||||
then
|
||||
log_error "resource $varname does not exists and no non empty default provided"
|
||||
echo "# $varname=<MISSING>" >>$collect_context
|
||||
exit 1
|
||||
fi
|
||||
log_warn "($0) Using default value '$varname'='$default_value' HARDCODED in script : should be fixed with proper default file resources.var, see traces in $collect_context"
|
||||
read "$varname" <<<"$default_value"
|
||||
echo "$varname=$default_value" >>$collect_context
|
||||
fi
|
||||
}
|
||||
|
||||
setup_resources_var()
|
||||
{
|
||||
local resource_var="$1"
|
||||
enforcefile "$resource_var" exists
|
||||
if [[ -f $resource_var ]]
|
||||
then
|
||||
while read line
|
||||
do
|
||||
if [[ $line =~ ^([a-zA-Z0-9_]+)=(.+)$ ]]
|
||||
then
|
||||
varname=${BASH_REMATCH[1]}
|
||||
value=${BASH_REMATCH[2]}
|
||||
read "$varname" <<<"$value"
|
||||
elif [[ $line =~ ^# ]]
|
||||
then
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
$defer : "$line"
|
||||
fi
|
||||
else
|
||||
log_warn "Invalid syntax in $resource_var '$line' does not match any expected expression"
|
||||
fi
|
||||
done <"$resource_var"
|
||||
fi
|
||||
}
|
||||
|
||||
# allow to check mounted points
|
||||
mountpoint_get_device()
|
||||
{
|
||||
local mount_point="$1"
|
||||
mount_point=$(readlink -f "$mount_point")
|
||||
$defer awk "{ if (\$2 == \""$mount_point"\") print \$1 ;}" /proc/mounts
|
||||
}
|
||||
|
||||
enforce_device() {
|
||||
local device=$1
|
||||
local constraint=$2
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
$defer "mountpoint=$(device_get_mountpoints $device)"
|
||||
else
|
||||
mountpoint=$(device_get_mountpoints $device)
|
||||
fi
|
||||
if [[ -n $mountpoint ]] && [[ $constraint == "unmounted" ]]
|
||||
then
|
||||
log_error "$device seen as mounted on $mountpoint"
|
||||
$defer exit 1
|
||||
elif [[ -z $mountpoint ]] && [[ $constraint == "mounted" ]]
|
||||
then
|
||||
log_error "$device not seen as mounted"
|
||||
$defer exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
device_get_mountpoints()
|
||||
{
|
||||
local device="$1"
|
||||
$defer awk "{ if (\$1 == \""$device"\") print \$2 ;}" /proc/mounts
|
||||
}
|
||||
|
||||
mount_if_needed()
|
||||
{
|
||||
local mountdevice="$1"
|
||||
local mountpoint="$2"
|
||||
local param="$3"
|
||||
if [[ -n $defer ]]
|
||||
then
|
||||
$defer 'device=$(mountpoint_get_device '"$mountpoint"')'
|
||||
else
|
||||
device=$(mountpoint_get_device "$mountpoint")
|
||||
fi
|
||||
|
||||
enforcedir "$mountpoint" exists
|
||||
# device is a device ...
|
||||
# enforcefile "$mountdevice" exists
|
||||
|
||||
if [[ -z $device ]]
|
||||
then
|
||||
$metarun mount $param "$mountdevice" "$mountpoint"
|
||||
elif [[ "$device" == "$mountdevice" ]]
|
||||
then
|
||||
log_info "$device already mounted on $mountdevice"
|
||||
else
|
||||
log_warn "Another device $device is mounted on $mountpoint, not $mountdevice"
|
||||
fi
|
||||
}
|
||||
|
||||
umount_if_needed()
|
||||
{
|
||||
local mountdevice="$1"
|
||||
local mountpoint="$2"
|
||||
local param="$3"
|
||||
device=$(mountpoint_get_device "$mountpoint")
|
||||
if [[ -n "$device" ]]
|
||||
then
|
||||
if [[ "$device" == "$mountdevice" ]]
|
||||
then
|
||||
$metarun umount "$mountpoint"
|
||||
else
|
||||
log_warn "Another device $device is mounted on $mountpoint, not $mountdevice"
|
||||
if [[ $param == '--bind' ]]
|
||||
then
|
||||
log_warn "Unmouning ANYWAY ( mount --bind show root device, not mounted directory )"
|
||||
$metarun umount "$mountpoint"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warn "no device found mounted for umount_if_needed $@"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_bg()
|
||||
{
|
||||
if [[ -z $defer ]]
|
||||
then
|
||||
"$@" &
|
||||
else
|
||||
$defer "$@" '&'
|
||||
fi
|
||||
}
|
||||
|
||||
notice()
|
||||
{
|
||||
case "$1" in
|
||||
git_commit)
|
||||
log_warn "$1 should be $2"
|
||||
;;
|
||||
*)
|
||||
log_fatal "unrecognized notice '$1'"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
enforceuser()
|
||||
{
|
||||
local expecteduser="$1"
|
||||
|
||||
if [[ "$USER" != "$expecteduser" ]]
|
||||
then
|
||||
log_error "expected user $expecteduser is not current user $USER"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# collect all hardcoded values.
|
||||
mkdir -p ~/.artlog
|
||||
collect_context=~/.artlog/collect_context.var
|
||||
|
||||
if [[ -z $toolsresourcesdir ]]
|
||||
then
|
||||
toolsresourcesdir=$toolsparentdir
|
||||
else
|
||||
toolsresourcesdir=$ENV_METASCRIPT_RESOURCESDIR
|
||||
# project directory
|
||||
|
||||
# from current $(pwd) will follow parent dir hierarchy to find resources.var
|
||||
dir="$(pwd)"
|
||||
while [[ -n $dir ]] && [[ -d $dir ]] && [[ ! -f $dir/resources.var ]]
|
||||
do
|
||||
new_dir=$(dirname "$dir")
|
||||
if [[ $new_dir == $dir ]]
|
||||
then
|
||||
# protect against infinite loop
|
||||
break
|
||||
fi
|
||||
dir="$new_dir"
|
||||
done
|
||||
if [[ -f $dir/resources.var ]]
|
||||
then
|
||||
toolsresourcesdir=$dir
|
||||
else
|
||||
if [[ -z $ENV_METASCRIPT_RESOURCESDIR ]]
|
||||
then
|
||||
toolsresourcesdir=$toolsparentdir
|
||||
else
|
||||
toolsresourcesdir=$ENV_METASCRIPT_RESOURCESDIR
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# quick way to give scl patches to fill scl_arg array
|
||||
if [[ -f $toolsparentdir/.scl_env ]]
|
||||
resources_var=$toolsresourcesdir/resources.var
|
||||
if [[ -f $resources_var ]]
|
||||
then
|
||||
source $toolsparentdir/.scl_env
|
||||
setup_resources_var "$resources_var"
|
||||
else
|
||||
log_warn "No $resources_var found"
|
||||
fi
|
||||
|
||||
|
||||
# quick way to give scl patches to fill scl_arg array
|
||||
if [[ -f $toolsresourcesdir/.scl_env ]]
|
||||
then
|
||||
source $toolsresourcesdir/.scl_env
|
||||
fi
|
||||
|
||||
# empty defer means doit
|
||||
@@ -588,6 +971,11 @@ defer=$ENV_METASCRIPT_DEFER
|
||||
|
||||
showinfo=showinfo
|
||||
|
||||
allparms=$@
|
||||
allparms="$@"
|
||||
|
||||
applymetaargs=applymetaargs
|
||||
|
||||
metarun=$defer
|
||||
|
||||
# metascript included
|
||||
fi
|
||||
|
||||
4
nextcloud_devenv/logs.sh
Normal file
4
nextcloud_devenv/logs.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
nextcloud_storage=/var/wwww/http/data
|
||||
sudo -u www-data tail -f $nextcloud_storage/nextcloud.log
|
||||
@@ -15,10 +15,16 @@ log_info "See https://docs.nextcloud.com/server/latest/admin_manual/configuratio
|
||||
|
||||
source ./config_db_query.sh
|
||||
|
||||
# convert localhost of host into container virtual brdige hostname mapping
|
||||
# convert localhost of host into container virtual bridge hostname mapping
|
||||
#db_hostname=host.docker.internal
|
||||
#db_hostname=host.containers.internal
|
||||
# host loopback mapped address
|
||||
db_hostname=10.1.1.13
|
||||
# dbtype_nextcloud inherited from config_db_query
|
||||
|
||||
$defer ./run_occ.sh db:convert-type --password="$password" --port="$db_port" --all-apps pgsql "$username" "$db_hostname" "$database_name"
|
||||
$defer ./run_occ.sh db:convert-type --password="$password" --port="$db_port" --all-apps "$dbtype_nextcloud" "$username" "$db_hostname" "$database_name"
|
||||
|
||||
if [[ $dbtype_nextcloud == mysql ]]
|
||||
then
|
||||
$defer ./run_occ.sh config:system:set mysql.utf8mb4 --type boolean --value="true"
|
||||
fi
|
||||
|
||||
72
nextcloud_devenv/nextcloud-docker-dev.env
Normal file
72
nextcloud_devenv/nextcloud-docker-dev.env
Normal file
@@ -0,0 +1,72 @@
|
||||
COMPOSE_PROJECT_NAME=master
|
||||
|
||||
# Default protocol to use for Nextcloud and other containers
|
||||
# check the readme for details how to setup https
|
||||
PROTOCOL=https
|
||||
|
||||
# Paths
|
||||
REPO_PATH_SERVER=/home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/nextcloud_devenv/server
|
||||
|
||||
# Specify a path to apps which will be shared between all containers. Useful for apps that support multiple nextcloud versions
|
||||
# ADDITIONAL_APPS_PATH=/home/alice/nextcloud-docker-dev/workspace/server/apps-shared
|
||||
|
||||
# Stable releases root directory
|
||||
# STABLE_ROOT_PATH=/home/alice/nextcloud-docker-dev/workspace
|
||||
|
||||
# Install Nextcloud apps per default
|
||||
# NEXTCLOUD_AUTOINSTALL_APPS="viewer activity"
|
||||
# Retry enabling apps for a provided amount of time (can be useful when using the containers in CI)
|
||||
# NEXTCLOUD_AUTOINSTALL_APPS_WAIT_TIME=0
|
||||
|
||||
# Blackfire configuration
|
||||
# BLACKFIRE_CLIENT_ID=
|
||||
# BLACKFIRE_CLIENT_TOKEN=
|
||||
# BLACKFIRE_SERVER_ID=
|
||||
# BLACKFIRE_SERVER_TOKEN=
|
||||
|
||||
# By default the published ports are only accessible at 127.0.0.1 (your localhost).
|
||||
# Set this to '0.0.0.0' to make them accessible from your whole local network.
|
||||
# IP_BIND=127.0.0.1
|
||||
|
||||
# can be used to run separate setups besides each other
|
||||
# DOCKER_SUBNET=192.168.15.0/24
|
||||
# PORTBASE=815
|
||||
|
||||
|
||||
# Main dns names for ssl proxy
|
||||
# This can be used to append a custom domain name to the container names
|
||||
DOMAIN_SUFFIX=-dev.l0g.eu
|
||||
|
||||
# May be used to set the PHP version. Defaults to 7.2.
|
||||
# PHP_VERSION=71
|
||||
# PHP_VERSION=72
|
||||
# PHP_VERSION=73
|
||||
# PHP_VERSION=74
|
||||
# PHP_VERSION=80
|
||||
PHP_VERSION=81
|
||||
|
||||
# May be used to choose database (sqlite, pgsql, mysql)
|
||||
SQL=mysql
|
||||
|
||||
# The mode of the xdebuger extention. This can be a comma separated list of
|
||||
# the entries none, develop, debug, trace, and profile.
|
||||
PHP_XDEBUG_MODE=develop
|
||||
|
||||
# Docker socket location, use it when you run rootless Docker
|
||||
# Replace "1000" with the uid used by your Docker engine (default $(id -u))
|
||||
# DOCKER_SOCKET=/run/user/1000/docker.sock
|
||||
DOCKER_SOCKET=/var/run/podman/podman.sock
|
||||
|
||||
# Nextcloud AppAPI Docker Socket Proxy
|
||||
# ------------------------------------
|
||||
# NC_HAPROXY_PASSWORD=some_secure_password
|
||||
# BIND_ADDRESS=172.17.0.1
|
||||
# CERT_PATH=./data/ssl/app_api/app_api.pem
|
||||
# NETWORK_MODE=host
|
||||
# HAPROXY_PORT=2375
|
||||
# TIMEOUT_CONNECT=10s
|
||||
# TIMEOUT_CLIENT=30s
|
||||
# TIMEOUT_SERVER=30s
|
||||
# EX_APPS_NET=ipv4@localhost
|
||||
# EX_APPS_COUNT=50
|
||||
# ------------------------------------
|
||||
6
nextcloud_devenv/reset_polls.sh
Executable file
6
nextcloud_devenv/reset_polls.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
./get_dbcontent.sh
|
||||
./run_occ.sh polls:db:purge
|
||||
./run_occ.sh app:enable polls
|
||||
./run_occ.sh polls:db:rebuild
|
||||
7
nextcloud_devenv/runit.README.md
Normal file
7
nextcloud_devenv/runit.README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
See README.md Option 2
|
||||
|
||||
./runit.sh dryrun up
|
||||
./runit.sh dryrun down
|
||||
|
||||
Please remove dryrun to actualy do it.
|
||||
54
nextcloud_devenv/runit.sh
Executable file
54
nextcloud_devenv/runit.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 artlog@l0g.eu
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
toolsdir=lib
|
||||
pushd $toolsdir >/dev/null
|
||||
toolsdir=$(pwd)
|
||||
source metascript.sh
|
||||
popd >/dev/null
|
||||
|
||||
usage() {
|
||||
cat runit.README.md
|
||||
}
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
case "$1" in
|
||||
up|down)
|
||||
action=$1
|
||||
;;
|
||||
*)
|
||||
parsemetaarg "$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z $action ]]
|
||||
then
|
||||
log_warn "Missing arguments"
|
||||
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
metarun=metarun
|
||||
metasudo=sudo
|
||||
|
||||
enforcedir nextcloud-docker-dev exists
|
||||
|
||||
pushd nextcloud-docker-dev
|
||||
|
||||
if [[ $action = down ]]
|
||||
then
|
||||
$metarun podman-compose down
|
||||
fi
|
||||
|
||||
services=(nextcloud proxy database-mysql)
|
||||
|
||||
if [[ $action == up ]]
|
||||
then
|
||||
$metarun podman-compose --env-file $(pwd)/.env $action "${services[@]}"
|
||||
fi
|
||||
|
||||
popd
|
||||
@@ -20,4 +20,14 @@ GRANT ALL PRIVILEGES ON SCHEMA public TO $username;
|
||||
EOF
|
||||
} | pipeto sudo -u postgres psql -f -
|
||||
|
||||
|
||||
{
|
||||
cat <<EOF
|
||||
CREATE USER '$username'@'localhost' IDENTIFIED BY '$password';
|
||||
CREATE DATABASE IF NOT EXISTS ${database_name} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
GRANT ALL PRIVILEGES on ${database_name}.* to '$username'@'localhost';
|
||||
EOF
|
||||
} | pipeto sudo mariadb -uroot -p
|
||||
|
||||
|
||||
log_info "created database ${database_name} with user $username"
|
||||
|
||||
1
samples/0.json
Normal file
1
samples/0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"candidates": {"A": "A", "B": "B", "C": "C"}, "voters": {"count": 4}, "mentions": ["Tr\u00e8s Bien", "Bien", "Assez Bien", "Passable", "Insuffisant", "A Rejeter"], "votation": {"votes": [{"A": 0, "B": 1, "C": 2}, {"A": 0, "B": 1, "C": 2}, {"A": 0, "B": 1, "C": 2}, {"A": 0, "B": 1, "C": 2}]}}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"candidats":{
|
||||
"candidates":{
|
||||
"A":"albert",
|
||||
"B":"Beatrice",
|
||||
"C":"Chloé",
|
||||
@@ -9,8 +9,8 @@
|
||||
"G":"Gisèle",
|
||||
"H":"Hugo"
|
||||
},
|
||||
"votants":{
|
||||
"decompte":10
|
||||
"voters":{
|
||||
"count":10
|
||||
},
|
||||
"mentions":[
|
||||
"Très Bien",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"candidats":{
|
||||
"candidates":{
|
||||
"A":"albert",
|
||||
"B":"Beatrice",
|
||||
"C":"Chloé",
|
||||
@@ -9,8 +9,8 @@
|
||||
"G":"Gisèle",
|
||||
"H":"Hugo"
|
||||
},
|
||||
"votants":{
|
||||
"decompte":10
|
||||
"voters":{
|
||||
"count":10
|
||||
},
|
||||
"mentions":[
|
||||
"Très Bien",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"candidats":{
|
||||
"candidates":{
|
||||
"A":"albert",
|
||||
"B":"Beatrice",
|
||||
"C":"Chloé",
|
||||
@@ -9,8 +9,8 @@
|
||||
"G":"Gisèle",
|
||||
"H":"Hugo"
|
||||
},
|
||||
"votants":{
|
||||
"decompte":11
|
||||
"voters":{
|
||||
"count":11
|
||||
},
|
||||
"mentions":[
|
||||
"Très Bien",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"candidats":{
|
||||
"candidates":{
|
||||
"A":"albert",
|
||||
"B":"Beatrice",
|
||||
"C":"Chloé",
|
||||
@@ -9,8 +9,8 @@
|
||||
"G":"Gisèle",
|
||||
"H":"Hugo"
|
||||
},
|
||||
"votants":{
|
||||
"decompte":11
|
||||
"voters":{
|
||||
"count":11
|
||||
},
|
||||
"mentions":[
|
||||
"Très Bien",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"candidats":{
|
||||
"candidates":{
|
||||
"A":"albert",
|
||||
"B":"Beatrice",
|
||||
"C":"Chloé",
|
||||
@@ -9,8 +9,8 @@
|
||||
"G":"Gisèle",
|
||||
"H":"Hugo"
|
||||
},
|
||||
"votants":{
|
||||
"decompte":11
|
||||
"voters":{
|
||||
"count":11
|
||||
},
|
||||
"mentions":[
|
||||
"Très Bien",
|
||||
|
||||
@@ -1,25 +1,53 @@
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 artlog@l0g.eu
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
|
||||
if [[ $# > 0 ]]
|
||||
then
|
||||
vote=$1
|
||||
fi
|
||||
toolsdir=../lib
|
||||
pushd $toolsdir >/dev/null
|
||||
toolsdir=$(pwd)
|
||||
source metascript.sh
|
||||
popd >/dev/null
|
||||
|
||||
if [[ -z $vote ]]
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
case "$1" in
|
||||
*.json)
|
||||
json_file=$1
|
||||
;;
|
||||
*)
|
||||
if [[ $1 =~ [0-9]+ ]]
|
||||
then
|
||||
vote=$1
|
||||
else
|
||||
parsemetaarg "$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z $vote ]] && [[ -z $json_file ]]
|
||||
then
|
||||
echo "[ERROR] numéro de vote manquant ( ex 1 pour 1.json )"
|
||||
log_error "numéro de vote manquant ( ex 1 pour 1.json )"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
json_file=$vote.json
|
||||
json=~/clients/artlog/artisanlogiciel.code/artlog_jsontools/build/json
|
||||
|
||||
if [[ -z $json_file ]]
|
||||
then
|
||||
json_file=$vote.json
|
||||
fi
|
||||
|
||||
enforcefile $json_file exists
|
||||
|
||||
json=json
|
||||
if [[ -x $json ]]
|
||||
then
|
||||
json_indent_file=$vote.indent.json
|
||||
echo "generating $json_indent_file"
|
||||
$json indent=spaces:2 -- $json_file > $json_indent_file
|
||||
json=$json_file
|
||||
json_file=$json_indent_file
|
||||
enforcefile $json_file exists
|
||||
fi
|
||||
|
||||
code_dir=../code/
|
||||
|
||||
16
samples/expected/6.stderr
Normal file
16
samples/expected/6.stderr
Normal file
@@ -0,0 +1,16 @@
|
||||
[WARN] No /.resources.var found
|
||||
Traceback (most recent call last):
|
||||
File "/home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/samples/../code/parse_sample.py", line 19, in <module>
|
||||
result = majority_judgment_run(poll)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/majority_judgment_method/__init__.py", line 39, in majority_judgment_run
|
||||
voters=poll['voters']['count']
|
||||
~~~~^^^^^^^^^^
|
||||
KeyError: 'voters'
|
||||
PHP Warning: Undefined array key "voters" in /home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/parse_sample.php on line 24
|
||||
PHP Warning: Trying to access array offset on null in /home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/parse_sample.php on line 24
|
||||
PHP Warning: Undefined array key "candidates" in /home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/parse_sample.php on line 25
|
||||
PHP Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in /home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/parse_sample.php:26
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/plhardy/clients/astrolabe/nextcloud/poll/jugement_majoritaire/code/parse_sample.php on line 26
|
||||
58
samples/mieuxvoter/cloture.indent.json
Normal file
58
samples/mieuxvoter/cloture.indent.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"ref":"zdslqbbzvk",
|
||||
"name":"Quel sont les mérites respectifs des participants",
|
||||
"description":"{\"description\":\"\",\"randomOrder\":true}",
|
||||
"candidates":[
|
||||
{
|
||||
"name":"A",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"id":61445
|
||||
},
|
||||
{
|
||||
"name":"B",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"id":61446
|
||||
},
|
||||
{
|
||||
"name":"C",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"id":61447
|
||||
}
|
||||
],
|
||||
"grades":[
|
||||
{
|
||||
"name":"Excellent",
|
||||
"value":4,
|
||||
"id":47253
|
||||
},
|
||||
{
|
||||
"name":"Très bien",
|
||||
"value":3,
|
||||
"id":47254
|
||||
},
|
||||
{
|
||||
"name":"Bien",
|
||||
"value":2,
|
||||
"id":47255
|
||||
},
|
||||
{
|
||||
"name":"Passable",
|
||||
"value":1,
|
||||
"id":47256
|
||||
},
|
||||
{
|
||||
"name":"Insuffisant",
|
||||
"value":0,
|
||||
"id":47257
|
||||
}
|
||||
],
|
||||
"num_voters":0,
|
||||
"hide_results":false,
|
||||
"force_close":true,
|
||||
"date_end":"2025-10-08T08:00:00Z",
|
||||
"auth_for_result":false,
|
||||
"restricted":false
|
||||
}
|
||||
1
samples/mieuxvoter/cloture.json
Normal file
1
samples/mieuxvoter/cloture.json
Normal file
@@ -0,0 +1 @@
|
||||
{"ref":"zdslqbbzvk","name":"Quel sont les mérites respectifs des participants","description":"{\"description\":\"\",\"randomOrder\":true}","candidates":[{"name":"A","description":"","image":"","id":61445},{"name":"B","description":"","image":"","id":61446},{"name":"C","description":"","image":"","id":61447}],"grades":[{"name":"Excellent","value":4,"id":47253},{"name":"Très bien","value":3,"id":47254},{"name":"Bien","value":2,"id":47255},{"name":"Passable","value":1,"id":47256},{"name":"Insuffisant","value":0,"id":47257}],"num_voters":0,"hide_results":false,"force_close":true,"date_end":"2025-10-08T08:00:00Z","auth_for_result":false,"restricted":false}
|
||||
72
samples/mieuxvoter/cloture.response.indent.json
Normal file
72
samples/mieuxvoter/cloture.response.indent.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"name":"Quel sont les mérites respectifs des participants",
|
||||
"description":"{\"description\":\"\",\"randomOrder\":true}",
|
||||
"ref":"zdslqbbzvk",
|
||||
"date_start":"2025-10-07T09:41:24.294727Z",
|
||||
"date_end":"2025-10-08T08:00:00Z",
|
||||
"hide_results":false,
|
||||
"restricted":false,
|
||||
"auth_for_result":false,
|
||||
"force_close":true,
|
||||
"grades":[
|
||||
{
|
||||
"name":"Excellent",
|
||||
"value":4,
|
||||
"description":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":47253
|
||||
},
|
||||
{
|
||||
"name":"Très bien",
|
||||
"value":3,
|
||||
"description":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":47254
|
||||
},
|
||||
{
|
||||
"name":"Bien",
|
||||
"value":2,
|
||||
"description":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":47255
|
||||
},
|
||||
{
|
||||
"name":"Passable",
|
||||
"value":1,
|
||||
"description":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":47256
|
||||
},
|
||||
{
|
||||
"name":"Insuffisant",
|
||||
"value":0,
|
||||
"description":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":47257
|
||||
}
|
||||
],
|
||||
"candidates":[
|
||||
{
|
||||
"name":"A",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":61445
|
||||
},
|
||||
{
|
||||
"name":"B",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":61446
|
||||
},
|
||||
{
|
||||
"name":"C",
|
||||
"description":"",
|
||||
"image":"",
|
||||
"election_ref":"zdslqbbzvk",
|
||||
"id":61447
|
||||
}
|
||||
],
|
||||
"invites":[]
|
||||
}
|
||||
2
samples/mieuxvoter/cloture.response.json
Normal file
2
samples/mieuxvoter/cloture.response.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{"name":"Quel sont les mérites respectifs des participants","description":"{\"description\":\"\",\"randomOrder\":true}","ref":"zdslqbbzvk","date_start":"2025-10-07T09:41:24.294727Z","date_end":"2025-10-08T08:00:00Z","hide_results":false,"restricted":false,"auth_for_result":false,"force_close":true,"grades":[{"name":"Excellent","value":4,"description":"","election_ref":"zdslqbbzvk","id":47253},{"name":"Très bien","value":3,"description":"","election_ref":"zdslqbbzvk","id":47254},{"name":"Bien","value":2,"description":"","election_ref":"zdslqbbzvk","id":47255},{"name":"Passable","value":1,"description":"","election_ref":"zdslqbbzvk","id":47256},{"name":"Insuffisant","value":0,"description":"","election_ref":"zdslqbbzvk","id":47257}],"candidates":[{"name":"A","description":"","image":"","election_ref":"zdslqbbzvk","id":61445},{"name":"B","description":"","image":"","election_ref":"zdslqbbzvk","id":61446},{"name":"C","description":"","image":"","election_ref":"zdslqbbzvk","id":61447}],"invites":[]}
|
||||
|
||||
4
samples/mieuxvoter/results-zds-lqb-bzvk.csv
Normal file
4
samples/mieuxvoter/results-zds-lqb-bzvk.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
"name","Excellent","Très bien","Bien","Passable","Insuffisant"
|
||||
"A","0","0","3","0","0"
|
||||
"B","0","1","1","1","0"
|
||||
"C","1","0","2","0","0"
|
||||
|
@@ -1,2 +1,5 @@
|
||||
Participants,Adresse e-mail,A,B,C
|
||||
admin,,Très Bien,Bien,Assez Bien
|
||||
admin,,Très Bien,Bien,Assez Bien
|
||||
user1,,Très Bien,Bien,Assez Bien
|
||||
user2,,Très Bien,Bien,Assez Bien
|
||||
user3,,Très Bien,Bien,Assez Bien
|
||||
|
1
samples/poll1.csv.json
Normal file
1
samples/poll1.csv.json
Normal file
@@ -0,0 +1 @@
|
||||
{"candidats": {"A": "A", "B": "B", "C": "C"}, "votants": {"decompte": 1}, "mentions": ["Tr\u00e8s Bien", "Bien", "Assez Bien", "Passable", "Insuffisant", "A Rejeter"], "votation": {"votes": [{"A": 0, "B": 1, "C": 2}]}}
|
||||
@@ -2,7 +2,20 @@
|
||||
|
||||
[[ -n $VIRTUAL_ENV ]] || source ../code/bin/activate
|
||||
|
||||
for (( i=0;i<7;i++ ))
|
||||
|
||||
expect_error()
|
||||
{
|
||||
sample=$1
|
||||
# error case on purpose
|
||||
resultdir=tmp
|
||||
[[ -d ${resultdir} ]] || mkdir ${resultdir}
|
||||
./check.sh $sample 2>${resultdir}/$sample.stderr
|
||||
diff ${resultdir}/$sample.stderr expected/$sample.stderr
|
||||
}
|
||||
|
||||
for (( i=0;i<6;i++ ))
|
||||
do
|
||||
./check.sh $i
|
||||
done
|
||||
|
||||
expect_error 6
|
||||
|
||||
Reference in New Issue
Block a user