reorganize code before adding features

master
Y 2020-02-08 09:58:17 +01:00
parent 86887ac7a3
commit fc72f21005
1 changed files with 236 additions and 98 deletions

View File

@ -6,19 +6,218 @@
# This script only works when executed in the timezone of the calendar, and only if this timezone offset
# is low enough to keep all events in the day they belong to in the web calendar (usually 6h ≤ TZ ≤ +8h).
#
# from EPSI “edtmobilityeng” to ICS, for the current school year
# $1: EPSI Login (firstname.lastname)
# $2: output file (else standard output)
# From EPSI “edtmobilityeng” to ICS, for the current school year. Usage:
#
# epsiEDTtoICS.sh -u <user> [ -c [ <path> | - ] ] [ -j ]
# epsiEDTtoICS.sh -u <user> [ -p [ <path> | - ] [ -w <count> ] ] [ -j ]
# <user>: EPSI Login (firstname.lastname)
# <path>: Location where the ICS file is created (-c) or patched (-p)
# <count>: Number of weeks to update (including the current one), if -p is specified
#
# If no path is given, “-” is assumed, which is a placeholder for the standard output.
# If neither -c nor -p is given, “-c -” is assumed.
# If -p is used with “-” as a path, then only the updated weeks are sent to the standard output.
# If -p is given without -w, the default count of updated weeks is 5.
# If -j is used, logs are sent to systemd-journald instead of the standard error (needs systemd-cat).
if [ -n "$2" -a -d "$(dirname "$2")" -a -w "$(dirname "$2")" ]; then
exec >"$2"
fi
LANG8BIT=fr_FR@euro
LOGIN=
PATCH=
OUTPUT=-
MAXOFFSET=
JOURNAL=
readonly -a LEVELS=( emerg alert crit err warning notice info debug )
while getopts u:c:p:w:j arg; do case "$arg" in
u) LOGIN="$OPTARG" ;;
c) OUTPUT="${OPTARG:--}"; PATCH= ;;
p) OUTPUT="${OPTARG:--}"; PATCH=true ;;
w) MAXOFFSET="${OPTARG//[^0-9]}" ;;
j) JOURNAL=true ;;
esac; done
# $1: log level ∈ [0 … 7]
# $2: log text (“-” for STDIN)
function log() {
local l="$1"
shift
{
[ "$1" == - ] && cat || echo "$*"
} | {
[ -n "$JOURNAL" ] && systemd-cat -t epsi2ical -p ${LEVELS[$l]} || sed "s/^/[${LEVELS[$l]}] /" >&2
}
}
function check_parameters_and_set_defaults() {
if [ -z "$LOGIN" ]; then
log 2 'No user provided (option -u)'
exit 1
fi
if [ "$OUTPUT" != - ] && ! [ \
-n "$OUTPUT" -a \
-d "$(dirname "$OUTPUT")" -a \
-w "$(dirname "$OUTPUT")" -a \
"$(basename "$OUTPUT")" != . -a \
"$(basename "$OUTPUT")" != .. \
]; then
log 2 "The directory (option -c or -p) “$(dirname "$OUTPUT")” is not writeable, or the filename is invalid"
exit 2
fi
if [ -n "$JOURNAL" ] && ! which systemd-cat >/dev/null; then
JOURNAL=
log 4 "Command “systemd-cat” not found"
fi
MAXOFFSET=$((${MAXOFFSET:-5}*7))
}
function fold_long_lines() {
env LANG="$LANG8BIT" sed -r 's/(.{73})(.)/\1\n \2/g'
}
function lf_to_crlf() {
sed 's/$/\r/'
}
function write_output() {
[ "$OUTPUT" == - ] && cat || {
cat >"${OUTPUT}.tmp" && mv -f "${OUTPUT}.tmp" "$OUTPUT" || {
log 3 "Failed to write to: $OUTPUT"
exit 20
}
}
}
# $1: string to encode
function urlencode() {
local saveLANG="$LANG"
export LANG="$LANG8BIT"
local index char
for (( index=0; index<${#1}; index++ )); do
char=${1:$index:1}
case "$char" in
[-A-Za-z0-9._~]) printf '%s' "$char" ;;
*) printf '%%%02x' "'$char" ;;
esac
done
export LANG="$saveLANG"
}
# $1: date in format %m/%d/%Y
function fetch_html_week() {
local url="https://edtmobiliteng.wigorservices.net/WebPsDyn.aspx?action=posEDTBEECOME&serverid=C&Tel=$(urlencode "$LOGIN")&date=$1"
for ((i=1; i<5; i=i*2)); do
week="$(curl -s "$url")" && { echo "$week"; sleep 0.3s; return; } || log 7 "Request “${url}” failed"
sleep ${i}s
done
log 3 "Cannot get week calendar from upstream for Month/Day/Year: $1"
exit 10
}
# &0: html week (see: fetch_html_week)
# $1: offset (±) from today, to locate the week
# $2: timestamp for this particular ical generation
function html_week_to_ical_week() {
local offsetDayOfWeek=$(date +%u -d "${1}days") # ∈ [1=monday … 5=friday]
local mon=$(date +%Y%m%dT%z -d "$((${1}+1-$offsetDayOfWeek))days")
local tue=$(date +%Y%m%dT%z -d "$((${1}+2-$offsetDayOfWeek))days")
local wed=$(date +%Y%m%dT%z -d "$((${1}+3-$offsetDayOfWeek))days")
local thu=$(date +%Y%m%dT%z -d "$((${1}+4-$offsetDayOfWeek))days")
local fri=$(date +%Y%m%dT%z -d "$((${1}+5-$offsetDayOfWeek))days")
tr '\r\n' ' ' \
| sed -r 's/[[:blank:]]+/ /g' \
| grep -oE '<DIV [^>]*class="Case" [^>]*style="[^"]*left *:[^"]*"|DIV [^>]*style="[^"]*left *:[^"]*" [^>]*class="Case"|<td [^>]*class="TC(ase|Prof|hdeb|Salle)"([^<]|<[^/]|</[^t]|</t[^d])*</td>' \
| awk -vmon=$mon -vtue=$tue -vwed=$wed -vthu=$thu -vfri=$fri -vid="epsi2ics/${LOGIN}@${HOSTNAME}" -vdtstamp=$2 -F$'\t' '
function out() {
if (from!="" && to!="" && ase!="") printf( \
"BEGIN:VEVENT\nUID:%s/%s/%s\nDTSTAMP:%s\nDTSTART:%s\nDTEND:%s\nSUMMARY:%s\nLOCATION:%s\nDESCRIPTION:🗣 %s 👥 %s\nEND:VEVENT\n", \
from, id, prof, dtstamp, from, to, ase, where, prof, who)
from=""
to=""
ase=""
where=""
prof=""
who=""
day=""
zoneH=0
zoneM=0
}
function toZtime(localH, localM) {
localM-=zoneM
if (localM<0) {
localM+=60
localH-=1
} else if (localM>59) {
localM-=60
localH+=1
}
localH-=zoneH
return sprintf("%s%02d%02d00Z", day, localH, localM)
}
/class="Case"/ {
out()
pc=gensub(".*left *: *([0-9]+)[%.].*", "\\1", 1)
if (pc < 115) tmpd=mon
else if (pc < 135) tmpd=tue
else if (pc < 155) tmpd=wed
else if (pc < 175) tmpd=thu
else tmpd=fri
day=substr(tmpd, 1, 9)
zoneH=substr(tmpd, 10, 3)+0
zoneM=(substr(tmpd, 10, 1) substr(tmpd, 13, 2))+0
}
/class="TCase"/ {
ase=gensub(".*</div>(.*)</.*", "\\1", 1)
}
/class="TCProf"/ {
split(gensub(".*>(.*)<br/?>(.*)</.*", "\\1\t\\2", 1), tmp)
prof=tmp[1]
who=tmp[2]
}
/class="TChdeb"/ {
split(gensub(".*> *0?([0-9]+):0?([0-9]+) *- *0?([0-9]+):0?([0-9]+) *</.*", "\\1\t\\2\t\\3\t\\4", 1), tmp)
from=toZtime(tmp[1], tmp[2])
to=toZtime(tmp[3], tmp[4])
}
/class="TCSalle"/ {
where=gensub(".*>(Salle:)?(.*)</.*", "\\2", 1)
}
END {
out()
}'
}
function output_ical_header() {
cat <<-ENDOFTEXT
BEGIN:VCALENDAR
VERSION:2.0
PRODID:yalis.fr/epsi2ical v1.1
ENDOFTEXT
}
function output_ical_footer() {
cat <<-ENDOFTEXT
END:VCALENDAR
ENDOFTEXT
}
# $1: offset (±) from today, to locate the week
# $2: timestamp for this particular ical generation
function output_ical_week {
fetch_html_week $(date +%m/%d/%Y -d "${1}days") \
| html_week_to_ical_week "$1" "$2"
}
# &1: first offset of the school-year, and max offset of the same school-year, space-separated
function get_first_and_max_offset_of_year() {
local currentYear=$(date +%Y)
local splitDay=$(date +%j -d "${currentYear}-08-01" | sed 's/^0*//') # XXXX-08-01 ∈ [1 … 366]
local dayOfYear=$(date +%j | sed 's/^0*//') # this day ∈ [1 … 366]
local offset= offsetMax=
(
dtstamp=$(date -u +%Y%m%dT%H%M%SZ)
currentYear=$(date +%Y)
splitDay=$(date +%j -d "${currentYear}-08-01" | sed 's/^0*//') # XXXX-08-01 ∈ [1 … 366]
dayOfYear=$(date +%j | sed 's/^0*//') # this day ∈ [1 … 366]
if [ $dayOfYear -lt $splitDay ]; then
offsetMax=$(( splitDay-dayOfYear ))
offset=$(( offsetMax-365 ))
@ -26,94 +225,33 @@ fi
offset=$(( splitDay-dayOfYear ))
offsetMax=$(( 365+offset ))
fi
dayOfWeek=$(date +%u -d "${offset}days") # ∈ [1=monday … 5=friday]
cat <<-ENDOFTEXT
BEGIN:VCALENDAR
VERSION:2.0
PRODID:yalis.fr/epsi2ical v1.1
ENDOFTEXT
printf '%s %s\n' $offset $offsetMax
}
while [ $offset -lt $offsetMax ]; do
mon=$(date +%Y%m%dT%z -d "$(($offset+1-$dayOfWeek))days")
tue=$(date +%Y%m%dT%z -d "$(($offset+2-$dayOfWeek))days")
wed=$(date +%Y%m%dT%z -d "$(($offset+3-$dayOfWeek))days")
thu=$(date +%Y%m%dT%z -d "$(($offset+4-$dayOfWeek))days")
fri=$(date +%Y%m%dT%z -d "$(($offset+5-$dayOfWeek))days")
function main() {
check_parameters_and_set_defaults
{
local dtstamp=$(date -u +%Y%m%dT%H%M%SZ)
local offset= offsetMax=
for ((i=1; i<5; i=i*2)); do
week="$(curl -s "https://edtmobiliteng.wigorservices.net/WebPsDyn.aspx?action=posEDTBEECOME&serverid=C&Tel=${1}&date=$(date +%m/%d/%Y -d "${offset}days")")" \
&& { echo "$week"; break; }
sleep ${i}s
done \
| tr '\r\n' ' ' \
| sed -r 's/[[:blank:]]+/ /g' \
| grep -oE '<DIV [^>]*class="Case" [^>]*style="[^"]*left *:[^"]*"|DIV [^>]*style="[^"]*left *:[^"]*" [^>]*class="Case"|<td [^>]*class="TC(ase|Prof|hdeb|Salle)"([^<]|<[^/]|</[^t]|</t[^d])*</td>' \
| awk -vmon=$mon -vtue=$tue -vwed=$wed -vthu=$thu -vfri=$fri -vid="epsi2ics/${1}@${HOSTNAME}" -vdtstamp=$dtstamp -F$'\t' '
function out() {
if (from!="" && to!="" && ase!="") printf( \
"BEGIN:VEVENT\nUID:%s/%s/%s\nDTSTAMP:%s\nDTSTART:%s\nDTEND:%s\nSUMMARY:%s\nLOCATION:%s\nDESCRIPTION:🗣 %s 👥 %s\nEND:VEVENT\n", \
from, id, prof, dtstamp, from, to, ase, where, prof, who)
from=""
to=""
ase=""
where=""
prof=""
who=""
day=""
zoneH=0
zoneM=0
}
function toZtime(localH, localM) {
localM-=zoneM
if (localM<0) {
localM+=60
localH-=1
} else if (localM>59) {
localM-=60
localH+=1
}
localH-=zoneH
return sprintf("%s%02d%02d00Z", day, localH, localM)
}
/class="Case"/ {
out()
pc=gensub(".*left *: *([0-9]+)[%.].*", "\\1", 1)
if (pc < 115) tmpd=mon
else if (pc < 135) tmpd=tue
else if (pc < 155) tmpd=wed
else if (pc < 175) tmpd=thu
else tmpd=fri
day=substr(tmpd, 1, 9)
zoneH=substr(tmpd, 10, 3)+0
zoneM=(substr(tmpd, 10, 1) substr(tmpd, 13, 2))+0
}
/class="TCase"/ {
ase=gensub(".*</div>(.*)</.*", "\\1", 1)
}
/class="TCProf"/ {
split(gensub(".*>(.*)<br/?>(.*)</.*", "\\1\t\\2", 1), tmp)
prof=tmp[1]
who=tmp[2]
}
/class="TChdeb"/ {
split(gensub(".*> *0?([0-9]+):0?([0-9]+) *- *0?([0-9]+):0?([0-9]+) *</.*", "\\1\t\\2\t\\3\t\\4", 1), tmp)
from=toZtime(tmp[1], tmp[2])
to=toZtime(tmp[3], tmp[4])
}
/class="TCSalle"/ {
where=gensub(".*>(Salle:)?(.*)</.*", "\\2", 1)
}
END {
out()
}'
offset=$(($offset+7))
sleep 0.3s
done
read -r offset offsetMax < <(get_first_and_max_offset_of_year)
if [ -n "$PATCH" ]; then
offset=0
[ $MAXOFFSET -lt $offsetMax ] && offsetMax=$MAXOFFSET
fi
cat <<-ENDOFTEXT
END:VCALENDAR
ENDOFTEXT
) \
| env LANG=fr_FR@euro sed -r 's/(.{73})(.)/\1\n \2/g' \
| sed 's/$/\r/'
output_ical_header
while [ $offset -lt $offsetMax ]; do
output_ical_week $offset "$dtstamp"
offset=$(($offset+7))
done
output_ical_footer
} \
| fold_long_lines \
| lf_to_crlf \
| write_output
}
main