<commit_before>-module(resolver).

-behaviour(gen_server).

%% API
-export([start_link/0, dispatch/2, dispatch/3, qid2pid/1, sid2pid/1, 
         resolvers/0, register_sid/2, add_resolver/5]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
     terminate/2, code_change/3]).

-record(state, {queries, sources, resolvers}).
-record(resolver, {mod, name, weight, targettime, pid}).

%% API
start_link()        -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

% dispatch a Query, returns the query pid
%dispatch(Q)         -> dispatch(Q, utils:uuid_gen()).
dispatch(Q, Qid)        -> dispatch(Q, Qid, []).
dispatch(Q, Qid, Cbs)   -> gen_server:call(?MODULE, {dispatch, Q, Qid, Cbs}).

qid2pid(Qid)        -> gen_server:call(?MODULE, {qid2pid, Qid}).
sid2pid(Sid)        -> gen_server:call(?MODULE, {sid2pid, Sid}).

register_sid(Sid, Qpid) -> gen_server:cast(?MODULE, {register_sid, Sid, Qpid}).

resolvers()         -> gen_server:call(?MODULE, resolvers).

add_resolver(Mod, Name, Weight, TargetTime, Pid) ->
    gen_server:cast(?MODULE,{add_resolver, Mod, Name, Weight, TargetTime, Pid}).

%% gen_server callbacks
init([]) ->
	{R1,R2,R3} = now(),
	random:seed(R1,R2,R3),
    % This ets maps Qids to query pids:
    Tid = ets:new(queries, []),
    % and this one maps Source IDs to query pids
    Tid2= ets:new(sources, []),
    % Load the resolvers:
    ResNames = [fake_resolver, fake_resolver2, lan_resolver],
    lists:foreach(fun(M)-> M:start_link() end, ResNames),
    % Load script-resolvers:
    Scripts = [ "~/src/playdar/contrib/demo-script/demo-resolver.php" ],
    % Resolvers will call resolver:add_resolver() to register themselves
    % once they've finished their startup routines.
    % TODO run a supervisor for the various resolvers, add to supervision tree
    % once they register themselves.
    lists:foreach(fun(S)-> script_resolver:start_link(S) end, Scripts),
    {ok, #state{    queries=Tid, 
                    sources=Tid2, 
                    resolvers=[]}}.

