使用gen_server实现的更为复杂的。。。山寨kahoot!!!!!!
然鹅这次我们换名字了,现在它叫阿童木TA(robota)!
-module(robota). -behaviour(gen_server). %% Test helper function -export([get_module_grader/1, get_concurrent_grader/1, valid/3, get_mgrader/1, run_all_module_grader/1, troels_eval/2, check/2, eval/3]). %% API -export([get_the_show_started/0, new/2, add_part/3, del_part/2, available/1, unavailable/1, status/1, grade/4]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(SERVER, ?MODULE). %%%=================================================================== %%% API %%%=================================================================== get_the_show_started() -> try gen_server:start_link({local, ?SERVER}, ?MODULE, [], []) catch _ : _ -> {error,"Failed to crate a RoboTA!/n"} end. new(RoboTA, Name) -> gen_server:call(RoboTA, {new, Name}). add_part(AssHandler, Label, Grader) -> gen_server:call(?MODULE, {add_part, AssHandler, Label, Grader}). del_part(AssHandler, Label) -> gen_server:cast(?MODULE, {del_part, AssHandler, Label}). available(AssHandler) -> gen_server:call(?MODULE, {available, AssHandler}). unavailable(AssHandler) -> gen_server:call(?MODULE, {unavailable, AssHandler}). status(AssHandler) -> gen_server:call(?MODULE, {status, AssHandler}). grade(RoboTA, Name, Submission, Pid) -> gen_server:call(RoboTA, {grade, Name, Submission, Pid}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== init([]) -> {ok, #{}}. handle_call({new, Name}, _From, State) -> case maps:find(Name, State) of {ok, _} -> {reply, {error,the_name_has_been_token}, State}; _ -> Ref = make_ref(), AssHandler = {Name, Ref}, NewState = maps:put(Name, {[], false, []}, State), {reply, {ok, AssHandler}, NewState} end; handle_call({add_part, AssHandler, Label, Grader}, _From, State) -> {Name, _Ref} = AssHandler, case maps:find(Name, State) of % check assignment not active {ok, {AssignmentList,false, GraderList}} -> % check label unique case lists:keymember(Label, 1, AssignmentList) of true -> {reply, {error, {Label, not_unique}}, State}; false -> NewAssignmentList = lists:append(AssignmentList,[{Label,Grader}]), NewState = maps:put(Name, {NewAssignmentList, false, GraderList}, State), {reply, ok, NewState} end; {ok, {_AssignmentList,true, _}} -> {reply, {error, {Name, is_available}}, State}; _ -> {reply, {error, fake_asshandler}, State} end; handle_call({available, AssHandler}, _From, State) -> {Name, _Ref} = AssHandler, case maps:find(Name, State) of {ok, {AssignmentList,false, GraderList}} -> ModuleGrader = get_mgrader(AssignmentList), case ModuleGrader of [] -> NewState = maps:put(Name, {AssignmentList, true}, State), {reply, ok, NewState}; _ -> try NewAssignmentList = run_all_module_grader(AssignmentList), NewState = maps:put(Name, {NewAssignmentList, true, GraderList}, State), {reply, ok, NewState} catch Error -> Error end end; {ok, {_AssignmentList,true, _}} -> {reply, {error, already_available}, State}; _ -> {reply, {error, fake_asshandler}, State} end; handle_call({unavailable, AssHandler}, _From, State) -> {Name, _Ref} = AssHandler, case maps:find(Name, State) of {ok, {AssignmentList, true, GraderList}} -> % check no submission under grading and all graders have been upload ModuleGrader = get_mgrader(AssignmentList), try unload_all(ModuleGrader), check_no_grading(GraderList), NewState = maps:put(Name, {AssignmentList, false, []}, State), {reply, ok, NewState} catch Reason -> {reply, {error, Reason}, State} end; {ok, {_AssignmentList,false, _}} -> {reply, {error, already_unavailable}, State}; _ -> {reply, {error, fake_asshandler}, State} end; handle_call({status, AssHandler}, _From, State) -> {Name, _Ref} = AssHandler, case maps:find(Name, State) of {ok, {AssignmentList, false, _}} -> {reply, {unavailable, AssignmentList}, State}; {ok, {AssignmentList, true, _}} -> {reply, {available, AssignmentList}, State} end; handle_call({grade, Name, Submission, Pid}, _From, State) -> case maps:find(Name, State) of {ok, {_AssignmentList, false, _}} -> {reply, {error, {Name, is_unavailable}}, State}; {ok, {AssignmentList, true, GraderList}} -> GraderProcess = spawn(fun() -> graderloop([]) end), NewState = maps:put(Name, {AssignmentList, true, [GraderProcess | GraderList]}, State), case valid(Submission, [], AssignmentList) of false -> Pid ! {error, invalid_submission}; ValidSub -> try Feedback = eval(ValidSub, AssignmentList, GraderProcess), Pid ! {ok, Feedback} catch _:_ -> totally_grader_failed end end, {reply, finished, NewState} end. handle_cast({del_part, AssHandler, Label}, State) -> {Name, _Ref} = AssHandler, case maps:find(Name, State) of {ok, {AssignmentList, Active, GraderList}} -> case lists:keyfind(Label, 1, AssignmentList) of false -> {noreply, State}; Tuple -> NewAssignmentList = lists:delete(Tuple, AssignmentList), NewState = maps:put(Name, {NewAssignmentList, Active, GraderList}, State), {noreply, NewState} end; _ -> {noreply, State} end. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. % helper functions get_concurrent_grader([]) -> []; get_concurrent_grader([X|Xs]) -> case X of {_Label, {troels, Gs}} -> lists:append(Gs, get_concurrent_grader(Xs)); _ -> get_concurrent_grader(Xs) end. get_module_grader([]) -> []; get_module_grader([X|Xs]) -> case X of {_Label, {andrzej, Callback, Init}} -> lists:append([{andrzej, Callback, Init}], get_module_grader(Xs)); {andrzej, Callback, Init} -> lists:append([{andrzej, Callback, Init}], get_module_grader(Xs)); _ -> get_module_grader(Xs) end. % combine get_concurrent_grader and get_module_grader get_mgrader(AL) -> CL = get_concurrent_grader(AL), AAL = lists:append(CL,AL), get_module_grader(AAL). % run all module grader run_all_module_grader([]) -> []; run_all_module_grader([X|Xs]) -> case X of {Label, {andrzej, Callback, Init}} -> case Callback:setup(Init) of {ok, Salt} -> lists:append([{Label, {andrzej, Callback, Salt}}], run_all_module_grader(Xs)); % throw? Error -> Error end; {Label, {troels, Gs}} -> try NGs = run_list_module_grader(Gs), lists:append([{Label, {troels, NGs}}], run_all_module_grader(Xs)) catch _ -> grader_failed end; Other -> lists:append([Other],run_all_module_grader(Xs)) end. run_list_module_grader([]) -> []; run_list_module_grader([X|Xs]) -> case X of {andrzej, Callback, Init} -> case Callback:setup(Init) of {ok, Salt} -> lists:append([{andrzej, Callback, Salt}], run_list_module_grader(Xs)); % throw? Error -> Error end; Other -> lists:append([Other],run_list_module_grader(Xs)) end. valid([],_,_) -> []; valid([{Label, Answer} | Xs], Cont, AssignmentList) -> case lists:keymember(Label, 1, AssignmentList) of true -> case lists:member(Label, Cont) of true -> valid(Xs, Cont, AssignmentList); false -> lists:append([{Label, Answer}], valid(Xs, [Label|Cont], AssignmentList)) end; false -> false end. % check submissions with label and Answers with label eval(_ValidSub,[],_) -> []; eval(ValidSub,[{Label,Grader} | AL],GraderProcess) -> case lists:keyfind(Label, 1, ValidSub) of false -> lists:append([{Label, missing}], eval(ValidSub, AL, GraderProcess)); {Label,Answer} -> try Me = self(), GraderProcess ! {Me,grading_start}, spawn(fun() -> try Result = check(Answer, Grader), Me ! Result catch _:_ -> grader_failed end end), receive Result -> GraderProcess ! {Me, grading_finish}, lists:append([{Label,Result}], eval(ValidSub,AL, GraderProcess)) after 3000 -> GraderProcess ! {Me, grading_finish}, lists:append([{Label,grader_failed}], eval(ValidSub,AL, GraderProcess)) end catch _:_ -> lists:append([{Label,grader_failed}], eval(ValidSub,AL, GraderProcess)) end end. % check Gs result or total grader fail troels_eval([],[]) -> []; troels_eval([A|As],[G|Gs]) -> Me = self(), try spawn(fun() -> try Res = check(A,G), Me ! Res catch _:_ -> grader_failed end end), Rs = troels_eval(As,Gs), receive Result -> lists:append(Rs, [Result]) end catch _:_ -> throw(grader_failed) end; troels_eval(_,_) -> throw(grader_failed). % check single Answer in Gs is correct or not check(A,G) -> case G of abraham -> looks_good; niels -> failed; {mikkel, Expect} -> case (A =:= Expect) of true -> looks_good; _ -> failed end; {simon, Arg, Expect} -> case (A(Arg) =:= Expect) of true -> looks_good; false -> failed end; {andrzej, Callback, Salt} -> {ok, Result} = Callback:grade(Salt, {fakelabel, A}), Result; {troels, Gs} -> try troels_eval(A, Gs) catch _:_ -> grader_failed end; _ -> throw(grader_failed) end. unload_all([]) -> ok; unload_all([{andrzej, Callback, Salt}|Xs]) -> try Callback:unload(Salt), unload_all(Xs) catch Error -> Error end. graderloop(GraderList) -> receive {Pid, grading_start} -> NewGraderList = [Pid | GraderList], graderloop(NewGraderList); {Pid, grading_finish} -> graderloop(lists:delete(Pid, GraderList)); {From, check_finished} -> case GraderList of [] -> From ! all_finished; _ -> graderloop(GraderList) end end. check_no_grading([]) -> ok; check_no_grading([G|Gs]) -> From = self(), G ! {From, check_finished}, receive all_finished -> check_no_grading(Gs) end.