% Copyright (c) Camden Dixie O'Brien % SPDX-License-Identifier: AGPL-3.0-only -module(subject_router). -behaviour(gen_server). -export([start_link/0, log_session/1, get_activities/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). log_session(Session) -> gen_server:call(?MODULE, {log_session, Session}). get_activities() -> gen_server:call(?MODULE, get_activities). init([]) -> {MonitorRef, Pids} = pg:monitor(study_system_server, subject_servers), SubjectTable = ets:new(subject_table, [private]), register_servers(SubjectTable, Pids), {ok, #{monitor_ref => MonitorRef, subject_table => SubjectTable}}. handle_call({log_session, {Subject, Type, Timestamp, Minutes}}, _From, State = #{subject_table := SubjectTable}) -> case ets:lookup(SubjectTable, Subject) of [{Subject, Pid}] -> {reply, subject_server:log_session(Pid, Type, Timestamp, Minutes), State}; [] -> {reply, {error, invalid_subject}, State} end; handle_call(get_activities, _From, State = #{subject_table := SubjectTable}) -> Pids = lists:flatten(ets:match(SubjectTable, {'_', '$1'})), Activities = lists:flatmap( fun(Pid) -> subject_server:get_activities(Pid) end, Pids), {reply, {activities, Activities}, State}; handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({Ref, join, subject_servers, Pids}, State = #{monitor_ref := MonitorRef, subject_table := SubjectTable}) when Ref =:= MonitorRef -> register_servers(SubjectTable, Pids), {noreply, State}; handle_info({Ref, leave, subject_servers, Pids}, State = #{monitor_ref := MonitorRef, subject_table := SubjectTable}) when Ref =:= MonitorRef -> deregister_servers(SubjectTable, Pids), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. register_servers(SubjectTable, Pids) -> [register_server(SubjectTable, Pid) || Pid <- Pids]. register_server(SubjectTable, Pid) -> try {subject, Subject} = subject_server:get_subject(Pid), ets:insert(SubjectTable, {Subject, Pid}) catch _:_ -> ok end. deregister_servers(SubjectTable, Pids) -> [deregister_server(SubjectTable, Pid) || Pid <- Pids]. deregister_server(SubjectTable, Pid) -> ets:match_delete(SubjectTable, {'_', Pid}).