Daniel@0: /* Part of DML (Digital Music Laboratory) Daniel@0: Copyright 2014-2015 Samer Abdallah, University of London Daniel@0: Daniel@0: This program is free software; you can redistribute it and/or Daniel@0: modify it under the terms of the GNU General Public License Daniel@0: as published by the Free Software Foundation; either version 2 Daniel@0: of the License, or (at your option) any later version. Daniel@0: Daniel@0: This program is distributed in the hope that it will be useful, Daniel@0: but WITHOUT ANY WARRANTY; without even the implied warranty of Daniel@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Daniel@0: GNU General Public License for more details. Daniel@0: Daniel@0: You should have received a copy of the GNU General Public Daniel@0: License along with this library; if not, write to the Free Software Daniel@0: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Daniel@0: */ Daniel@0: Daniel@0: :- module(memo_ui, []). Daniel@0: Daniel@0: /** UI for viewing memoised functions Daniel@0: */ Daniel@0: :- use_module(library(http/html_write)). Daniel@0: :- use_module(library(http/html_head)). Daniel@0: :- use_module(library(http/http_dispatch)). Daniel@0: :- use_module(library(http/http_parameters)). Daniel@0: :- use_module(library(decoration)). Daniel@0: :- use_module(library(htmlutils)). Daniel@0: :- use_module(library(memo)). Daniel@0: :- use_module(library(async)). Daniel@0: :- use_module(library(listutils)). Daniel@0: :- use_module(library(httpfiles)). Daniel@0: :- use_module(components(table)). Daniel@0: :- use_module(components(icons)). Daniel@0: :- use_module(cliopatria(hooks)). Daniel@0: Daniel@0: :- set_prolog_flag(double_quotes,string). Daniel@0: Daniel@0: :- http_handler(root(dml/memo/view), memo_view, []). Daniel@0: :- http_handler(root(dml/jobs/view), job_view, []). Daniel@0: :- http_handler(root(dml/jobs/cancel), cancel_job, []). Daniel@0: Daniel@0: decoration:resource_view(URI,_) --> Daniel@0: { rdf(URI,rdf:type,memo:'Function'), !, Daniel@0: uripattern:pattern_uri(\func(Mod,Pred,Arity),URI), Daniel@0: format(string(Name),"~w:~w/~d",[Mod,Pred,Arity]), Daniel@0: http_link_to_id(pldoc_object,[object=Name],DocLink), Daniel@0: http_link_to_id(memo_view,[uri(URI)],ViewURL) Daniel@0: }, Daniel@0: html_requires(font_awesome), Daniel@0: html( [ a(href=ViewURL, [\icon(table)," View"]), ' ' Daniel@0: , a(href=DocLink, [\icon(book)," Documentation"]) Daniel@0: ]). Daniel@0: Daniel@0: decoration:resource_decoration(URI,Link) --> Daniel@0: { rdf(URI,rdf:type,memo:'Function'), !, Daniel@0: % uripattern:pattern_uri(dml: \func(Mod,Pred,Arity),URI), Daniel@0: % format(string(Name),"~w:~w/~d",[Mod,Pred,Arity]), Daniel@0: % http_link_to_id(pldoc_object,[object=Name],DocLink), Daniel@0: http_link_to_id(memo_view,[uri=URI],ViewLink) Daniel@0: }, Daniel@0: html_requires(font_awesome), Daniel@0: html( span( [ a(href(ViewLink),\icon(table)), &(nbsp) Daniel@0: % , a(href(DocLink),\icon(book)), &(nbsp) Daniel@0: , \Link ])). Daniel@0: Daniel@0: memo_view(Request) :- Daniel@0: http_parameters(Request, Daniel@0: [ uri(URI, [ optional(false), description("URI of CSV file")]) Daniel@0: , page(Page, [ nonneg, default(1) ]) Daniel@0: , limit(Limit, [ nonneg, default(50) ]) Daniel@0: ]), Daniel@0: format(string(FullTitle),"View for ~w",[URI]), Daniel@0: uripattern:pattern_uri(\func(Mod,Pred,Arity),URI), Daniel@0: length(Args,Arity), Daniel@0: Head =.. [Pred|Args], Daniel@0: aggregate_all(count,browse(Mod:Head),Total), Daniel@0: ( Total=0 Daniel@0: -> Content=p("This function has no successful memoised computations.") Daniel@0: ; Offset is Limit*(Page-1), Daniel@0: Pages is ceil(Total/Limit), Daniel@0: insist(Page= Rows1=Rows2; take(Limit,Rows1,Rows2)), Daniel@0: Content = [ \paginator(memo_view-[uri(URI),limit(Limit)],Page,Pages) Daniel@0: , \table_from_goal(goal_row(limit(Limit,offset(Offset,browse(Mod:Head))),Args),[]) Daniel@0: , \paginator(memo_view-[uri(URI),limit(Limit)],Page,Pages) Daniel@0: ] Daniel@0: ), Daniel@0: reply_html_page(cliopatria(demo), [title(FullTitle)], [ h1(FullTitle) | Content ], [unstable]). Daniel@0: Daniel@0: cancel_job(Request) :- Daniel@0: insist(user_db:logged_on(_),not_authorised(cancel_job)), Daniel@0: http_parameters(Request, Daniel@0: [ pool(Pool, [ atom, optional(false) ]) Daniel@0: , id(Id, [ atom, optional(false) ]) Daniel@0: , return_to(ReturnTo, [ atom, default(_) ]) Daniel@0: ]), Daniel@0: async_cancel(Pool,Id), % !!! this could throw an error Daniel@0: (var(ReturnTo) -> member(referer(ReturnTo),Request); true), Daniel@0: debug(async,'Cancelling job ~w:~w, returning to ~w',[Pool,Id,ReturnTo]), Daniel@0: http_redirect(see_other,ReturnTo,Request). Daniel@0: Daniel@0: job_view(Request) :- Daniel@0: http_parameters(Request, [pool(Pool, [atom, default(vis_cla)])]), Daniel@0: format(string(FullTitle),"Current jobs in thread pool ~w",[Pool]), Daniel@0: findall(job(Goal,ID,TSub,Status),async_current_job(Pool,Goal,ID,TSub,Status),Jobs), Daniel@0: reply_html_page(cliopatria(demo), [title(FullTitle)], Daniel@0: [h1(FullTitle), \job_tables(Pool,Jobs)], Daniel@0: [unstable]). Daniel@0: Daniel@0: job_tables(Pool,Jobs) --> Daniel@0: {partition(is_waiting, Jobs, Waiting, Running)}, Daniel@0: ( {Running=[]} -> html(h3("No jobs running.")) Daniel@0: ; html( [ h3("Running jobs") Daniel@0: , \table_from_goal(running_job(Pool,Running), Daniel@0: [headings(['Started','Elapsed','Progress','Goal','Actions'])]) Daniel@0: ]) Daniel@0: ), Daniel@0: ( {Waiting=[]} -> html(h3("No jobs waiting.")) Daniel@0: ; html( [ h3("Waiting jobs") Daniel@0: , \table_from_goal(waiting_job(Pool,Waiting), Daniel@0: [headings(['Position','Submitted','Goal','Actions'])]) Daniel@0: ]) Daniel@0: ). Daniel@0: Daniel@0: Daniel@0: is_waiting(job(_,_,_,waiting(_))). Daniel@0: Daniel@0: waiting_job(Pool,Jobs,[Pos,TSubmit,GoalCell,a(href(Cancel),cancel)]) :- Daniel@0: member(job(Goal,ID,TSubmit,waiting(Pos)),Jobs), % !!! add submit time to table? Daniel@0: http_link_to_id(cancel_job,[pool=Pool,id=ID],Cancel), Daniel@0: goal_cell(Goal,GoalCell). Daniel@0: running_job(Pool,Jobs,[StartS,ElapsedS,ProgressS,GoalCell,a(href(Cancel),cancel)]) :- Daniel@0: member(job(Goal,ID,_TSubmit,running(Thread,Start)),Jobs), Daniel@0: http_link_to_id(cancel_job,[pool=Pool,id=ID],Cancel), Daniel@0: format_time(string(StartS),'%FT%T%:z',Start), Daniel@0: debug(async,'Getting running job progress from thread ~w...',[Thread]), Daniel@0: catch(( async_thread_progress(Thread,[summary],TNow,[Progress]), Daniel@0: progress_string(Progress,ProgressS), Daniel@0: Elapsed is TNow-Start, format(string(ElapsedS),'~2f',[Elapsed]) Daniel@0: ), Ex, ( debug(async,'Failed to get job progress ~q',[Ex]), Daniel@0: ElapsedS="N/A", ProgressS="ENDED")), Daniel@0: copy_term(Goal,Goal1), Daniel@0: numbervars(Goal1,0,_), Daniel@0: goal_cell(Goal1,GoalCell). Daniel@0: Daniel@0: goal_cell(Mod:Head,a(href(URL),GoalS)) :- Daniel@0: functor(Head,Pred,Arity), Daniel@0: uripattern:pattern_uri(\func(Mod,Pred,Arity),URI), Daniel@0: http_link_to_id(list_resource,[r(URI)],URL), Daniel@0: term_cell(Mod:Head,GoalS). Daniel@0: Daniel@0: progress_string(Progress,String) :- memberchk(stepwise(_Desc,Done/Total), Progress), !, format(string(String),'~d/~d',[Done,Total]). Daniel@0: progress_string(_Terms, "-"). Daniel@0: Daniel@0: goal_row(Pred,Vals,Cells) :- call(Pred), maplist(term_cell,Vals,Cells). Daniel@0: term_cell(Term,Cell) :- Daniel@0: with_output_to(string(Cell),write_term(Term,[quoted(true),max_depth(6),numbervars(true)])). Daniel@0: