diff --git a/custom_components/aula/client.py b/custom_components/aula/client.py
index 5c2513a..b6f1b88 100644
--- a/custom_components/aula/client.py
+++ b/custom_components/aula/client.py
@@ -737,9 +737,10 @@ def mu_opgaver(week, thisnext):
and "0004" not in self.widgets
and "0062" not in self.widgets
and "0001" not in self.widgets
+ and "0128" not in self.widgets
):
_LOGGER.error(
- "You have enabled ugeplaner, but we cannot find any supported widgets (0029,0004,0001) in Aula."
+ "You have enabled ugeplaner, but we cannot find any supported widgets (0029,0004,0001,0128) in Aula."
)
if "0029" in self.widgets and "0004" in self.widgets:
_LOGGER.warning(
@@ -777,138 +778,199 @@ def ugeplan(week, thisnext):
except:
_LOGGER.debug("Cannot fetch ugeplaner, so setting as empty")
_LOGGER.debug("ugeplaner response " + str(ugeplaner.text))
- if "0001" in self.widgets:
- import calendar
-
- _LOGGER.debug("In the EasyIQ flow")
- token = self.get_token("0001")
- csrf_token = self._get_csrf_token()
+ easyiq_widget = next(
+ (w for w in ("0001", "0128") if w in self.widgets), None
+ )
+ if easyiq_widget is not None:
+ _LOGGER.debug("In the EasyIQ flow (widget %s)", easyiq_widget)
+ token = self.get_token(easyiq_widget)
+
+ # Monday 00:00 UTC of the requested ISO week, as JS-style
+ # ISO timestamp (the skoleportal calendar endpoint accepts
+ # any time on the target Monday — it derives the week
+ # itself).
+ iso_year, iso_week = (int(p) for p in week.split("-W"))
+ monday = datetime.date.fromisocalendar(iso_year, iso_week, 1)
+ week_iso = (
+ datetime.datetime.combine(
+ monday, datetime.time(0, 0, 0), tzinfo=pytz.utc
+ ).strftime("%Y-%m-%dT%H:%M:%S.000Z")
+ )
- easyiq_headers = {
- "x-aula-institutionfilter": str(self._institutionProfiles[0]),
- "x-aula-userprofile": "guardian",
+ skoleportal_common = {
"Authorization": token,
- "accept": "application/json",
- "origin": "https://www.aula.dk",
- "referer": "https://www.aula.dk/",
- "authority": "api.easyiqcloud.dk",
+ "X-Login": guardian,
+ "X-InstitutionFilter": ",".join(self._institutionProfiles),
+ "X-UserProfile": "guardian",
+ "X-ChildFilter": ",".join(self._childuserids),
+ "X-Requested-With": "Fetch",
+ "Accept": "application/json",
+ "Origin": "https://skoleportal.easyiqcloud.dk",
+ "Referer": "https://skoleportal.easyiqcloud.dk/UgeplanWidget",
+ "User-Agent": (
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/147.0.0.0 Safari/537.36"
+ ),
}
- if csrf_token:
- easyiq_headers["csrfp-token"] = csrf_token
-
- for child in self._childrenFirstNamesAndUserIDs.items():
- userid = child[0]
- first_name = child[1]
-
- _LOGGER.debug("EasyIQ headers " + str(easyiq_headers))
- post_data = {
- "sessionId": guardian,
- "currentWeekNr": week,
- "userProfile": "guardian",
- "institutionFilter": self._institutionProfiles,
- "childFilter": [userid],
- }
- _LOGGER.debug("EasyIQ post data " + str(post_data))
- ugeplaner = requests.post(
- EASYIQ_API + "/weekplaninfo",
- json=post_data,
- headers=easyiq_headers,
- verify=True,
- )
- # _LOGGER.debug(
- # "EasyIQ Opgaver status_code " + str(ugeplaner.status_code)
- # )
- _LOGGER.debug(
- "EasyIQ Opgaver response " + str(ugeplaner.json())
- )
- _ugep = (
- "
"
- # + ugeplaner.json()["Weekplan"]["ActivityName"]
- + " Uge "
- + week.split("-W")[1]
- # + ugeplaner.json()["Weekplan"]["WeekNo"]
- + "
"
- )
- # from datetime import datetime
-
- def findDay(date):
- day, month, year = (int(i) for i in date.split(" "))
- dayNumber = calendar.weekday(year, month, day)
- days = [
- "Mandag",
- "Tirsdag",
- "Onsdag",
- "Torsdag",
- "Fredag",
- "Lørdag",
- "Søndag",
- ]
- return days[dayNumber]
- def is_correct_format(date_string, format):
+ danish_days = [
+ "Mandag",
+ "Tirsdag",
+ "Onsdag",
+ "Torsdag",
+ "Fredag",
+ "Lørdag",
+ "Søndag",
+ ]
+
+ for userid, first_name in self._childrenFirstNamesAndUserIDs.items():
+ child_headers = dict(skoleportal_common)
+ child_headers["X-Child"] = userid
+
+ try:
+ auth_resp = requests.post(
+ "https://skoleportal.easyiqcloud.dk/Aula/AuthenticateAulaUser",
+ headers={**child_headers, "Content-Length": "0"},
+ data=b"",
+ timeout=15,
+ )
+ except requests.RequestException as exc:
+ _LOGGER.warning(
+ "EasyIQ AuthenticateAulaUser failed for %s: %s",
+ first_name,
+ exc,
+ )
+ continue
+ if auth_resp.status_code != 200:
+ _LOGGER.warning(
+ "EasyIQ AuthenticateAulaUser %s for %s: %s",
+ auth_resp.status_code,
+ first_name,
+ auth_resp.text[:200],
+ )
+ continue
+ try:
+ login_id = auth_resp.json().get("loginId")
+ except ValueError:
+ _LOGGER.warning(
+ "EasyIQ AuthenticateAulaUser returned non-JSON for %s: %s",
+ first_name,
+ auth_resp.text[:200],
+ )
+ continue
+ if not login_id:
+ _LOGGER.warning(
+ "EasyIQ AuthenticateAulaUser missing loginId for %s: %s",
+ first_name,
+ auth_resp.text[:200],
+ )
+ continue
+
+ try:
+ events_resp = requests.get(
+ "https://skoleportal.easyiqcloud.dk/Calendar/CalendarGetWeekplanEvents",
+ params={
+ "loginId": login_id,
+ "date": week_iso,
+ "activityFilter": "-1",
+ "courseFilter": "-1",
+ "textFilter": "",
+ "ownWeekPlan": "false",
+ },
+ headers=child_headers,
+ timeout=15,
+ )
+ except requests.RequestException as exc:
+ _LOGGER.warning(
+ "EasyIQ CalendarGetWeekplanEvents failed for %s week %s: %s",
+ first_name,
+ week,
+ exc,
+ )
+ continue
+ if events_resp.status_code != 200:
+ _LOGGER.warning(
+ "EasyIQ CalendarGetWeekplanEvents %s for %s week %s: %s",
+ events_resp.status_code,
+ first_name,
+ week,
+ events_resp.text[:200],
+ )
+ continue
+ try:
+ events = events_resp.json()
+ except ValueError:
+ _LOGGER.warning(
+ "EasyIQ CalendarGetWeekplanEvents non-JSON for %s: %s",
+ first_name,
+ events_resp.text[:200],
+ )
+ continue
+ if not isinstance(events, list):
+ _LOGGER.warning(
+ "EasyIQ CalendarGetWeekplanEvents unexpected shape for %s: %s",
+ first_name,
+ type(events).__name__,
+ )
+ events = []
+
+ _ugep = " Uge " + week.split("-W")[1] + "
"
+ events.sort(key=lambda e: e.get("StartTime") or "")
+ for ev in events:
+ start_str = ev.get("StartTime")
+ end_str = ev.get("EndTime")
+ if not start_str or not end_str:
+ continue
try:
- datetime.datetime.strptime(date_string, format)
- return True
+ start_dt = datetime.datetime.strptime(
+ start_str, "%Y/%m/%d %H:%M"
+ )
+ end_dt = datetime.datetime.strptime(
+ end_str, "%Y/%m/%d %H:%M"
+ )
except ValueError:
_LOGGER.debug(
- "Could not parse timestamp: " + str(date_string)
+ "EasyIQ event for %s: unrecognised time format %r/%r",
+ first_name,
+ start_str,
+ end_str,
)
- return False
-
- try:
- for i in ugeplaner.json()["Events"]:
- if is_correct_format(i["start"], "%Y/%m/%d %H:%M"):
- _LOGGER.debug("No Event")
- start_datetime = datetime.datetime.strptime(
- i["start"], "%Y/%m/%d %H:%M"
- )
- _LOGGER.debug(start_datetime)
- end_datetime = datetime.datetime.strptime(
- i["end"], "%Y/%m/%d %H:%M"
- )
- if start_datetime.date() == end_datetime.date():
- formatted_day = findDay(
- start_datetime.strftime("%d %m %Y")
- )
- formatted_start = start_datetime.strftime(
- " %H:%M"
- )
- formatted_end = end_datetime.strftime("- %H:%M")
- dresult = f"{formatted_day} {formatted_start} {formatted_end}"
- else:
- formatted_start = findDay(
- start_datetime.strftime("%d %m %Y")
- )
- formatted_end = findDay(
- end_datetime.strftime("%d %m %Y")
- )
- dresult = f"{formatted_start} {formatted_end}"
- _ugep = _ugep + "
" + dresult + "
"
- if i["itemType"] == "5":
- _ugep = (
- _ugep
- + "
"
- + str(i["title"])
- + "
"
- )
- else:
- _ugep = (
- _ugep
- + "
"
- + str(i["ownername"])
- + "
"
- )
- _ugep = _ugep + str(i["description"]) + "
"
- else:
- _LOGGER.debug("None")
- except KeyError:
- _LOGGER.debug("None")
+ continue
+ if start_dt.date() == end_dt.date():
+ dresult = (
+ f"{danish_days[start_dt.weekday()]} "
+ f"{start_dt.strftime('%H:%M')}"
+ f"- {end_dt.strftime('%H:%M')}"
+ )
+ else:
+ dresult = (
+ f"{danish_days[start_dt.weekday()]} "
+ f"{danish_days[end_dt.weekday()]}"
+ )
+ _ugep += "
" + dresult + "
"
+ subject = (
+ ev.get("CoursesDisplay")
+ or ev.get("Title")
+ or ""
+ ).strip()
+ if subject:
+ _ugep += "
" + str(subject) + "
"
+ description = ev.get("Description") or ""
+ if description:
+ _ugep += str(description) + "
"
if thisnext == "this":
self.ugep_attr[first_name] = _ugep
elif thisnext == "next":
self.ugepnext_attr[first_name] = _ugep
- _LOGGER.debug("EasyIQ result: " + str(_ugep))
+ _LOGGER.debug(
+ "EasyIQ result for %s week %s (%d events): %s",
+ first_name,
+ week,
+ len(events),
+ _ugep[:200],
+ )
if "0062" in self.widgets:
_LOGGER.debug("In the Huskelisten flow...")