:- module(yaz_tag_garden, [ http_yaz_tag_garden/1 ]). :- use_module(library(http/http_dispatch)). :- use_module(library(http/http_parameters)). :- use_module(library(http/http_path)). :- use_module(library(http/html_write)). :- use_module(library(http/html_head)). :- use_module(library(http/http_json)). :- use_module(library(http/js_write)). :- use_module(library(http/json)). :- use_module(library(http/json_convert)). :- use_module(library(http/http_session)). :- use_module(user(user_db)). :- use_module(library(semweb/rdf_db)). :- use_module(library(semweb/rdfs)). :- use_module(library(semweb/rdf_label)). :- use_module(library(yaz_util)). :- use_module(library(yui3)). :- use_module(library(user_process)). :- use_module(library(video_annotation)). :- use_module(library(tag_match)). %:- use_module(library(reconcile)). :- use_module(components(label)). :- use_module(components(yaz_page)). :- use_module(api(video_frames)). :- setting(reconcile_server, atom, 'http://standard-reconcile.freebaseapps.com/reconcile', 'Url of a reconcile server, use "local" for built-in service of this server'). :- http_handler(yaz(taggarden), http_yaz_tag_garden, []). :- http_handler(yaz('data/frames'), http_data_frames, []). http_yaz_tag_garden(Request) :- ensure_logged_on(User0), user_property(User0, url(CurrentUser)), http_parameters(Request, [ video(Video, [description('Current video')]), process(Process, [optional(true), desription('When set only annotations within this process are shown')]), user(User, [optional(true), description('When set only annotations created by this user are shown')]), interval(Interval, [default(0), number, description('When set one entry per tag is returned in interval (in milliseconds)')]), confirmed(Confirmed, [boolean, default(false), description('When true only tags that are entered by >1 user are shown')]), limit(_Limit, [default(1000), number, description('limit number of tags shown')]) ]), Options = [process(Process), user(User), interval(Interval), confirmed(Confirmed) ], create_user_process(CurrentUser, [rdf:type=pprime:'TagGarden', opmv:used=Video ], _Process), video_annotations(Video, Annotations, Options), group_annotations(Annotations, AnnotationGroups), html_garden_page(Video, AnnotationGroups, Options). group_annotations(Annotations, AnnotationGroups) :- group_by_arg(Annotations, 1, Groups0), maplist(group_to_annotation, Groups0, Groups1), sort_by_arg(Groups1, 2, Groups), reverse(Groups, AnnotationGroups). group_to_annotation(Value-Annotations, annotation(Value, Count)) :- length(Annotations, Count). %% http_data_frames(+Request) % % Emit a JSON object with all frames for a given tag and video. http_data_frames(Request) :- http_parameters(Request, [ video(Video, [description('Current video')]), process(Process, [optional(true), desription('When set only annotations within this process are shown')]), user(User, [optional(true), description('When set only annotations created by this user are shown')]), limit(_Limit, [default(1000), number, description('limit number of tags shown')]), tag(Tag, [jsonresource, description('Tag assinged to the video')]) ]), Options = [process(Process), user(User) ], tag_term_label(Tag, _, Label), Obj = Time-json([tag=Label, entry=Id, time=Time]), findall(Obj, video_annotation(Video, Id, Tag, Time, Options), TagEntries0), keysort(TagEntries0, TagEntries), pairs_values(TagEntries, Fragments), reply_json(json([fragments=Fragments])). /******************************* * HTML * *******************************/ %% html_garden_page(+Video, +Annotations, +Options) % % Emit an HTML page for tag gardening. html_garden_page(Video, Annotations, Options) :- reply_html_page(yaz, [ title(['YAZ - ', Video]) ], [ \html_requires(css('garden.css')), div(class('yui3-skin-sam'), \html_page_layout(Video)), script(type('text/javascript'), \html_page_yui(Video, Annotations, Options)) ]). html_page_layout(Video) --> { display_label(Video, Title) }, html([ h2(Title), div(id(tags), [ %h4('Tags'), div(id(taglist), []) ]), div(id(main), [ div([id(concepts), class(hidden)], [ div(id(tagReconcile), []) ]), %h4('Fragments'), div(id(frames), []) ]), div([id(suggest), class(hidden)], [ input([type(text), id(taginput)]), div(id(suggestResults), []) ]) ]). /******************************* * JS * *******************************/ %% html_page_yui(+Video, +Annotations, +Options) % % Emit JavaScript for the tag gardening page. html_page_yui(Video, Annotations, _Options) --> { video_source(Video, Src, Duration), http_absolute_location(js('videoplayer/'), PlayerPath, []), http_absolute_location(js('videoplayer/videoplayer.js'), VideoPlayer, []), http_absolute_location(js('tagcarousel/tagcarousel.js'), TagCarousel, []), http_absolute_location(js('videoframes/videoframes.js'), VideoFrames, []), http_absolute_location(js('timeline/timeline.js'), Timeline, []), http_location_by_id(serve_video_frame, FrameServer), http_location_by_id(http_data_frames, DataServer), ( setting(reconcile_server, local) -> http_location_by_id(http_reconcile, ReconcileServer) ; setting(reconcile_server, ReconcileServer) ), annotation_to_json(Annotations, JSONTags) }, html_requires(js('videoplayer/swfobject.js')), js_yui3([{modules:{'video-player':{fullpath:VideoPlayer}, 'tag-carousel':{fullpath:TagCarousel}, 'video-frames':{fullpath:VideoFrames}, 'timeline':{fullpath:Timeline} }} ], [node,event,widget,anim,slider,'align-plugin', 'querystring-stringify-simple','io','jsonp','json', 'video-player','tag-carousel','timeline', 'video-frames' ], [\js_new(tagList, 'Y.mazzle.TagCarousel'({topIndent:symbol(false), tags:JSONTags, height:500, width:200, edit:true, suggest:symbol('Y.one("#suggest")') })), \js_new(videoFrames, 'Y.mazzle.VideoFrames'({frameServer:FrameServer, dataServer:DataServer, video:Src, duration:Duration, playerPath:PlayerPath })), 'Y.one("#suggest").plug(Y.Plugin.Align);\n', \js_call('tagList.render'('#taglist')), \js_call('videoFrames.render'('#frames')), \js_yui3_on(tagList, itemSelect, \js_tag_select(Video)), \js_yui3_on(videoFrames, confirmSelect, \js_confirm_select), %\js_call('videoFrames.fetchData'), \js_freebase_suggest ]). js_tag_select(Video) --> js_function([e], [ ' var tag = Y.JSON.stringify(e.tag.tag); videoFrames.set("confirm", true); videoFrames.fetchData({video:"',Video,'", tag:tag});\n' ]). js_concept_select --> js_function([e], [ 'videoFrames.set("concept", e.concept);\n' ]). js_confirm_select --> { http_location_by_id(http_confirm_fragment, Path) }, js_function([e], [ 'var f = e.frame; var tag = e.concept ? e.concept.name : f.tag; var uri = e.concept ? e.concept.id : null;\n', \js_call('Y.io'(Path, {data: {type:symbol('e.type'), video:symbol('f.video'), starttime:symbol('f.time'), tag:symbol(tag), uri:symbol(uri)} })) ]). js_freebase_suggest --> html_requires('http://freebaselibs.com/static/suggest/1.2.1/suggest.min.css'), html_requires(js('jquery/jquery-1.4.2.min.js')), html_requires('http://freebaselibs.com/static/suggest/1.2.1/suggest.min.js'), html(\[ '$("#taginput").suggest({parent:"#suggestResults", soft:true, required:true, flyout:true})\n' ]). :- http_handler(yaz(confirm), http_confirm_fragment, []). %% http_confirm_fragment(+Request) % % Handler for confirmation of video fragment http_confirm_fragment(Request) :- http_in_session(SessionID), logged_on(User, SessionID), http_parameters(Request, [ video(Video, [description('URL of video')]), starttime(StartTime, [number, description('StartTime of fragment')]), endtime(EndTime, [number, optional(true), description('EndTime of fragment')]), type(RoleType, [optional(true), description('Role of the tag')]), tag(Tag, [description('Tag being confirmed')]), uri(URI, [optional(true), description('URI of confirmed concept')]) ]), ( var(EndTime0) -> EndTime is StartTime + 10000 ; EndTime = EndTime0 ), rdf_transaction(assert_confirm_event(Video, StartTime, EndTime, Tag, URI, RoleType, User)), reply_json(json([success='confirmation saved', user=User, video=Video, starttime=StartTime, endtime=EndTime, tag=Tag, uri=URI ])). assert_confirm_event(Video, StartTime, EndTime, Tag, URI, RoleType, User) :- rdf_bnode(CE), rdf_assert(CE, rdf:type, pprime:'ConfirmEvent', User), rdf_assert(CE, sem:involves, Video, User), rdf_assert(CE, sem:hasActor, User, User), rdf_bnode(FA), rdf_assert(FA, rdf:type, pprime:'FragmentAnnotation', User), rdf_assert(FA, sem:subEventOf, CE, User), rdf_assert(FA, sem:hasBeginTimeStamp, literal(StartTime), User), rdf_assert(FA, sem:hasEndTimeStamp, literal(EndTime), User), rdf_assert(FA, sem:involves, literal(Tag), User), ( var(URI); URI==null -> URI = null ; rdf_assert(FA, sem:involves, URI, User) ), ( var(Type) -> Type = null ; rdf_assert(FA, pprime:role, RoleType, User) ).