defmodule Webuntis do @moduledoc """ Documentation for Webuntis. """ @doc """ Returns the internal id of the student. Example: 889 > Requires authentication """ def student_id do fetch_data("public/timetable/weekly/pageconfig?type=5&date=" <> current_date()) |> decode |> Map.fetch!("elements") |> List.first() |> Map.fetch!("id") end @doc """ Returns the internal id of the student's class. Example: 237 > Requires authentication """ def klasse_id do fetch_data("public/timetable/weekly/pageconfig?type=5&date=" <> current_date()) |> decode |> Map.fetch!("elements") |> List.first() |> Map.fetch!("klasse_id") end @doc """ Returns the last update of the timetable in NaiveDateTime. > Requires authentication """ def last_update do webuntis_date = fetch_data("public/info/infoWidgetData") |> decode |> Map.fetch!("lastImportDate") |> to_string |> String.codepoints() {year, _} = webuntis_date |> Enum.slice(0, 4) |> Enum.join() |> Integer.parse() {month, _} = webuntis_date |> Enum.slice(4, 2) |> Enum.join() |> Integer.parse() {day, _} = webuntis_date |> Enum.slice(6, 2) |> Enum.join() |> Integer.parse() {hour, _} = webuntis_date |> Enum.slice(8, 2) |> Enum.join() |> Integer.parse() {minute, _} = webuntis_date |> Enum.slice(10, 2) |> Enum.join() |> Integer.parse() {:ok, date} = NaiveDateTime.new(year, month, day, hour, minute, 0) date end @doc """ Returns the timetable of the student for the current week. Same as: ```elixir Webuntis.timetable_student(Webuntis.student_id(), Webuntis.current_date()) ``` > Requires authentication """ def timetable_student do timetable(5, student_id(), current_date()) end @doc """ Returns an ics file for the current week. Example: Webuntis.timetable_ics(5, Webuntis.student_id(), Webuntis.current_date()) > Requires authentication """ def timetable_ics(type, id, date, base_url \\ "mese.webuntis.com") do url = "https://" <> base_url <> "/WebUntis/Ical.do?" <> "elemType=" <> to_string(type) <> "&elemId=" <> to_string(id) <> "&rpt_sd=" <> date state = Webuntis.Auth.current_state() HTTPoison.get!(url, [{"Cookie", state.cookies}]).body end @doc """ Returns the timetable for the week which starting at the given date. > Requires authentication """ def timetable(type, id, date) do data = timetable_raw(type, id, date) |> decode elements = data |> Map.fetch!("result") |> Map.fetch!("data") |> Map.fetch!("elements") |> Enum.map(fn json_element -> %Webuntis.Struct.Element{ type: Map.get(json_element, "type"), id: Map.get(json_element, "id", 0), name: Map.get(json_element, "name", ""), long_name: Map.get(json_element, "longName", ""), displayname: Map.get(json_element, "displayname", ""), alternatename: Map.get(json_element, "alternatename", ""), fore_color: Map.get(json_element, "foreColor", ""), back_color: Map.get(json_element, "backColor", ""), can_view_timetable: Map.get(json_element, "canViewTimetable", false), extern_key: Map.get(json_element, "externKey", ""), room_capacity: Map.get(json_element, "roomCapacity", 0) } end) periods = data |> Map.fetch!("result") |> Map.fetch!("data") |> Map.fetch!("elementPeriods") |> Map.fetch!(to_string(id)) %{elements: elements, periods: periods} end @doc """ Returns the periods for the day at the given date. Example usage: ```elixir timetable_student() |> periods("") ``` """ @doc since: "1.0.1" def periods(timetable_week, date) do date_string = date |> to_string |> String.replace("-", "") timetable_week.periods |> Enum.filter(fn period_raw -> to_string(Map.fetch!(period_raw, "date")) == date_string end) |> Enum.map(fn period_raw -> teacher = timetable_week.elements |> Enum.filter(fn element -> element.type == 2 end) |> Enum.filter(fn element -> teacher_id = Map.fetch!(period_raw, "elements") |> Enum.filter(fn element -> Map.fetch!(element, "type") == 2 end) |> Enum.fetch!(0) |> Map.fetch!("id") element.id == teacher_id end) |> Enum.map(fn element -> Webuntis.Struct.Teacher.from_element(element) end) |> Enum.fetch!(0) lesson = timetable_week.elements |> Enum.filter(fn element -> element.type == 3 end) |> Enum.filter(fn element -> lesson_id = Map.fetch!(period_raw, "elements") |> Enum.filter(fn element -> Map.fetch!(element, "type") == 3 end) |> Enum.fetch!(0) |> Map.fetch!("id") element.id == lesson_id end) |> Enum.map(fn element -> Webuntis.Struct.Lesson.from_element(element) end) |> Enum.fetch!(0) room = timetable_week.elements |> Enum.filter(fn element -> element.type == 4 end) |> Enum.filter(fn element -> room_id = Map.fetch!(period_raw, "elements") |> Enum.filter(fn element -> Map.fetch!(element, "type") == 4 end) |> Enum.fetch!(0) |> Map.fetch!("id") element.id == room_id end) |> Enum.map(fn element -> Webuntis.Struct.Room.from_element(element) end) |> Enum.fetch!(0) %Webuntis.Struct.Period{ start: parse_time(Map.fetch!(period_raw, "startTime")), end: parse_time(Map.fetch!(period_raw, "endTime")), date: date, teacher: teacher, lesson: lesson, room: room } end) |> Enum.sort(&(&1.start < &2.start)) end @doc """ Returns the raw json response. Don't use it if you don't know what you're doing! > Requires authentication """ @doc since: "0.1.1" def timetable_raw(type, id, date) do fetch_data( "public/timetable/weekly/data?elementType=" <> to_string(type) <> "&elementId=" <> to_string(id) <> "&date=" <> date <> "&formatId=5" ) end @doc """ Returns news for the current day. """ def news do date = current_date() |> String.replace("-", "") fetch_data("public/news/newsWidgetData?date=" <> date) |> decode end @doc """ Returns the date of the current day. """ def current_date do Date.utc_today() |> to_string end # -= private stuff =- defp parse_date(webuntis_date) do {year, _} = webuntis_date |> Enum.slice(0, 4) |> Enum.join() |> Integer.parse() {month, _} = webuntis_date |> Enum.slice(4, 2) |> Enum.join() |> Integer.parse() {day, _} = webuntis_date |> Enum.slice(6, 2) |> Enum.join() |> Integer.parse() {hour, _} = webuntis_date |> Enum.slice(8, 2) |> Enum.join() |> Integer.parse() {minute, _} = webuntis_date |> Enum.slice(10, 2) |> Enum.join() |> Integer.parse() {:ok, date} = NaiveDateTime.new(year, month, day, hour, minute, 0) date end defp parse_time(webuntis_time) when webuntis_time < 1000 do string = webuntis_time |> to_string |> String.codepoints() {hour, _} = string |> Enum.slice(0, 1) |> Enum.join() |> Integer.parse() {minute, _} = string |> Enum.slice(1, 2) |> Enum.join() |> Integer.parse() {:ok, time} = Time.new(hour, minute, 0) time end defp parse_time(webuntis_time) do string = webuntis_time |> to_string |> String.codepoints() {hour, _} = string |> Enum.slice(0, 2) |> Enum.join() |> Integer.parse() {minute, _} = string |> Enum.slice(2, 2) |> Enum.join() |> Integer.parse() {:ok, time} = Time.new(hour, minute, 0) time end defp fetch_data(url) do state = Webuntis.Auth.current_state() {:ok, response} = HTTPoison.get("https://" <> state.base_url <> "/WebUntis/api/" <> url, [ {"Accept", "application/json"}, {"Cookie", state.cookies} ]) response.body end defp decode(reponse_body) do Jason.decode!(reponse_body)["data"] end end