reorganize code before adding features
parent
86887ac7a3
commit
fc72f21005
334
epsiEDTtoICS.sh
334
epsiEDTtoICS.sh
|
@ -6,19 +6,218 @@
|
||||||
# This script only works when executed in the timezone of the calendar, and only if this timezone offset
|
# 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).
|
# 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
|
# From EPSI “edtmobilityeng” to ICS, for the current school year. Usage:
|
||||||
# $1: EPSI Login (firstname.lastname)
|
#
|
||||||
# $2: output file (else standard output)
|
# 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
|
LANG8BIT=fr_FR@euro
|
||||||
exec >"$2"
|
|
||||||
fi
|
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
|
if [ $dayOfYear -lt $splitDay ]; then
|
||||||
offsetMax=$(( splitDay-dayOfYear ))
|
offsetMax=$(( splitDay-dayOfYear ))
|
||||||
offset=$(( offsetMax-365 ))
|
offset=$(( offsetMax-365 ))
|
||||||
|
@ -26,94 +225,33 @@ fi
|
||||||
offset=$(( splitDay-dayOfYear ))
|
offset=$(( splitDay-dayOfYear ))
|
||||||
offsetMax=$(( 365+offset ))
|
offsetMax=$(( 365+offset ))
|
||||||
fi
|
fi
|
||||||
dayOfWeek=$(date +%u -d "${offset}days") # ∈ [1=monday … 5=friday]
|
|
||||||
|
|
||||||
cat <<-ENDOFTEXT
|
printf '%s %s\n' $offset $offsetMax
|
||||||
BEGIN:VCALENDAR
|
}
|
||||||
VERSION:2.0
|
|
||||||
PRODID:yalis.fr/epsi2ical v1.1
|
|
||||||
ENDOFTEXT
|
|
||||||
|
|
||||||
while [ $offset -lt $offsetMax ]; do
|
function main() {
|
||||||
mon=$(date +%Y%m%dT%z -d "$(($offset+1-$dayOfWeek))days")
|
check_parameters_and_set_defaults
|
||||||
tue=$(date +%Y%m%dT%z -d "$(($offset+2-$dayOfWeek))days")
|
{
|
||||||
wed=$(date +%Y%m%dT%z -d "$(($offset+3-$dayOfWeek))days")
|
local dtstamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||||
thu=$(date +%Y%m%dT%z -d "$(($offset+4-$dayOfWeek))days")
|
local offset= offsetMax=
|
||||||
fri=$(date +%Y%m%dT%z -d "$(($offset+5-$dayOfWeek))days")
|
|
||||||
|
|
||||||
for ((i=1; i<5; i=i*2)); do
|
read -r offset offsetMax < <(get_first_and_max_offset_of_year)
|
||||||
week="$(curl -s "https://edtmobiliteng.wigorservices.net/WebPsDyn.aspx?action=posEDTBEECOME&serverid=C&Tel=${1}&date=$(date +%m/%d/%Y -d "${offset}days")")" \
|
if [ -n "$PATCH" ]; then
|
||||||
&& { echo "$week"; break; }
|
offset=0
|
||||||
sleep ${i}s
|
[ $MAXOFFSET -lt $offsetMax ] && offsetMax=$MAXOFFSET
|
||||||
done \
|
fi
|
||||||
| 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
|
|
||||||
|
|
||||||
cat <<-ENDOFTEXT
|
output_ical_header
|
||||||
END:VCALENDAR
|
while [ $offset -lt $offsetMax ]; do
|
||||||
ENDOFTEXT
|
output_ical_week $offset "$dtstamp"
|
||||||
) \
|
offset=$(($offset+7))
|
||||||
| env LANG=fr_FR@euro sed -r 's/(.{73})(.)/\1\n \2/g' \
|
done
|
||||||
| sed 's/$/\r/'
|
output_ical_footer
|
||||||
|
|
||||||
|
} \
|
||||||
|
| fold_long_lines \
|
||||||
|
| lf_to_crlf \
|
||||||
|
| write_output
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
|
|
Loading…
Reference in New Issue