Showing 8 changed files with 1281 additions and 0 deletions
+827
usr/local/bin/analyse-votes-AN
... ...
@@ -0,0 +1,827 @@
1
+#!/bin/bash
2
+
3
+set -e
4
+
5
+# on n'autorise qu'une seule exécution à la fois
6
+process_token=$(mktemp --dry-run /dev/shm/XXXXXXXXXXXXXXXX)
7
+token_file="$process_token.${0##*/}"
8
+
9
+for tool in sqlite3 getopt mktemp w3m jq; do
10
+    which $tool > /dev/null 2>&1 || {
11
+        echo missing tool $tool
12
+        exit 1
13
+    }
14
+done
15
+
16
+IFS_=$IFS
17
+
18
+function sqlite_request () {
19
+    sqlite3 ${2:+-cmd} ${2:+".mode $2"} "$in_ram_database" <<< "$1"
20
+}
21
+
22
+function create_database () {
23
+    sqlite_request "create table if not exists dossiers (id integer primary key, titre text, url text)"
24
+    sqlite_request "create table if not exists votes    (id integer primary key, nom text)"
25
+    sqlite_request "create table if not exists députés  (id integer primary key, nom text, groupe integer, date text)"
26
+    sqlite_request "create table if not exists groupes  (id integer primary key, nom text unique, nom_court text)"
27
+    sqlite_request "create table if not exists scrutins (num integer primary key, séance text, date text not null, intitulé text non null, adoption boolean, dossier integer, mise_au_point text)"
28
+    sqlite_request "create table if not exists dépouillements (scrutin integer not null, député integer not null, vote integer not null)"
29
+    sqlite_request "create unique index if not exists 'index_députés'        on députés (nom, groupe)"
30
+    sqlite_request "create unique index if not exists 'index_dossiers'       on dossiers (titre, url)"
31
+    sqlite_request "create unique index if not exists 'index_dépouillements' on dépouillements (député, scrutin)"
32
+
33
+    for v in Pour Contre Abstention Non-votant; do
34
+        sqlite_request "insert or ignore into votes (nom) values ('$v')"
35
+    done
36
+}
37
+
38
+function update_database () {
39
+    test "$no_db_update" = $true_flag && return
40
+    tempfile="/dev/shm/scrutin.$$"
41
+    progress=0
42
+    for r in "${!acronymes[@]}"; do
43
+        sqlite_request "update groupes set nom_court = \"${acronymes[$r]}\" where nom = \"$r\""
44
+    done
45
+    sqlite_request "create table if not exists dossier_par_scrutin (scrutin integer, url text)"
46
+    echo "récupération des dossiers"
47
+    wget -qO- "https://www.assemblee-nationale.fr/dyn/$mandature/dossiers" \
48
+    | sed -rn 's/<p class="m-0"><a title="Accéder au dossier législatif" href="([^"]+)">([^<]+)<.+$/\1 \2/p' \
49
+    | sed -r "s/^[[:space:]]*//; s/&#039;/'/g" \
50
+    | awk -v dq='"' '{
51
+        printf("insert or ignore into dossiers (titre, url) values (%s, %s);\n", dq gensub($1 " ", "", "1", $0) dq, dq "https://www.assemblee-nationale.fr" $1 dq)
52
+    }' > $tempfile
53
+    sqlite3 "$in_ram_database" < $tempfile
54
+    first_=$first
55
+    first=$(sqlite_request "select max(num) from scrutins")
56
+    if test ${first:-0} -lt $last; then
57
+        echo "récupération des scrutins n°$((${first:-0}+1)) à n°$last dans "$database" (à conserver autant que possible)" >&2
58
+
59
+        test $((last % 100)) -ne 0 && last_offset=0
60
+        IFS=$' \t\n'
61
+        for offset in $(seq $((last - 100)) -100 ${first:-0} ) $last_offset; do
62
+            wget -qO- "http://www2.assemblee-nationale.fr/scrutins/liste/(offset)/$offset/(legislature)/$mandature/(type)/TOUS/(idDossier)/TOUS" \
63
+                | awk -v dq='"' '
64
+                    BEGIN {
65
+                    }
66
+                    /<td class="denom">/ {
67
+                        scrutin = gensub(/^.+denom.>([[:digit:]]+)\\*?<.td./,"\\1","1",$0)
68
+                    }
69
+                    /<td class="desc">/ {
70
+                        if (match($0, ">dossier<") > 0)
71
+                            dossier[scrutin] = gensub(/^.+.<a href="([^"]+)">dossier<.a>.*$/,"\\1","1",$0)
72
+                    }
73
+                    END {
74
+                        for (i in dossier) {
75
+                            printf("insert into dossier_par_scrutin (scrutin, url) values (%i, %s);\n", i, dq dossier[i] dq)
76
+                        }
77
+                    }' > $tempfile
78
+            sqlite3 "$in_ram_database" < $tempfile
79
+        done
80
+
81
+
82
+#        IFS=$'\n'
83
+        begin=$(date +%s)
84
+        for scrutin in $(seq $((${first:-0}+1)) $last); do
85
+            w3m -cols 512 -dump "http://www2.assemblee-nationale.fr/scrutins/detail/(legislature)/$mandature/(num)/$scrutin" \
86
+            | sed -n '/^Analyse du scrutin n° /,/^Votes des groupes/{/^Navigation/,/^  • Non inscrits/d;/^[[:space:]]*$/d;p}' \
87
+            | awk -v sq="'" -v dq='"' '
88
+                BEGIN { adoption = -1; map = 0 }
89
+                /^Analyse du scrutin/ { scrutin = $NF }
90
+                /séance du [0-3][0-9]\/[01][0-9]\/(19|20)[0-9]+/ { date = $NF; seance = $1 }
91
+                /^Scrutin public sur /            { titre = gensub("^Scrutin public sur l[ae" sq "]s? ?", "", "1") }
92
+                /^L.Assemblée .+ adopté/          { adoption = NF == 3 }
93
+                /^Nombre de votants :/            { votants      = $NF }
94
+                /^Nombre de suffrages exprimés :/ { exprimes     = $NF }
95
+                /^Majorité absolue :/             { majo_absolue = $NF }
96
+                /^Pour l.adoption :/              { pour         = $NF }
97
+                /^Contre :/                       { contre       = $NF }
98
+                /^Groupe /                        { groupe = gensub("^Groupe (.+) \\([1-9].+$", "\\1", "1")
99
+                                                    groupe = gensub("^(la|les|le|l" sq "|du|des|de|de la|d" sq ") ", "", "1", groupe)
100
+                                                  }
101
+                /^Non inscrits/                   { groupe = "Non inscrits" }
102
+                /^(Pour|Abstention|Contre):/      { position = gensub(":", "", "1", $1) }
103
+                /^Non-votants?:/                  {
104
+                                                    position = gensub("s?:", "", "1", $1)
105
+                                                    nvl = ""
106
+                                                    while ($1 != "Groupe" || $0 != "Contenus annexes") {
107
+                                                        getline
108
+                                                        if ($1 == "Groupe" || $0 == "Contenus annexes")
109
+                                                            break
110
+                                                        nvl = nvl $0
111
+                                                    }
112
+                                                    f = split(nvl, nv, "(, | et )")
113
+                                                    for (i=1; i<=f; i++) {
114
+                                                        votes[groupe][position][gensub("(^ +|M\\. |Mme |Mlle | \\(.+)", "", "g", nv[i])]++
115
+                                                    }
116
+                                                    groupe = gensub("^Groupe (.+) \\([1-9].+$", "\\1", "1")
117
+                }
118
+                /^  • /                           { votes[groupe][position][gensub("^[^A-Z]*", "", "1")]++ }
119
+                /^Mises au point/,/^Votes des groupes/ { if ($1 != "(Sous") mises_au_point[map++] = $0 }
120
+                END {
121
+                    if (adoption < 0)
122
+                        adoption = pour >= majo_absolue
123
+
124
+                    for (i=1; i<map-1; i++)
125
+                        mise_au_point = sprintf("%s[%s]", mise_au_point, mises_au_point[i])
126
+
127
+                    printf("insert into scrutins (num, séance, date, intitulé, adoption, mise_au_point) values (%i, %s, %s, %s, %i, %s);\n",
128
+                            scrutin,
129
+                            sq seance sq,
130
+                            sq date sq,
131
+                            dq gensub(dq, dq dq, "g", titre) dq,
132
+                            adoption,
133
+                            dq gensub(dq, dq dq, "g", mise_au_point) dq,
134
+                            scrutin)
135
+                    printf("update scrutins set dossier = ( select id from dossiers inner join dossier_par_scrutin where dossiers.url = dossier_par_scrutin.url and dossier_par_scrutin.scrutin = %i) where num = %i;\n",
136
+                            scrutin,
137
+                            scrutin)
138
+                    for (groupe in votes) {
139
+                        printf("insert or ignore into groupes (nom) values (%s);\n", dq groupe dq)
140
+                        for (position in votes[groupe]) {
141
+                            for (nom in votes[groupe][position]) {
142
+                                if (nom !~ " \\(.+\\) *$")
143
+                                    printf("insert or ignore into députés (nom, groupe, date) select %s, id, %s from groupes where nom = %s;\n",
144
+                                            dq nom dq,
145
+                                            dq date dq,
146
+                                            dq groupe dq)
147
+                                printf("insert or ignore into dépouillements (scrutin, député, vote) select %i, députés.id, votes.id from députés inner join votes where députés.nom = %s and votes.nom = %s;\n",
148
+                                       scrutin,
149
+                                       dq nom dq,
150
+                                       dq position dq)
151
+                            }
152
+                        }
153
+                    }
154
+                }
155
+            ' > $tempfile
156
+            sqlite3 "$in_ram_database" < $tempfile
157
+
158
+
159
+            if test $(( ($scrutin - ${first:-0}) * 100 / ( $last - ${first:-0} ) )) -ne ${progress:-0}; then
160
+                progress=$(( ($scrutin - ${first:-0}) * 100 / ( $last - ${first:-0} ) ))
161
+                if test $(($progress % ${update_progress:-1})) -eq 0; then
162
+                    now=$(date +%s)
163
+                    delta=$(( $now - $begin ))
164
+#                   scrutin = {first:-0}+1 à la première itération
165
+                    printf "\r%d%%, ETA %s" $progress $(date +%H:%M:%S -d "$(($delta * ($last - $scrutin) / ($scrutin - ${first:-0}) )) seconds")
166
+                fi
167
+            fi
168
+        done
169
+        sqlite_request 'drop table dossier_par_scrutin'
170
+
171
+        echo -e "\r\033[KTerminé: $(($scrutin - ${first:-0} - 1)) scrutins ajoutés"
172
+        rm -f "$tempfile"
173
+    fi
174
+    first=$first_
175
+}
176
+
177
+function write_comparaison () {
178
+    result="scrutins ($(sum <<< "${groupe[@]}" | cut -b1-5))${dossier:+ - ${dossier}}"
179
+    if test "$envoi_par_mail" = $true_flag; then
180
+        result="scrutins"
181
+    fi
182
+    content="/dev/shm/$result/content.xml"
183
+    id_cols=(Scrutin Date Séance Titre Adoption Dossier)
184
+    declare -A style=(["Pour"]=Good ["Contre"]=Bad ["Abstention"]=Neutral ["oui"]=Good ["non"]=Bad)
185
+    eval $(sqlite_request 'select printf("typevotes[%i]=%s;", id, nom) from votes')
186
+    nb_cols=$(( ${#id_cols[@]} + ${#groupe[@]} ))
187
+    colors=($(awk -v n=${#groupe[@]} -v from=${from_color:-2A0636} -v to=${to_color:-D09B8A} '
188
+        function rgbL (p) {
189
+            r = rgb_from[1] + p * (rgb_to[1] - rgb_from[1])
190
+            g = rgb_from[2] + p * (rgb_to[2] - rgb_from[2])
191
+            b = rgb_from[3] + p * (rgb_to[3] - rgb_from[3])
192
+            L = r * 0.299 + g * 0.587 + b * 0.114
193
+            printf("%02x%02x%02x:%s\n", int(r), int(g), int(b), L > 185 ? "000000" : "ffffff")
194
+        }
195
+        BEGIN {
196
+            for (i = split(gensub("(..)(..)(..)", "\\1,\\2,\\3", "1", from), rgb_from, ","); i > 0; i--)
197
+                rgb_from[i] = strtonum(sprintf("%d", strtonum("0x" rgb_from[i])))
198
+            for (i = split(gensub("(..)(..)(..)", "\\1,\\2,\\3", "1", to), rgb_to, ","); i > 0; i--)
199
+                rgb_to[i] = strtonum(sprintf("%d", strtonum("0x" rgb_to[i])))
200
+
201
+            print "pour_bash_array_qui_commence_a_index_0"
202
+            rgbL(0)
203
+            for (i = 1; i < n-1; i++) {
204
+                rgbL(i/n)
205
+            }
206
+            if (n > 1) rgbL(1)
207
+        }
208
+    '))
209
+    function get_colname () {
210
+        awk -v n=$1 '
211
+            BEGIN{
212
+                printf("%c%c", n < 27 ? "" : int(n/26) + 64, (n % 26) + (n % 26 == 0 ? 26 : 0) + 64)
213
+            }' | tr -d '\0'
214
+    }
215
+    function write_cell () {
216
+        cell='<table:table-cell office:value-type='
217
+        case $1 in
218
+            url)
219
+                cell+='"string" calcext:value-type="string">'
220
+                cell+="<text:p><text:a xlink:href=$2 xlink:type=\"simple\">$3</text:a></text:p>"
221
+                ;;
222
+            texte)
223
+                cell+='"string" calcext:value-type="string"'${style[${2:- }]:+ table:style-name=\"${style[$2]}\"}'>'
224
+                cell+="<text:p>${2:- }</text:p>"
225
+                ;;
226
+            nombre)
227
+                cell+='"float" office:value="'$2'" calcext:value-type="float">'
228
+                cell+="<text:p>$2</text:p>"
229
+                ;;
230
+            formule)
231
+                cell+='"string" table:formula="of:='"$2"'" calcext:value-type="string"'${3:+ table:style-name=\"${3}\"}'>'
232
+                ;;
233
+            *)
234
+                return 1;;
235
+        esac
236
+        cell+='</table:table-cell>'
237
+        echo $cell >> "$content"
238
+    }
239
+
240
+    if test -z "$envoi_par_mail"; then
241
+        echo "génération du fichier $result"
242
+    fi
243
+
244
+    mkdir -p "/dev/shm/$result/META-INF"
245
+
246
+    cat > "/dev/shm/$result/META-INF/manifest.xml" << EOmetainf
247
+<?xml version="1.0" encoding="UTF-8"?>
248
+<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">
249
+ <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>
250
+ <manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
251
+ <manifest:file-entry manifest:full-path="settings.xml" manifest:media-type="text/xml"/>
252
+</manifest:manifest>
253
+EOmetainf
254
+
255
+    cat > "/dev/shm/$result/settings.xml" << EOsettings
256
+<?xml version="1.0" encoding="UTF-8"?>
257
+<office:document-settings xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.3">
258
+  <office:settings>
259
+    <config:config-item-set config:name="ooo:view-settings">
260
+      <config:config-item-map-indexed config:name="Views">
261
+        <config:config-item-map-entry>
262
+          <config:config-item config:name="ViewId" config:type="string">view1</config:config-item>
263
+          <config:config-item-map-named config:name="Tables">
264
+            <config:config-item-map-entry config:name="$result">
265
+              <config:config-item config:name="HorizontalSplitMode" config:type="short">2</config:config-item>
266
+              <config:config-item config:name="VerticalSplitMode" config:type="short">2</config:config-item>
267
+              <config:config-item config:name="HorizontalSplitPosition" config:type="int">${#id_cols[@]}</config:config-item>
268
+              <config:config-item config:name="VerticalSplitPosition" config:type="int">1</config:config-item>
269
+              <config:config-item config:name="ActiveSplitRange" config:type="short">1</config:config-item>
270
+              <config:config-item config:name="PositionLeft" config:type="int">0</config:config-item>
271
+              <config:config-item config:name="PositionRight" config:type="int">${#id_cols[@]}</config:config-item>
272
+              <config:config-item config:name="PositionTop" config:type="int">0</config:config-item>
273
+              <config:config-item config:name="PositionBottom" config:type="int">1</config:config-item>
274
+            </config:config-item-map-entry>
275
+          </config:config-item-map-named>
276
+        </config:config-item-map-entry>
277
+      </config:config-item-map-indexed>
278
+    </config:config-item-set>
279
+  </office:settings>
280
+</office:document-settings>
281
+EOsettings
282
+
283
+    printf 'application/vnd.oasis.opendocument.spreadsheet' > "/dev/shm/$result/mimetype"
284
+
285
+    echo '<?xml version="1.0" encoding="UTF-8"?>' > "$content"
286
+
287
+    cat >> "$content" << EOcontent
288
+    <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2">
289
+    <office:scripts/>
290
+    <office:font-face-decls>
291
+    <style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>
292
+    <style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
293
+    <style:font-face style:name="FreeSans" svg:font-family="FreeSans" style:font-family-generic="system" style:font-pitch="variable"/>
294
+    </office:font-face-decls>
295
+    <office:automatic-styles>
296
+EOcontent
297
+
298
+    IFS=$'\n'
299
+    for i in $(seq $nb_cols); do
300
+        cat >> "$content" << EOcontent
301
+            <style:style style:name="co$i" style:family="table-column">
302
+            <style:table-column-properties fo:break-before="auto" style:column-width="30.00mm"/>
303
+            </style:style>
304
+EOcontent
305
+    done
306
+
307
+    cat >> "$content" << EOcontent
308
+    <style:style style:name="ro1" style:family="table-row">
309
+    <style:table-row-properties style:row-height="4.52mm" fo:break-before="auto" style:use-optimal-row-height="true"/>
310
+    </style:style>
311
+    <style:style style:name="ta1" style:family="table" style:master-page-name="Default">
312
+    <style:table-properties table:display="true" style:writing-mode="lr-tb"/>
313
+    </style:style>
314
+    <style:style style:name="T1" style:family="text">
315
+    <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:color="#5eb91e"/>
316
+    </style:style>
317
+    <style:style style:name="T2" style:family="text">
318
+    <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:color="#c9211e"/>
319
+    </style:style>
320
+    <style:style style:name="T3" style:family="text">
321
+    <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:color="#e8a202"/>
322
+    </style:style>
323
+EOcontent
324
+
325
+    for i in $(seq ${#groupe[@]}); do
326
+        cat >> "$content" << EOcontent
327
+        <style:style style:name="groupe$i" style:family="table-cell" style:parent-style-name="Default">
328
+        <style:table-cell-properties fo:wrap-option="wrap" style:vertical-align="middle" fo:background-color="#${colors[$i]%:*}"/>
329
+        <style:text-properties fo:hyphenate="false" fo:color="#${colors[$i]}"/>
330
+        </style:style>
331
+EOcontent
332
+    done
333
+
334
+    cat >> "$content" << EOcontent
335
+    </office:automatic-styles>
336
+    <office:body>
337
+    <office:spreadsheet>
338
+    <table:calculation-settings table:automatic-find-labels="false"/>
339
+    <table:table table:name="$result" table:style-name="ta1">
340
+    <office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
341
+    <table:table-column table:style-name="co1" table:number-columns-repeated="${#id_cols[@]}" table:default-cell-style-name="Default"/>
342
+EOcontent
343
+
344
+    for g in $(seq ${#groupe[@]}); do
345
+        cat >> "$content" << EOcontent
346
+        <table:table-column table:style-name="co1" table:default-cell-style-name="ce$g"/>
347
+EOcontent
348
+    done
349
+    echo '<table:table-row table:style-name="ro1">' >> "$content"
350
+
351
+    IFS=$IFS_
352
+    for colonne in ${id_cols[@]}; do
353
+        write_cell texte $colonne
354
+    done
355
+
356
+    for (( g=0; g<${#groupe[@]}; g++ )); do
357
+        colname=$(get_colname $(( ${#id_cols[@]} + $g + 1)) )
358
+        write_cell formule "COM.MICROSOFT.CONCAT(&quot;${groupe[$g]} (&quot;; $(($last-$first+1))-COUNTIF([.${colname}2:.${colname}$(($last-$first+2))];&quot; &quot;); &quot;)&quot;)" groupe$(($g+1))
359
+    done
360
+
361
+    echo '</table:table-row>' >> "$content"
362
+
363
+    progress=0
364
+    begin=$(date +%s)
365
+    line=1
366
+    test -z "$seq" && qty=$(( $last - $first ))
367
+    IFS=$'\n'
368
+    scrutin_base_url="https://www2.assemblee-nationale.fr/scrutins/detail/(legislature)/$mandature/(num)/"
369
+    for scrutin in $(eval ${seq:-seq $first $last}); do
370
+
371
+        data=$(sqlite_request "select date,séance,intitulé,adoption,dossiers.url,dossiers.titre from scrutins left join dossiers on scrutins.dossier = dossiers.id where num is $scrutin" json)
372
+        date=$(jq -r '.[].date' <<< $data)
373
+        seance=$(jq -r '.[]."séance"' <<< $data)
374
+        title=$(jq -r '.[]."intitulé" | @html' <<< $data)
375
+        adoption=$(jq '.[].adoption' <<< $data)
376
+        dossier_url=$(jq '.[].url' <<< $data)
377
+        dossier_texte=$(jq -r '.[].titre | @html' <<< $data)
378
+        test $adoption -eq 1 && adoption='oui' || adoption='non'
379
+
380
+        echo '<table:table-row table:style-name="ro1">' >> "$content"
381
+
382
+        write_cell url   "\"$scrutin_base_url$scrutin\"" $scrutin
383
+        write_cell texte "$date"
384
+        write_cell texte "$seance"
385
+        write_cell texte "$title"
386
+        write_cell texte "$adoption"
387
+        write_cell url   "${dossier_url/#null/\"\"}" "${dossier_texte/#null}"
388
+
389
+        unset votes
390
+        for (( g = 0; g < ${#groupe[@]}; g++ )); do
391
+            case ${id_groupe[$g]%:*} in
392
+                groupes)
393
+                    unset _vote
394
+                    eval $(sqlite_request "select '_vote[' || vote || ']=' || count(vote) from dépouillements
395
+                                           where scrutin = $scrutin and député in (
396
+                                               select id from députés
397
+                                               where groupe in (${id_groupe[$g]#*:})
398
+                                           )
399
+                                           group by vote")
400
+                    if test ${#_vote[@]} -gt 0; then
401
+                        unset _votes
402
+                        for i in {1..3}; do
403
+                            _votes+="<text:span text:style-name=\"T$i\">${_vote[$i]:-0}</text:span> / "
404
+                        done
405
+                        _votes+="${_vote[4]:-0}"
406
+                        votes[${#votes[@]}]=$_votes
407
+                    else
408
+                        votes[${#votes[@]}]=" "
409
+                    fi
410
+                ;;
411
+                députés)
412
+                    votes[${#votes[@]}]=$(sqlite_request "select nom from votes
413
+                                                          where id in (
414
+                                                              select vote from dépouillements
415
+                                                              where scrutin = $scrutin and député in (${id_groupe[$g]#*:})
416
+                                                          )")
417
+                ;;
418
+            esac
419
+        done
420
+        for ((i = 0; i < ${#votes[@]}; i ++)); do
421
+            write_cell texte ${votes[$i]}
422
+        done
423
+        echo '</table:table-row>' >> "$content"
424
+
425
+        if test $(( ($line * 100) / ${qty:-$last} )) -ne $progress; then
426
+            progress=$(( ($line * 100) / ${qty:-$last} ))
427
+            if test $(( $progress % ${generation_progress:-5} )) -eq 0; then
428
+                now=$(date +%s)
429
+                delta=$(( $now - $begin ))
430
+                printf "\r%d%%, ETA %s" $progress $(date +%H:%M:%S -d "$(( $delta * (${qty:-$last} - $line) / $line )) seconds")
431
+            fi
432
+        fi
433
+
434
+        let line++
435
+
436
+    done
437
+
438
+    cat >> "$content" << EOcontent
439
+    </table:table>
440
+    <table:named-expressions/>
441
+    <table:database-ranges>
442
+    <table:database-range table:name="__Anonymous_Sheet_DB__0" table:target-range-address="&apos;$result&apos;.D1:&apos;$result&apos;.$(get_colname $nb_cols)$line" table:display-filter-buttons="true"/>
443
+    </table:database-ranges>
444
+    </office:spreadsheet>
445
+    </office:body>
446
+    </office:document-content>
447
+EOcontent
448
+
449
+    ( cd "/dev/shm/$result" && zip -r ../"$result" * > /dev/null 2>&1 && cd .. && rm -fr "$result" )
450
+
451
+    mv -f "/dev/shm/$result.zip" "${destination_path:+$destination_path/}$result.ods"
452
+
453
+    if test -z "$envoi_par_mail"; then
454
+        echo -e "\r\033[KTerminé : ${destination_path:+$destination_path/}$result.ods"
455
+    fi
456
+}
457
+
458
+function save_database () {
459
+    rm -f  "$token_file"
460
+    test -n "$result" -a -d "/dev/shm/$result" && rm -fr "/dev/shm/$result"
461
+    test -n "$database" -a -n "$in_ram_database" || return
462
+    if test "$envoi_par_mail" = $true_flag; then
463
+        if test -n "$mailconfig_file" && test -r "$mailconfig_file"; then
464
+            source "$mailconfig_file"
465
+        elif test -r "/usr/local/etc/${0##*/}.mail.conf"; then
466
+            source "/usr/local/etc/${0##*/}.mail.conf"
467
+        fi
468
+        stat -Lc "(date de mise à jour de la base: %x)" $database
469
+        cat > $process_token.headers << EOC
470
+From: ${from_mail:?}
471
+To: $destinataire
472
+Subject: les scrutins demandés
473
+EOC
474
+        curl_opt=(
475
+                --url smtp://${smtp_address:?}:${smtp_port:?}
476
+                --mail-rcpt $destinataire
477
+                -H @$process_token.headers
478
+                -F "=(;type=multipart/alternative"
479
+                -F "=<$process_token.txt;encoder=quoted-printable"
480
+                -F "=<$process_token.html;encoder=quoted-printable"
481
+                -F "=)"
482
+        )
483
+        if test -r "${destination_path:+$destination_path/}$result.ods"; then
484
+            curl_opt[${#curl_opt[@]}]="-F"
485
+            curl_opt[${#curl_opt[@]}]="=@${destination_path:+$destination_path/}$result.ods;encoder=base64"
486
+        fi
487
+        exec 1>&-
488
+        aha -f $process_token.mail -t "envoi automatisé" > $process_token.html
489
+        w3m -dump $process_token.html > $process_token.txt
490
+        curl ${curl_opt[@]}
491
+        rm -f "${destination_path:+$destination_path/}$result.ods" $process_token*
492
+    elif test -r "$database" && sqldiff=$(sqldiff $in_ram_database $database) && test -z "$sqldiff"; then
493
+        echo "pas de modification"
494
+    elif test -w "$database"; then
495
+        rm -f "$database"
496
+        sqlite_request '.dump' | sqlite3 "$database"
497
+        echo "base de données $database mise à jour"
498
+    elif test ! -e "$database" -a -w ${database%/*}; then
499
+        sqlite_request '.dump' | sqlite3 "$database"
500
+        echo "base de données $database créée"
501
+    else
502
+        echo "je ne peux rien faire avec $database !"
503
+    fi
504
+    rm -f "$in_ram_database" "$tempfile"
505
+}
506
+
507
+function dernier_scrutin_public () {
508
+    wget -qO- "http://www2.assemblee-nationale.fr/scrutins/liste/(legislature)/$mandature/(type)/TOUS/(idDossier)/TOUS" \
509
+            | sed -rn 's/^.*<td class="denom">([0-9]+)[^0-9].*$/\1/p' \
510
+            | head -1
511
+}
512
+
513
+trap save_database EXIT
514
+
515
+test -z "$database" && database="${0}.db"
516
+
517
+echo "$0 $@" > $token_file
518
+declare -A acronymes
519
+true_flag=$(mktemp --dry-run XXXXX)
520
+
521
+while [[ $# -gt 0 ]]; do
522
+    case "$1" in
523
+        "--no-db-update")
524
+#|ne met pas à jour la base de données
525
+            if test ${db_update_only:-OK} = $true_flag; then
526
+                echo "option incompatible avec --db-update-only"
527
+                exit 1
528
+            fi
529
+            no_db_update=$true_flag;;
530
+        "--db-update-only")
531
+#|ne génère pas de fichier de résultat
532
+            if test ${no_db_update:-OK} = $true_flag; then
533
+                echo "option incompatible avec --no-db-update"
534
+                exit 1
535
+            fi
536
+            db_update_only=$true_flag;;
537
+        "--cible"|"-c")
538
+#<nom court du groupe>|ajoute les scrutins de ce groupe, de ce ou cette députée, les colonnes seront dans l'ordre
539
+            _groupe[${#_groupe[@]}]="${2//\'/\'\'}"
540
+            shift;;
541
+        "--couleurs")
542
+#<nombre hexadécimal>:<nombre hexadécimal>|colore les colonnes en dégradé entre les deux couleurs comprises
543
+            if grep -iq '[^0-9A-F:]' <<< ${2:-ERROR}; then
544
+                echo "$1 ${2:-ERROR}: format attendu <nombre>:<nombre>"
545
+                exit 1
546
+            elif egrep -iq '[0-9A-F]{6}:[0-9A-F]{6}' <<< ${2:-ERROR}; then
547
+                from_color=${2%:*}
548
+                to_color=${2#*:}
549
+            else
550
+                echo erreur $2: couleur RGB au format hexadécimal demandé
551
+            fi
552
+            shift;;
553
+        "--mandature")
554
+           mandature="$2"
555
+           ;;
556
+        "--scrutin")
557
+#<nombre>[:<nombre>]|commence la génération du résultat pour le scrutin <nombre>, ou entre les deux nombres donnés
558
+            if grep -q '[^0-9:]' <<< ${2:-ERROR}; then
559
+                echo "$1 ${2:-ERROR}: format attendu <nombre>[:<nombre>]"
560
+                exit 1
561
+            elif egrep -q '[1-9][0-9]*(:[1-9][0-9]*)?' <<< ${2:-ERROR}; then
562
+                first=${2%:*}
563
+                last=${2#*:}
564
+                if test $first -gt $last; then
565
+                    last+=:$first
566
+                    first=${last%:*}
567
+                    last=${last#*:}
568
+                fi
569
+            else
570
+                echo "$1 ${2:-ERROR}: <nombre> ne doit pas commencer par 0"
571
+                exit 1
572
+            fi
573
+            shift;;
574
+        "--premier-scrutin")
575
+#<numéro>|commence la génération du résultat à partir du scrutin <numéro>
576
+            first="$2"
577
+            shift;;
578
+        "--dernier-scrutin")
579
+#<numéro>|termine la génération du résultat au scrutin <numéro>
580
+            last="$2"
581
+            shift;;
582
+        "--période")
583
+#<jj/mm/aaaa:JJ/MM/AAAA>|génère un résultat pour les scrutins allant de jj/mm/aaaa à JJ/MM/AAAA
584
+            periode=$true_flag
585
+            periode_value="$2"
586
+            shift;;
587
+        "--liste-députés-du-groupe")
588
+#<groupe>|liste les député·e·s du groupe <groupe>
589
+            liste_deputes=$true_flag
590
+            liste_deputes_value="${2}"
591
+            shift;;
592
+        "--liste-députés")
593
+#|liste tou-te-s les député-e-s de la mandature
594
+            liste_deputes=$true_flag;;
595
+        "--liste-dossiers")
596
+#|affiche une liste numérotée des dossiers et sort
597
+            liste_dossiers=$true_flag;;
598
+        "--dossier")
599
+#<numéro>|génère un résultat pour le dossier numéroté <numéro>
600
+            dossier=$true_flag
601
+            dossier_value="$2"
602
+            shift;;
603
+        "--dossiers")
604
+#|sélection interactive du dossier
605
+            dossier=$true_flag;;
606
+        "--conf")
607
+#<fichier>|indique le chemin vers le fichier de configuration. Par défaut "{_}.conf"
608
+            test -r "$2" || {
609
+                echo "config introuvable $2" >&2
610
+                options_error=$true_flag
611
+            }
612
+            config_file="$2"
613
+            shift;;
614
+        "--mailconf")
615
+#<fichier>|indique le chemin vers le fichier de configuration. Par défaut "{_}.conf"
616
+            test -r "$2" || {
617
+                echo "config introuvable $2" >&2
618
+                options_error=$true_flag
619
+            }
620
+            mailconfig_file="$2"
621
+            shift;;
622
+        "--dest")
623
+#<répertoire>|génère le fichier dans le répertoire spécifié. Par défaut $PWD
624
+            if test -n "$2" && test -d "$2" -a -r "$2"; then
625
+                destination_path="$2"
626
+                shift
627
+            else
628
+                echo "$2 n'est pas un répertoire ou n'est pas autorisé en écriture" >&2
629
+                exit 1
630
+            fi;;
631
+        "--database")
632
+#<fichier>|indique le chemin vers la base de données SQLite3 contenant les informations. Par défaut "{_}.db"
633
+            if test -r "$2" && file -Lb "$2" | grep -q '^SQLite 3.x database'; then
634
+                :
635
+            else
636
+                echo "erreur sur option database: fichier '$2' introuvable ou pas une base SQLite 3" >&2
637
+                options_error=$true_flag
638
+            fi
639
+            database="$2"
640
+            shift;;
641
+        "--progrès-génération")
642
+#<chiffre>|affiche de la progression de la génération du fichier tous les <chiffre>%. Par défaut 5
643
+            generation_progress="$2"
644
+            shift;;
645
+        "--progrès-update")
646
+#<chiffre>|affiche de la progression de la mise à jour de la base de données tous les <chiffre>%. Par défaut 1
647
+            update_progress="$2"
648
+            shift;;
649
+        "--mail")
650
+#<adresse mail>|envoie le fichier généré à l'adresse mail indiquée
651
+            envoi_par_mail=$true_flag
652
+            destinataire="$2"
653
+            no_db_update=$true_flag
654
+            destination_path=/dev/shm
655
+            generation_progress=1000
656
+            exec > $process_token.mail 2>&1
657
+            shift;;
658
+        "--help")
659
+#|affiche cette aide et quitte
660
+            echo "$0 [options]"
661
+            echo "génère un classeur ODS pour comparer les scrutins publics de la 16ème mandature à l'Assemblée Nationale"
662
+            echo
663
+            sed -rn '/^ *"--.+"\)/N; s/^ *"(--.+)"\)\n#(.+)$/\1|\2/p' "$0" \
664
+                | awk -F'|' -v marge='  ' -v prog="$0" '{
665
+                    printf("%s %s\n" marge "%s\n\n", $1, $2, gensub("\\. ", "\\\n" marge, "g", gensub("\\{_\\}", prog, "g", $3)))
666
+                }'
667
+            exit;;
668
+    esac
669
+    shift
670
+done
671
+
672
+test "$options_error" = $true_flag && exit 1
673
+
674
+if test -n "$config_file"; then
675
+    source "$config_file"
676
+else
677
+    config_file="${0}.conf"
678
+    if test -r "$config_file"; then
679
+        source "$config_file"
680
+    fi
681
+fi
682
+
683
+while true; do
684
+    if ls -1rt /dev/shm/*."${0##*/}" | head -1 | grep -q "^$token_file$"; then
685
+        # c'est notre tour
686
+        break
687
+    else
688
+        sleep 5
689
+    fi
690
+done
691
+
692
+in_ram_database=$process_token.db
693
+if test -r "$database"; then
694
+    cp "$database" "$in_ram_database"
695
+else
696
+    create_database
697
+fi
698
+
699
+if test "$periode" = $true_flag; then
700
+    function get_date () {
701
+        sqlite_request "select distinct(date) from scrutins order by num asc" | awk -v d="$1" -v comp=$2 '
702
+            function norm_date (date) {
703
+                split(date, a, "/")
704
+                return sprintf("%s%s%s",
705
+                    length(a[3]) == 4 ? a[3] : length(a[3]) == 2 ? "20" a[3] : strftime("%Y", systime()),
706
+                    length(a[2]) == 2 ? a[2] : "0" a[2],
707
+                    length(a[1]) == 2 ? a[1] : "0" a[1])
708
+            }
709
+            function output (date) {
710
+                print date
711
+                found = 1
712
+                exit
713
+            }
714
+            BEGIN { d = norm_date(d) }
715
+            {
716
+                s = norm_date($1)
717
+                if (NR == 1 && s > d && comp == "first") output($1)
718
+                if (s >= d && comp == "first") output($1)
719
+                if (s == d && comp == "last")  output($1)
720
+                if (s >  d && comp == "last")  output(previous)
721
+                previous = $1
722
+            }
723
+            END {
724
+                if (!found) print previous
725
+            }'
726
+    }
727
+    first=$(sqlite_request "select min(num) from scrutins where date = '$(get_date ${periode_value%:*} first)'")
728
+    last=$(sqlite_request "select max(num) from scrutins where date = '$(get_date ${periode_value#*:} last)'")
729
+    if test "$envoi_par_mail" = $true_flag; then
730
+        texte_periode="du $(get_date ${periode_value%:*} first) (scrutin n°$first) au $(get_date ${periode_value#*:} last) (scrutin n°$last)"
731
+    fi
732
+elif test "$dossier" != $true_flag -a "$no_db_update" = $true_flag; then
733
+    test -z "$last" && last=$(sqlite_request "select max(num) from scrutins")
734
+    test -z "$first" && first=1
735
+fi
736
+
737
+if test "$liste_dossiers" = $true_flag; then
738
+    if test "$envoi_par_mail" = $true_flag; then
739
+        echo "Voici la liste des dossiers actuellement à l'étude"
740
+    fi
741
+    sqlite_request "select printf('• %s (%s)', titre, url) from dossiers"
742
+    exit
743
+fi
744
+
745
+if test "$db_update_only" = $true_flag; then
746
+    unset first last
747
+    last=$(dernier_scrutin_public)
748
+    update_database
749
+    exit
750
+fi
751
+
752
+if test "$liste_deputes" = $true_flag; then
753
+    if test -n "$liste_deputes_value"; then
754
+        if test "$envoi_par_mail" = $true_flag; then
755
+            echo "Voici la liste des député·e·s du groupe dont le nom correspond au critère $liste_deputes_value"
756
+        fi
757
+        sqlite_request "select printf('%s - %s%s',
758
+                                      députés.nom,
759
+                                      groupes.nom,
760
+                                      iif(groupes.nom_court is not null, ' [' || groupes.nom_court || ']', ''))
761
+                        from députés
762
+                        inner join groupes on groupes.id = députés.groupe
763
+                        where
764
+                            groupes.nom like '%$liste_deputes_value%'
765
+                        or
766
+                            groupes.nom_court = '$liste_deputes_value'"
767
+    else
768
+        if test "$envoi_par_mail" = $true_flag; then
769
+            echo "Voici la liste des député·e·s"
770
+        fi
771
+        sqlite_request "select printf('%s - %s%s',
772
+                                      députés.nom,
773
+                                      groupes.nom,
774
+                                      iif(groupes.nom_court is not null, ' [' || groupes.nom_court || ']', ''))
775
+                        from députés
776
+                        inner join groupes on groupes.id = députés.groupe
777
+                        order by groupes.nom asc"
778
+    fi
779
+    exit
780
+fi
781
+
782
+for (( g = 0; g < ${#_groupe[@]}; g++ )); do
783
+    # on vérifie si c'est un ou une député
784
+    depute_count=$(sqlite_request "select count(distinct nom) from députés where nom like '%${_groupe[$g]}%'")
785
+    groupe_count=$(sqlite_request "select count(distinct nom) from groupes where nom like \"%${_groupe[$g]}%\" or nom_court is '${_groupe[$g]}'")
786
+    if test $depute_count -eq 1 -a $groupe_count -ne 1; then
787
+        groupe[$g]=$(sqlite_request "select distinct nom from députés where nom like '%${_groupe[$g]}%'")
788
+        id_groupe[$g]=députés:$(sqlite_request "select group_concat(id) from députés where nom is '${groupe[$g]//\'/\'\'}'")
789
+    elif test $groupe_count -eq 1 -a $depute_count -ne 1; then
790
+        groupe[$g]=$(sqlite_request "select distinct nom from groupes where nom like \"%${_groupe[$g]}%\" or nom_court is '${_groupe[$g]}'")
791
+        id_groupe[$g]=groupes:$(sqlite_request "select id from groupes where nom is '${groupe[$g]//\'/\'\'}'")
792
+    elif test $groupe_count -eq 1 -a $depute_count -eq 1; then
793
+        echo "dénomination ambigüe pour « ${_groupe[$g]} »"
794
+        sqlite_request "select printf('député·e: %s', distinct nom) from députés where nom like '%${_groupe[$g]}%'" | grep --color=always -i "${_groupe[$g]}"
795
+        sqlite_request "select printf('groupe  : %s', distinct nom) from groupes where nom like \"%${_groupe[$g]}%\" or nom_court is '${_groupe[$g]}'" | grep --color=always -i "${_groupe[$g]}"
796
+        echo
797
+    elif test $depute_count -gt 1; then
798
+        echo "plusieurs député·e·s trouvé·e·s correspondant à « ${_groupe[$g]} »"
799
+        sqlite_request "select distinct nom from députés where nom like '%${_groupe[$g]}%'" | grep --color=always -i "${_groupe[$g]}"
800
+        echo
801
+    elif test $groupe_count -gt 1; then
802
+        echo "plusieurs groupes trouvés correspondant à « ${_groupe[$g]} »"
803
+        sqlite_request "select distinct nom from groupes where nom like \"%${_groupe[$g]}%\" or nom_court is '${_groupe[$g]}'" | grep --color=always -i "${_groupe[$g]}"
804
+        echo
805
+    else
806
+        echo "aucun·e député·e ou groupe ne correspond au critère « ${_groupe[$g]} »"
807
+        echo
808
+    fi
809
+done
810
+
811
+if test -s $process_token.mail; then
812
+    exit 1
813
+fi
814
+
815
+update_database
816
+write_comparaison
817
+
818
+if test "$envoi_par_mail" = $true_flag; then
819
+    echo Vous pourrez trouver en pièce-jointe les résultats demandés avec ces critères:
820
+    if test ${#groupe[@]} -gt 0; then
821
+        echo "votes des groupes et député·e·s suivant·e·s:"
822
+        printf " • %s\n" "${groupe[@]}"
823
+    fi
824
+    if test "$periode" = $true_flag; then
825
+        echo sur la période allant $texte_periode
826
+    fi
827
+fi
+85
usr/local/bin/appels_manqués
... ...
@@ -0,0 +1,85 @@
1
+#!/usr/bin/env python3
2
+
3
+import requests
4
+import json
5
+import hmac
6
+import hashlib
7
+import re
8
+import sys
9
+import time
10
+import os
11
+import signal
12
+
13
+configfile = __file__ + ".conf"
14
+if not os.path.exists(configfile):
15
+    if os.path.exists("/usr/local/etc/" + os.path.basename(configfile)):
16
+        configfile = "/usr/local/etc/" + os.path.basename(configfile)
17
+    elif os.path.exists("/etc/" + os.path.basename(configfile)):
18
+        configfile = "/etc/" + os.path.basename(configfile)
19
+    else:
20
+        print("no config file found !")
21
+        exit(1)
22
+exec(open(configfile).read())
23
+
24
+def eprint(*args, **kwargs):
25
+    print(*args, file=sys.stderr, **kwargs)
26
+
27
+def stoppe(number, frame):
28
+    sys.exit(0)
29
+
30
+def connexion_post(method, data=None, headers={}):
31
+    url = FREEBOX_URL + "/api/" + API_VERSION + method + "/"
32
+    if data: data = json.dumps(data)
33
+    return json.loads(requests.post(url, data=data, headers=headers).text)
34
+
35
+def connexion_get(method, headers={}):
36
+    url = FREEBOX_URL + "/api/" + API_VERSION + method + "/"
37
+    return json.loads(requests.get(url, headers=headers).text)
38
+
39
+def connexion_put(method, data=None, headers={}):
40
+    url = FREEBOX_URL + "/api/" + API_VERSION + method + "/"
41
+    if data: data = json.dumps(data)
42
+    return json.loads(requests.put(url, data=data, headers=headers).text)
43
+
44
+def mksession():
45
+    challenge = connexion_get("/login")["result"]["challenge"]
46
+    data={
47
+        "app_id": APP_ID,
48
+        "password": hmac.new(APP_TOKEN, challenge.encode('utf-8'), hashlib.sha1).hexdigest()
49
+    }
50
+    return connexion_post("/login/session", data)["result"]["session_token"]
51
+
52
+def envoi_sms(number):
53
+    url="https://smsapi.free-mobile.fr/sendmsg?" \
54
+        + "user="  + user \
55
+        + "&pass=" + password \
56
+        + "&msg="  + re.sub(r' ', '%20', "sms pour " + number + ':' + msg)
57
+    requests.get(url)
58
+
59
+def marque_comme_lu(id_appel, number):
60
+    method = "/call/log/" + str(id_appel)
61
+    resultat = connexion_put(method, { "new": False }, headers={"X-Fbx-App-Auth": session_token})
62
+    if resultat.get('success'):
63
+        eprint("appel de " + number + "(id: " + str(id_appel) + ") marqué comme lu")
64
+    else:
65
+        eprint(resultat)
66
+
67
+def appels(session_token):
68
+    method = "/call/log"
69
+    resultat =  connexion_get(method, headers={"X-Fbx-App-Auth": session_token})
70
+    for appel in resultat["result"]:
71
+        if appel.get('new') and appel.get('type') == 'missed' and appel_depuis_portable.match(appel.get('number')):
72
+            envoi_sms(appel.get('number'))
73
+            marque_comme_lu(appel.get('id'), appel.get('number'))
74
+            time.sleep(5)
75
+
76
+signal.signal(signal.SIGTERM, stoppe)
77
+appel_depuis_portable = re.compile('^(0|\\+33)[67]')
78
+while True:
79
+    try:
80
+        session_token = mksession()
81
+    except:
82
+        time.sleep(10)
83
+    else:
84
+        appels(session_token)
85
+        time.sleep(60)
+14
usr/local/bin/hipi-energenie
... ...
@@ -0,0 +1,14 @@
1
+#!/usr/bin/perl 
2
+use strict;
3
+use warnings;
4
+use HiPi::Energenie::Command;
5
+
6
+our $VERSION ='0.81';
7
+
8
+my $result = HiPi::Energenie::Command->new->handle_command;
9
+
10
+print $result . qq(\n);
11
+
12
+1;
13
+
14
+__END__
+76
usr/local/bin/imprime
... ...
@@ -0,0 +1,76 @@
1
+#!/bin/bash
2
+
3
+couleur_default="Niveaux de gris"
4
+quantite_default=10
5
+warning_qty=250
6
+
7
+declare -A _type=([Couleur]=CMYK [$couleur_default]=Gray)
8
+declare -A _ecoprint=([Non]=Off [Oui]=On)
9
+declare -A _format=([A4]=PF500B [A3]=PF500A)
10
+
11
+get_size () {
12
+	eval $(pdfinfo "$f" | awk '/^Page size:/{if ($6 == "pts") printf("mm_x=%d;mm_y=%d", $3*0.352778, $5*0.352778)}')
13
+}
14
+set -x
15
+for f in "$@"; do
16
+	file_type=$(file -bn "$f")
17
+	if [[ $file_type =~ ^(Microsoft Word|OpenDocument Text) ]]; then
18
+            libreoffice --headless --convert-to pdf "/dev/shm/${f##*/}.pdf" "$f"
19
+	    f="/dev/shm/${f##*/}.pdf"
20
+	fi
21
+	file "$f" | grep -q ': PDF document, version' || continue
22
+	sides=$(pdfinfo "$f" | awk '/^Page size:/{print ($3 > $5) ? "short" : "long"}')
23
+	get_size
24
+	while IFS='|' read type format ecoprint qty massicote <<< "$(yad --title "${f##*/}" --form \
25
+		                                        --field='Type:CB' 'Couleur!^Niveaux de gris' \
26
+		                                        --field='Format:CB' 'A3!^A4' \
27
+							--field='EcoPrint:CB' 'Non!^Oui' \
28
+							--field='Quantité:NUM' '1!1..500!1' \
29
+							--field='Massicote-moi ça:CHK' 'FALSE' \
30
+							--field="\n\nStatut imprimante:\n$(/usr/local/bin/printer status):LBL")"; do
31
+		test -z "${qty}${type}" && exit
32
+		if test ${qty:-10} -gt $warning_qty; then
33
+			zenity --warning --text="Attention plus de $warning_qty impressions"
34
+			continue
35
+		fi
36
+		if [[ ! "${f##*/}" =~ ^massicotable- && "$massicote" =~ ^TRUE ]]; then
37
+			/usr/local/bin/massicot "$f"
38
+			f="${f%/*}/massicotable-${f##*/}"
39
+			sides="short"
40
+			get_size
41
+		fi
42
+		if test $format = A3; then
43
+			gs -o "/dev/shm/A3-${f##*/}" \
44
+				-sDEVICE=pdfwrite \
45
+				-sPAPERSIZE=a3 \
46
+				-dFIXEDMEDIA \
47
+				-dPDFFitPage \
48
+				-dCompatibilityLevel=1.5 \
49
+				"$f"
50
+			f="/dev/shm/A3-${f##*/}"
51
+		elif ! test "${mm_x}x${mm_y}" = "210x297" -o "${mm_x}x${mm_y}" = "297x210"; then
52
+			gs -o "/dev/shm/A4-${f##*/}" \
53
+				-sDEVICE=pdfwrite \
54
+				-sPAPERSIZE=a4 \
55
+				-dFIXEDMEDIA \
56
+				-dPDFFitPage \
57
+				-dCompatibilityLevel=1.5 \
58
+				"$f"
59
+			f="/dev/shm/A4-${f##*/}"
60
+		fi
61
+		lp -o InputSlot=${_format[$format]} \
62
+		   -o sides=two-sided-$sides-edge \
63
+		   -o ColorModel=${_type[${type}]} \
64
+		   -o KCEcoprint=${_ecoprint[${ecoprint}]} \
65
+		   -o KCCollate1=On \
66
+		   -n ${qty} \
67
+		   "$f"
68
+		if test $format = A3; then
69
+			rm -f "$f"
70
+		fi
71
+		if [[ $file_type =~ ^(Microsoft Word|OpenDocument Text) ]]; then
72
+			rm -f "$f"
73
+		fi
74
+		break
75
+	done
76
+done
+33
usr/local/bin/livret
... ...
@@ -0,0 +1,33 @@
1
+#!/bin/bash
2
+
3
+pdf="$1"
4
+size=$(pdfinfo "$pdf" | awk '/^Page size:/{
5
+	a = $3
6
+	b = $5
7
+	if (a >= 419  && a <= 421  && b >= 594  && b <= 596 ) print "a5"
8
+	if (a >= 594  && a <= 596  && b >= 419  && b <= 421 ) print "a5"
9
+	if (a >= 594  && a <= 596  && b >= 841  && b <= 843 ) print "a4"
10
+	if (a >= 841  && a <= 843  && b >= 594  && b <= 596 ) print "a4"
11
+	if (a >= 841  && a <= 843  && b >= 1189 && b <= 1191) print "a3"
12
+	if (a >= 1189 && a <= 1191 && b >= 841  && b <= 843 ) print "a3"
13
+}')
14
+pages=$(pdfinfo "$pdf" | awk '/Pages:/{print $2}')
15
+size=${size:-a4}
16
+test $pages -ne 4 -o -z "$size" && exit	
17
+
18
+recto=$(mktemp -d /dev/shm/XXXXXXXX)
19
+verso=$(mktemp -d /dev/shm/XXXXXXXX)
20
+output=$(mktemp --dry-run /dev/shm/XXXXXXXX.pdf)
21
+
22
+if test $size = "a3"; then
23
+    size="a4"
24
+    gs -o $output -sDEVICE=pdfwrite -sPAPERSIZE=$size -dFIXEDMEDIA -dPDFFitPage -dCompatibilityLevel=1.6 "$pdf"
25
+else
26
+    cp "$pdf" $output
27
+fi
28
+
29
+booksize="a$((${size//[^45]}-1))"
30
+pdfjam --paper ${booksize}paper --nup 2x1 --landscape --outfile $recto "$output" 4 "$output" 1
31
+pdfjam --paper ${booksize}paper --nup 2x1 --landscape --outfile $verso "$output" 2 "$output" 3
32
+pdftk A=$(find $recto -name "*pdf") B=$(find $verso -name "*pdf") cat A B output "${pdf%/*}/livret-${pdf##*/}"
33
+rm -fr $recto $verso $output
+32
usr/local/bin/massicot
... ...
@@ -0,0 +1,32 @@
1
+#!/bin/bash
2
+
3
+pdf="$1"
4
+size=$(pdfinfo "$pdf" | awk '/^Page size:/{
5
+	a = $3
6
+	b = $5
7
+	if (a >= 419  && a <= 421  && b >= 594  && b <= 596 ) print "a5"
8
+	if (a >= 594  && a <= 596  && b >= 419  && b <= 421 ) print "a5"
9
+	if (a >= 594  && a <= 596  && b >= 841  && b <= 843 ) print "a4"
10
+	if (a >= 841  && a <= 843  && b >= 594  && b <= 596 ) print "a4"
11
+	if (a >= 841  && a <= 843  && b >= 1189 && b <= 1191) print "a3"
12
+	if (a >= 1189 && a <= 1191 && b >= 841  && b <= 843 ) print "a3"
13
+}')
14
+pages=$(pdfinfo "$pdf" | awk '/Pages:/{print $2}')
15
+output=$(mktemp --dry-run /dev/shm/XXXXXXXX.pdf)
16
+recto=$(mktemp --directory /dev/shm/XXXXXXXX)
17
+verso=$(mktemp --directory /dev/shm/XXXXXXXX)
18
+
19
+if test "${size,,}" != "a5"; then
20
+    gs -o $output -sDEVICE=pdfwrite -sPAPERSIZE=a5 -dFIXEDMEDIA -dPDFFitPage -dCompatibilityLevel=1.6 "$pdf"
21
+else
22
+    cp "$pdf" $output
23
+fi
24
+pdfjam --paper a4paper --nup '2x1' --landscape --outfile $recto -- "$output" 1 "$output" 1
25
+if test $pages -eq 2; then
26
+    pdfjam --paper a4paper --nup '2x1' --landscape --outfile $verso -- "$output" 2 "$output" 2
27
+    pdftk A=$(find $recto -name "*pdf") B=$(find $verso -name "*pdf") cat A B output "$output"
28
+else
29
+    mv $(find $recto -name "*pdf") $output
30
+fi
31
+mv $output "${pdf%/*}/massicotable-${pdf##*/}"
32
+rm -fr $recto $verso $output
+119
usr/local/bin/monitoring
... ...
@@ -0,0 +1,119 @@
1
+#!/bin/bash
2
+
3
+basename=$(basename $0)
4
+monitoring_status="/dev/shm/${basename}${USER:+.$USER}.status"
5
+monitoring_disabled="/dev/shm/${basename}${USER:+.$USER}.disabled"
6
+monitoring_turns="/dev/shm/${basename}${USER:+.$USER}.turns"
7
+touch $monitoring_status $monitoring_disabled $monitoring_turns
8
+now=$(date +%s)
9
+
10
+function _err () {
11
+    test $# -eq 2 || return
12
+    err[${#err[@]}]="$1"
13
+    err[${#err[@]}]="$2%0a"
14
+}
15
+
16
+function _load () {
17
+    local conf=
18
+    local l=
19
+    local d=
20
+    local l_ok=
21
+    local count_loaded=
22
+    local msg=
23
+    local directories=(
24
+        "$HOME/.config/$basename"
25
+        "${0}.d"
26
+        "/usr/local/share/${basename}"
27
+    )
28
+    for conf in $*; do
29
+        count_loaded=0
30
+        if grep -q "^@$conf$" $monitoring_disabled; then
31
+            disabled[${#disabled[@]}]=$conf
32
+        else
33
+            for dir in ${directories[@]}; do
34
+                test -r "$dir/$conf" && source "$dir/$conf" $dir && let count_loaded++
35
+            done
36
+            test $count_loaded -gt 0 && loaded[${#loaded[@]}]=$conf
37
+        fi
38
+    done
39
+    if test ${#loaded[@]} -ne $#; then
40
+        for conf in $*; do
41
+            l_ok=0
42
+            for l in ${loaded[@]}; do
43
+                test $conf == $l && l_ok=1 && break
44
+            done
45
+            if test $l_ok -eq 0; then
46
+                for d in ${disabled[@]}; do
47
+                    test $conf == $d && msg+="$conf (disabled)%0a" && l_ok=1 && break
48
+                done
49
+            fi
50
+            test $l_ok -eq 0 && msg+="$conf%0a"
51
+        done
52
+        _err "_load" "config not loaded:%0a${msg/%%0a}"
53
+    fi
54
+}
55
+
56
+function _turn () {
57
+    test -n "$1" && [[ $1 =~ [0-9]+ ]] || return 1
58
+    echo ${FUNCNAME[1]} >> $monitoring_turns
59
+    test $(( $(grep -c ${FUNCNAME[1]} $monitoring_turns) % $1 )) -eq 0 && sed -i "/^${FUNCNAME[1]}$/d" $monitoring_turns
60
+}
61
+
62
+function _do_tests () {
63
+    local t=
64
+    for t in $(declare -f | sed -rn '/^[[:alpha:]]/s/^([[:alnum:]_]+)\s*\(\)\s*$/\1/p'); do
65
+        local msg=
66
+        if grep -q "^${t}$" $monitoring_disabled; then
67
+            echo supervision by $t disabled
68
+        else
69
+            msg=$( $t | sed 's/$/%0a/g' )
70
+            if test -n "$msg"; then
71
+                echo "problem: ${t}"
72
+                err[${#err[@]}]=$t
73
+                err[${#err[@]}]="${msg}"
74
+            else
75
+                if ! grep --quiet --word-regexp $t $monitoring_turns; then
76
+                    sed -ri "/^$t=.+$/d" $monitoring_status
77
+                fi
78
+            fi
79
+        fi
80
+    done
81
+}
82
+
83
+function _do_manage_errors () {
84
+    source $monitoring_status
85
+    local msg=
86
+    local t=
87
+    local errors=
88
+    local notification=0
89
+
90
+    if test ${#err[@]} -gt 0; then
91
+        errors="Supervision $HOSTNAME (${loaded[@]}):%0a"
92
+        for ((i=0; i<${#err[@]}; i++)); do
93
+            t=${err[i]}
94
+            msg=${err[++i]}
95
+            errors="${errors}"'*'" [${t}] ${msg}"
96
+            if test -n "${!t}"; then
97
+                if test ${!t} -lt $(( $now - 86400 )); then
98
+                    sed -ri "s/^(${t}=).+$/\1$now/" $monitoring_status
99
+                    let notification++
100
+                fi
101
+            else
102
+                echo "${t}=$now" >> $monitoring_status
103
+                let notification++
104
+            fi
105
+        done
106
+        errors=$(tr -d '\n' <<< $errors)
107
+        if test "$SHLVL" -eq 1; then
108
+            if test $notification -gt 0; then
109
+                $(dirname "$0")/smsapi "sms pour 0685421938:$errors"
110
+            fi
111
+        else
112
+            echo -e ${errors//\%0a/\\n}
113
+        fi
114
+    fi
115
+}
116
+
117
+_load ${*:-common-tests $HOSTNAME}
118
+_do_tests
119
+_do_manage_errors
+95
usr/local/bin/printer
... ...
@@ -0,0 +1,95 @@
1
+#!/bin/bash
2
+# http://www.oidview.com/mibs/0/Printer-MIB.html
3
+# http://www.ietf.org/rfc/rfc1759.txt
4
+
5
+printer=192.168.1.54
6
+
7
+# COULEURS
8
+echo Couleurs
9
+prtMarkerColorantValue=1.3.6.1.2.1.43.12.1.1.4.1
10
+prtMarkerSuppliesMaxCapacity=1.3.6.1.2.1.43.11.1.1.8.1
11
+prtMarkerSuppliesLevel=1.3.6.1.2.1.43.11.1.1.9.1
12
+for i in $(seq 4); do 
13
+    snmpget -v 1 -c public $printer \
14
+        ${prtMarkerColorantValue}.$i \
15
+        ${prtMarkerSuppliesLevel}.$i \
16
+        ${prtMarkerSuppliesMaxCapacity}.$i | awk -v dq='"' '
17
+        BEGIN{
18
+            i=0
19
+        }
20
+        {
21
+            a[i]=$NF
22
+            i++
23
+        }
24
+        END{
25
+            printf("%s: %d%%\n",gensub(dq, "", "g", a[0]),a[1]/a[2]*100)
26
+        }'
27
+done
28
+echo
29
+
30
+# PAPIER
31
+echo Papiers
32
+prtInputMaxCapacity=1.3.6.1.2.1.43.8.2.1.9.1
33
+prtInputCurrentLevel=1.3.6.1.2.1.43.8.2.1.10.1
34
+prtInputMediaDimFeedDirChosen=1.3.6.1.2.1.43.8.2.1.6.1
35
+prtInputMediaDimXFeedDirChosen=1.3.6.1.2.1.43.8.2.1.7.1
36
+for i in 2 3; do
37
+    snmpget -v 1 -c public $printer \
38
+        ${prtInputMaxCapacity}.$i \
39
+        ${prtInputCurrentLevel}.$i \
40
+        ${prtInputMediaDimFeedDirChosen}.$i \
41
+        ${prtInputMediaDimXFeedDirChosen}.$i | awk -v inputslot=$((i - 1)) '
42
+        BEGIN {
43
+            i = 0
44
+            format["82667 x 116900"] = "A4 portrait / A5 paysage"
45
+            format["116900 x 82667"] = "A4 paysage"
46
+            format["165333 x 117000"] = "A3 paysage"
47
+            is[1] = "A"
48
+            is[2] = "B"
49
+            is[3] = "C"
50
+            is[4] = "D"
51
+        }
52
+        { 
53
+            a[i] = $NF
54
+            i++
55
+        }
56
+        END {
57
+            size = a[2] " x " a[3]
58
+            f = format[size] ? format[size] : "format inconnu : " a[2] " x " a[3]
59
+            printf("bac %d (InputSlot=PF730%s): %d%% (%s)\n", inputslot, is[inputslot], a[1]/a[0]*100, f)
60
+        }
61
+    '
62
+done
63
+echo
64
+
65
+echo Status
66
+hrPrinterStatus=1.3.6.1.2.1.25.3.5.1.1.1
67
+snmpget -v 1 -c public $printer $hrPrinterStatus | awk -v sq="'" '
68
+    {
69
+        switch ($NF) {
70
+            case "3" :
71
+                status = "en attente"
72
+                break
73
+            case "4" :
74
+                status = "en cours d" sq "impression"
75
+                break
76
+            case "5" :
77
+                status = "préchauffage"
78
+                break
79
+            case "1" :
80
+            case "2" :
81
+            default :
82
+                status = "inconnu"
83
+                break
84
+        }
85
+        print status
86
+    }'
87
+echo
88
+
89
+prtAlertDescription=1.3.6.1.2.1.43.18.1.1.8
90
+snmpwalk -v 1 -c public $printer $prtAlertDescription | awk -F '"' '
91
+    {
92
+     	if ($2 != "") print $2   
93
+    }
94
+'
95
+