Signal drop!
Relay (operand.online) is unreachable.
Usually, a dropped signal means an upgrade is happening. Hold on!
Sorry, no connección.
Hang in there while we get back on track
gram:pain
> ./lib/pain/schedule.ex
Lenses
(coming soon!)
defmodule Pain.Schedule do
import Acuity, only: [headers: 0]
def ending, do: ":00-0400"
def log(node), do: node |> IO.inspect
def today, do: now() |> DateTime.to_date()
def now do
case DateTime.now("America/New_York") do
{:ok, c } -> c
{:error, _ } -> DateTime.utc_now()
end
end
def day(phrase), do: Date.from_iso8601(phrase) |> elem(1)
def range(day), do: Date.range(day, day)
def this_month do
Date.range(today(), today() |> Date.end_of_month)
end
def month(month) do
[y,m] = month |> String.split("-") |> Enum.map(&String.to_integer/1)
beginning = Date.new(y,m,1) |> elem(1) |> Date.beginning_of_month
Date.range(beginning, beginning |> Date.end_of_month)
end
def service_demand(service_keys) do
service_keys
|> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, &(&1 + 1)) end)
end
@doc """
import Pain.Schedule
check_blocks(
service_demand([ 39928578, 39928780, 39931669, ]),
[ 7733522, 4351609, 8178118, 7733431, 7733550, 7822447, 7832226, ],
this_month())
"""
def check_blocks demand, employee_keys, range do
(range |> Enum.take(14) |> Parallel.map(fn day ->
demand |> Parallel.map(fn { service, demand } ->
check_calendar_day_service(service, employee_keys, day)
|> reduce_calendars
|> Enum.filter(fn { _, num } -> num >= demand end)
|> Enum.map(fn { block, _ } -> block end)
end)
|> reduce_blocks
end))
end
def check_calendar_day_service service, employee_keys, day do
cals = calendars()
employee_keys
|> Enum.filter(&(Enum.member?(Map.get(cals, service), &1)))
|> Enum.map(fn employee ->
search_hours = "https://acuityscheduling.com/api/v1/availability/times?date=#{day}&appointmentTypeID=#{service}&calendarID=#{employee}"
case Req.get(search_hours, headers: headers()) do
{:error, r} -> log r; []
{:ok, r = %Req.Response{status: 400}} -> log r; []
{:ok, r } -> r.body
end
end)
end
@doc """
Pain.Schedule.check_blocks_on_calendars(
"2023-08-28T14:00:00-0400",
[39929015, 39925969, 39928578, 39931669],
[7733522, 4351609, 8178118, 7733431, 7733550, 7822447, 7832226])
"""
def check_blocks_on_calendars block, service_keys, employee_keys do
address = "https://acuityscheduling.com/api/v1/availability/check-times"
cals = calendars()
service_keys |> Enum.map(fn service ->
body = employee_keys
|> Enum.filter(&(Enum.member?(Map.get(cals, service), &1)))
|> Enum.map(fn employee -> %{
datetime: block,
appointmentTypeID: service,
calendarID: employee
} end)
case Req.post(address, json: body, headers: headers()) do
{:error, r} -> log r; []
{:ok, r = %Req.Response{status: 400}} -> log r; []
{:ok, r } -> r.body
end
end) |> Squish.squish
end
@appts "https://acuityscheduling.com/api/v1/appointment-types"
def calendars do
Req.get!(@appts, headers: headers()).body
|> Enum.reduce(%{}, fn x, calendars ->
Map.put(calendars, Map.get(x, "id"), Map.get(x, "calendarIDs"))
end)
end
@addons "https://acuityscheduling.com/api/v1/appointment-addons"
def addons, do: Req.get!(@addons, headers: headers()).body
def reduce_calendars calendars do
calendars |> Enum.reduce(%{}, fn calendar, all ->
calendar |> Enum.reduce(all, fn calBlock, cal ->
Map.update(cal, calBlock["time"], 0, &(&1 + calBlock["slotsAvailable"]))
end)
end)
end
def reduce_blocks [original | remaining] do
remaining |> Enum.reduce(MapSet.new(original), fn blocks, solid ->
MapSet.intersection MapSet.new(blocks), solid
end) |> MapSet.to_list
end
def blocks_by_day blocks do
blocks
|> Enum.filter(&(length(&1) > 0))
|> Enum.reduce(%{}, fn day, all ->
Map.put(all, (day |> hd |> String.split("T") |> hd), day)
end)
end
@doc """
import Pain.Schedule
( service_demand([ 39928578, 39928780, 39931669, ])
|> check_blocks(
[ 7733522, 4351609, 8178118, 7733431, 7733550, 7822447, 7832226, ],
this_month()
) |> blocks_by_day()
)["2023-09-30"] |> hour_map()
"""
def hour_map(clocks) do
clocks
|> Enum.map(&( Regex.scan(~r/\d{2}:\d{2}/, &1) |> hd |> hd))
|> Enum.sort()
|> Enum.reduce(%{}, fn c, by_hour ->
[h | [m | []]] = c |> String.split(":")
Map.update(by_hour, h, [m], &(&1 ++ [m]))
end)
end
end