handle_call(resolvers, _From, State) ->
    % the #resolver record is internal to this module
    % so we make a proplist for handing out externally:
    Res = [ [{mod, R#resolver.mod},
             {name, R#resolver.name},
             {weight, R#resolver.weight},
             {targettime, R#resolver.targettime},
             {pid, R#resolver.pid}] || R <- State#state.resolvers ],
    {reply, Res, State};

handle_call({qid2pid, Qid}, _From, State) ->
    case ets:lookup(State#state.queries, {qid, Qid}) of
        [{_, Pid}] ->
            {reply, Pid, State};
        _ ->
            {reply, undefined, State}
    end;
    
handle_call({sid2pid, Sid}, _From, State) ->
    case ets:lookup(State#state.sources, {sid, Sid}) of
        [{_, Pid}] ->
            {reply, Pid, State};
        _ ->
            {reply, undefined, State}
    end;
    
handle_call({dispatch, Q, Qid, Cbs}, _From, State) ->
	% First of all, do nothing if a query with this Qid already exists:
	case ets:lookup(State#state.queries, {qid, Qid}) of
		[P] when is_pid(P) ->
			{reply, P, State};
		_ ->
			% spawn a qry process to hold this query:
			{ok, Pid} = gen_server:start(qry, [Q, Qid], []),
			% register callbacks before triggering search:
			lists:foreach(fun(C) -> qry:add_result_callback(Pid, C) end, Cbs),
			% keep track of qid -> qry_pid
			ets:insert(State#state.queries, {{qid, Qid}, Pid}),
			% dispatch to resolvers
			start_resolver_pipeline(Q, Pid, State#state.resolvers),
			{reply, Pid, State}
	end.


handle_cast({add_resolver,Mod, Name, Weight, TargetTime, Pid}, State) ->
    io:format("add_resolver: Mod:~w\tName:'~s'\tWeight:~w\tTT:~w\tPid:~w~n",
                [Mod, Name, Weight, TargetTime, Pid]),
    R = #resolver{mod=Mod, name=Name, weight=Weight, targettime=TargetTime, pid=Pid},
    % 4 is the pos in the #resolver tuple for "weight":
    Resolvers = lists:reverse(lists:keysort(3, [R|State#state.resolvers])),
    {noreply, State#state{resolvers=Resolvers}};

handle_cast({register_sid, Sid, Qpid}, State) -><commit_msg>fix resolver sort order as per comments. doh.<commit_after>-module(resolver).

-behaviour(gen_server).

%% API
-export([start_link/0, dispatch/2, dispatch/3, qid2pid/1, sid2pid/1, 
         resolvers/0, register_sid/2, add_resolver/5]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
     terminate/2, code_change/3]).

-record(state, {queries, sources, resolvers}).
-record(resolver, {mod, name, weight, targettime, pid}).

%% API
start_link()        -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

% dispatch a Query, returns the query pid
%dispatch(Q)         -> dispatch(Q, utils:uuid_gen()).
dispatch(Q, Qid)        -> dispatch(Q, Qid, []).
dispatch(Q, Qid, Cbs)   -> gen_server:call(?MODULE, {dispatch, Q, Qid, Cbs}).

qid2pid(Qid)        -> gen_server:call(?MODULE, {qid2pid, Qid}).
sid2pid(Sid)        -> gen_server:call(?MODULE, {sid2pid, Sid}).

register_sid(Sid, Qpid) -> gen_server:cast(?MODULE, {register_sid, Sid, Qpid}).

resolvers()         -> gen_server:call(?MODULE, resolvers).

add_resolver(Mod, Name, Weight, TargetTime, Pid) ->
    gen_server:cast(?MODULE,{add_resolver, Mod, Name, Weight, TargetTime, Pid}).

%% gen_server callbacks
init([]) ->
	{R1,R2,R3} = now(),
	random:seed(R1,R2,R3),
    % This ets maps Qids to query pids:
    Tid = ets:new(queries, []),
    % and this one maps Source IDs to query pids
    Tid2= ets:new(sources, []),
    % Load the resolvers:
    ResNames = [fake_resolver, fake_resolver2, lan_resolver],
    lists:foreach(fun(M)-> M:start_link() end, ResNames),
    % Load script-resolvers:
    Scripts = [ "/home/rj/src/playdar/contrib/demo-script/demo-resolver.php" ],
    % Resolvers will call resolver:add_resolver() to register themselves
    % once they've finished their startup routines.
    % TODO run a supervisor for the various resolvers, add to supervision tree
    % once they register themselves.
    lists:foreach(fun(S)-> script_resolver:start_link(S) end, Scripts),
    {ok, #state{    queries=Tid, 
                    sources=Tid2, 
                    resolvers=[]}}.

handle_call(resolvers, _From, State) ->
    % the #resolver record is internal to this module
    % so we make a proplist for handing out externally:
    Res = [ [{mod, R#resolver.mod},
             {name, R#resolver.name},
             {weight, R#resolver.weight},
             {targettime, R#resolver.targettime},
             {pid, R#resolver.pid}] || R <- State#state.resolvers ],
    {reply, Res, State};

handle_call({qid2pid, Qid}, _From, State) ->
    case ets:lookup(State#state.queries, {qid, Qid}) of
        [{_, Pid}] ->
            {reply, Pid, State};
        _ ->
            {reply, undefined, State}
    end;
    
handle_call({sid2pid, Sid}, _From, State) ->
    case ets:lookup(State#state.sources, {sid, Sid}) of
        [{_, Pid}] ->
            {reply, Pid, State};
        _ ->
            {reply, undefined, State}
    end;
    
handle_call({dispatch, Q, Qid, Cbs}, _From, State) ->
	% First of all, do nothing if a query with this Qid already exists:
	case ets:lookup(State#state.queries, {qid, Qid}) of
		[P] when is_pid(P) ->
			{reply, P, State};
		_ ->
			% spawn a qry process to hold this query:
			{ok, Pid} = gen_server:start(qry, [Q, Qid], []),
			% register callbacks before triggering search:
			lists:foreach(fun(C) -> qry:add_result_callback(Pid, C) end, Cbs),
			% keep track of qid -> qry_pid
			ets:insert(State#state.queries, {{qid, Qid}, Pid}),
			% dispatch to resolvers
			start_resolver_pipeline(Q, Pid, State#state.resolvers),
			{reply, Pid, State}
	end.


handle_cast({add_resolver,Mod, Name, Weight, TargetTime, Pid}, State) ->
    io:format("add_resolver: Mod:~w\tName:'~s'\tWeight:~w\tTT:~w\tPid:~w~n",
                [Mod, Name, Weight, TargetTime, Pid]),
    R = #resolver{mod=Mod, name=Name, weight=Weight, targettime=TargetTime, pid=Pid},
    % 4 is the pos in the #resolver tuple for "weight":
    Resolvers = lists:reverse(lists:keysort(4, [R|State#state.resolvers])),
    {noreply, State#state{resolvers=Resolvers}};

handle_cast({register_sid, Sid, Qpid}, State) ->
{'fraction_non_alphanumeric': 0.1579075722472869, 'fraction_numerical': 0.008047799048896477, 'mean_word_length': 3.344279661016949, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6781090', 'n_tokens_mistral': 3024, 'n_tokens_neox': 2914, 'n_words': 693}