:- module(yaz_util, [ find_unique/4, % +Var, +Goal, +Max, -Results list_offset/3, list_limit/4, pairs_sort_by_value_count/2, pairs_sort_by_value_sum/2, arg_key/3, sort_by_arg/3, sort_by_arg/4, sort_by_arg_count/4, group_by_arg/3, display_label/2, tag_label/2, tag_term_label/3, literal_to_number/2, is_annotation_event/1, game_time/3, tag_entry_time/2, iri_to_url/2, delete_nonground/2, video_source/2, video_source/3, annotation_to_json/2, video_desc/2, display_time/2 ]). :- use_module(library(semweb/rdf_db)). :- use_module(library(semweb/rdfs)). :- use_module(library(semweb/rdf_label)). :- use_module(library(http/http_dispatch)). :- use_module(library(http/json)). :- use_module(library(http/json_convert)). :- use_module(library(http/http_path)). :- rdf_meta is_annotation_event(r), annotation_class(r), %label_property(r), display_label(r, -). :- meta_predicate find_unique(-, 0, +, -). %% find_unique(Var, :Goal, +MaxResults, -SortedSet) % % Find at most MaxResults distinct solutions for Var in Goal. find_unique(T, G, inf, Ts) :- !, findall(T, G, Raw), sort(Raw, Ts). find_unique(T, G, Max, Ts) :- empty_nb_set(Set), State = count(0), ( G, add_nb_set(T, Set, true), arg(1, State, C0), C is C0 + 1, nb_setarg(1, State, C), C == Max -> true ; true ), nb_set_to_list(Set, Ts). %% list_offset(+List, +N, -SmallerList) % % SmallerList starts at the nth element of List. list_offset(L, N, []) :- length(L, Length), Length < N, !. list_offset(L, N, L1) :- list_offset_(L, N, L1). list_offset_(L, 0, L) :- !. list_offset_([_|T], N, Rest) :- N1 is N-1, list_offset_(T, N1, Rest). %% list_limit(+List, +N, -SmallerList, -Rest) % % SmallerList ends at the nth element of List. list_limit(L, N, L, []) :- N < 0, !. list_limit(L, N, L, []) :- length(L, Length), Length < N, !. list_limit(L, N, L1, Rest) :- list_limit_(L, N, L1, Rest). list_limit_(Rest, 0, [], Rest) :- !. list_limit_([H|T], N, [H|T1], Rest) :- N1 is N-1, list_limit_(T, N1, T1, Rest). %% pairs_sort_by_value_count(+Pairs:key-list, %% -Sorted:listcount-key) % % Sorted is a list with the keys of Pairs sorted by the number of % elements in the value list. pairs_sort_by_value_count(Grouped, Sorted) :- pairs_value_count(Grouped, Counted), keysort(Counted, Sorted0), reverse(Sorted0, Sorted). pairs_value_count([], []). pairs_value_count([Key-Values|T], [Count-Key|Rest]) :- length(Values, Count), pairs_value_count(T, Rest). %% pairs_sort_by_value_sum(+Pairs:key-list, %% -Sorted:listcount-key) % % Sorted is a list with the keys of Pairs sorted by the sum % of the elements in value list. pairs_sort_by_value_sum(Grouped, Sorted) :- pairs_value_sum(Grouped, Counted), keysort(Counted, Sorted0), reverse(Sorted0, Sorted). pairs_value_sum([], []). pairs_value_sum([Key-Values|T], [Count-Key|Rest]) :- sumlist(Values, Sum), length(Values, Length), Count is Sum/Length, pairs_value_sum(T, Rest). %% sort_by_arg(+ListOfTerms, +Arg, -SortedList) % % SortedList contains the Terms from ListOfTerms sorted by their % nth Arg. sort_by_arg(List, Arg, Sorted) :- maplist(arg_key(Arg), List, Pairs), keysort(Pairs, SortedPairs), pairs_values(SortedPairs, Sorted). %% sort_by_arg(+ListOfTerms, +Arg, +Direction, -SortedList) % % SortedList contains the Terms from ListOfTerms sorted by their % nth Arg. sort_by_arg(List, Arg, Direction, Sorted) :- sort_by_arg(List, Arg, Sorted0), ( Direction == desc -> reverse(Sorted0, Sorted) ; Sorted = Sorted0 ). sort_by_arg_count(List, Arg, Sorted, Direction) :- maplist(arg_key_count(Arg), List, Pairs), keysort(Pairs, SortedPairs), pairs_values(SortedPairs, Sorted0), ( Direction == desc -> reverse(Sorted0, Sorted) ; Sorted = Sorted0 ). %% group_by_arg(+ListOfTerms, +Arg, -GroupedList) % % GroupedList contains the Terms from ListOfTerms grouped by their % nth Arg. group_by_arg(List, Arg, Sorted) :- maplist(arg_key(Arg), List, Pairs), keysort(Pairs, SortedPairs), group_pairs_by_key(SortedPairs, Sorted). arg_key(Args, Term, Keys-Term) :- is_list(Args), !, args(Args, Term, Keys). arg_key(Arg, Term, Key-Term) :- arg(Arg, Term, Key). arg_key_count(Arg, Term, Key-Term) :- arg(Arg, Term, List), is_list(List), length(List, Key). args([A], Term, [Key]) :- !, arg(A, Term, Key). args([A|As], Term, [Key|Ks]) :- arg(A, Term, Key), args(As, Term, Ks). /* :- multifile label_property/1. % ?Resource %% label_property(?Property) is nondet. % % True if the value of Property can be used to (non-uniquely) % describe an object to the user. label_property(P) :- cliopatria:label_property(P). label_property(skos:prefLabel). label_property(dc:title). label_property(skos:altLabel). label_property(rdfs:label). */ %% display_label(+Resource, -Txt) % % Txt is a label of Resource suited for display. display_label(R, Label) :- label_property(P), rdf_has(R, P, Lit), !, literal_text(Lit, Label). display_label(R, Label) :- rdfs_label(R, Label). %% literal_to_number(+Literal, ?Number) % % Conversion between RDF literal and numbers. literal_to_number(Literal, Number) :- literal_text(Literal, Txt), ( number(Txt) -> Number = Txt ; atom_number(Txt, Number) ). %% tag_label(?Term, ?Label) % % Label is the label of Term. tag_label(Term, Label) :- rdf(Term, rdfs:label, Literal), literal_text(Literal, Label). %% tag_term_label(+Tag, -Term, -Label) % % Term is the URI of a Tag, or the label in case of a literal. tag_term_label(Tag, Term, Label) :- ( Tag = literal(_) -> literal_text(Tag, Label), Term = Label % hmm do we want this, do language tags matter? ; rdf(Tag, rdf:type, pprime:'Term') -> tag_label(Tag, Label), Term = '' ; Term = Tag, display_label(Tag, Label) ). %% is_annotation_event(+Resource) % % True if Resource is of type that we consider an annotation event is_annotation_event(R) :- rdf(R, rdf:type, Class), annotation_class(Class), !. annotation_class(pprime:'Game'). annotation_class(pprime:'AnnotationSession'). %% game_time(?Game, ?StartTime, ?EndTime) % % True if StartTime is the value of sem:beginsAt and EndTime the % value of sem:endsAt. game_time(Game, StartTime, EndTime) :- rdf(Game, opmv:wasStartedAt, Start), literal_text(Start, StartTime), ( rdf(Game, sem:wasEndedAt, End) -> literal_text(End, EndTime) ; EndTime = 0 ). %% tagentry_entry_time(?EntryId, ?Time) % % Time is the time of tag entry. tag_entry_time(TagEntry, Time) :- rdf(TagEntry, pprime:videoPlayhead, Time0), literal_to_number(Time0, Time). %% iri_to_url(+IRI, -URL) is det. % % Translate a IRI (using arbitrary unicode characters) into an % URL that can be handled to http_open/3, etc. % % @tbd Very incomplete. We must map other forbidden characters % and do UTF-8 and % encoding for anything >= 128. iri_to_url(IRI, URL) :- atom_codes(IRI, Codes), phrase(iri_to_url(Codes), URLCodes), atom_codes(URL, URLCodes). iri_to_url([]) --> []. iri_to_url([H|T]) --> map_code(H), !, iri_to_url(T). iri_to_url([H|T]) --> [H], iri_to_url(T). map_code(32) --> "%20". % Space map_code(43) --> "%20". % + %% delete_nonground(+ListIn, -ListOut) % % Remove nonground terms from List. delete_nonground([], []). delete_nonground([H|T], [H|Rest]) :- ground(H), !, delete_nonground(T, Rest). delete_nonground([_H|T], Rest) :- delete_nonground(T, Rest). %% video_source(+URL, -Video) % % True if Video is a pprime:video of URL. % Otherwise URL might be a Video??? video_source(URL, Video, Duration) :- video_source(URL, Video), ( rdf(URL, pprime:duration, literal(Duration0)) -> ( number(Duration0) -> Duration1 = Duration0 ; atom_number(Duration0, Duration1) ), Duration is Duration1*1000 ; Duration = 0 ). video_source(URL, Video) :- rdf_has(URL, pprime:source, Video), !. video_source(URL, FragmentId) :- rdf_has(URL, dc:id, literal(FragmentId)), !. video_source(URL, URL). %% annotation_to_json(+AnnotationList, -JSONTerm) % % Convert between a prolog list of annotation terms and a JSON % term. annotation_to_json(As, JSON) :- prolog_to_json(As, JSON). http:convert_parameter(jsonresource, Atom, Term) :- atom_json_term(Atom, JSON, []), json_to_prolog(JSON, Term). :- json_object uri(value:uri) + [type=uri], uri(value:uri, label:_) + [type=uri], literal(lang:atom, value:_) + [type=literal], literal(type:atom, value:_) + [type=literal], literal(value:_) + [type=literal], i(uri:atom, time:number), entry(entry:atom, tag:_, startTime:number), annotation(tag:_, annotations:list), annotation(tag:_, count:number), annotation(tag:_, tags:list, count:number), annotation(tag:_, startTime:number, endTime:number, annotations:list), annotation(tag:_, startTime:number, endTime:number, annotations:list, score:number), annotation(tag:_, startTime:number, endTime:number, annotations:list, score:number, match:_), video_annotation(tag:_, video:atom, startTime:number, endTime:number, annotations:list), match(type:atom, score:_), match(type:atom, score:_, uri:_, value:_), concept(uri:_, label:atom, altlabel:atom, desc:atom). video_desc(VideoURL, Desc) :- rdf_has(VideoURL, pprime:description, Lit), literal_text(Lit, Desc), !. video_desc(_, ''). display_time(Time, FormattedTime) :- parse_time(Time, Stamp), !, format_time(string(FormattedTime), '%d %B %H:%M', Stamp). display_time(Stamp, FormattedTime) :- catch(format_time(string(FormattedTime), '%d %B %H:%M', Stamp), _, fail), !. display_time(Time, Time).