29
30:- module(cpa_browse,
31 [ graph_info//1, 32 graph_as_resource//2, 33 graph_actions//1, 34 list_resource//2, 35 context_graph//2 36 ]).
37:- use_module(library(http/http_dispatch)).
38:- use_module(library(http/http_parameters)).
39:- use_module(library(http/html_write)).
40:- use_module(library(http/js_write)).
41:- use_module(library(http/html_head)).
42:- use_module(library(http/http_wrapper)).
43:- use_module(library(http/yui_resources)).
44:- use_module(library(http/http_path)).
45:- use_module(library(http/cp_jquery)).
46
47:- use_module(library(semweb/rdf_db)).
48:- use_module(library(semweb/rdfs)).
49:- use_module(library(semweb/rdf_litindex)).
50:- use_module(library(semweb/rdf_persistency)).
51
52:- use_module(library(aggregate)).
53:- use_module(library(lists)).
54:- use_module(library(pairs)).
55:- use_module(library(debug)).
56:- use_module(library(option)).
57:- use_module(library(apply)).
58
59:- use_module(components(label)).
60:- use_module(components(simple_search)).
61:- use_module(components(graphviz)).
62:- use_module(components(basics)).
63:- use_module(api(lod_crawler)).
64:- use_module(api(sesame)).
65:- use_module(library(semweb/rdf_abstract)).
66:- use_module(library(semweb/rdf_label)).
67
68:- use_module(user(user_db)).
69
70
84
85
86 89
90:- http_handler(rdf_browser(.),
91 http_404([index(list_graphs)]),
92 [spawn(cliopatria), prefix]).
93:- http_handler(rdf_browser(list_graphs), list_graphs, []).
94:- http_handler(rdf_browser(list_graph), list_graph, []).
95:- http_handler(rdf_browser(list_classes), list_classes, []).
96:- http_handler(rdf_browser(list_instances), list_instances, []).
97:- http_handler(rdf_browser(list_predicates), list_predicates, []).
98:- http_handler(rdf_browser(list_predicate_resources),
99 list_predicate_resources, []).
100:- http_handler(rdf_browser(list_resource), list_resource, []).
101:- http_handler(rdf_browser(list_triples), list_triples, []).
102:- http_handler(rdf_browser(list_triples_with_object),
103 list_triples_with_object, []).
104:- http_handler(rdf_browser(list_triples_with_literal),
105 list_triples_with_literal, []).
106
107:- http_handler(rdf_browser(list_prefixes), list_prefixes, []).
108:- http_handler(rdf_browser(search), search, []).
109:- http_handler(rdf_browser(multigraph_action), multigraph_action,
110 [ time_limit(infinite) ]).
111
112
113:- meta_predicate
114 table_rows(3, +, ?, ?),
115 table_rows_top_bottom(3, +, +, +, ?, ?),
116 html_property_table(?, 0, ?, ?).
117
122
123list_graphs(_Request) :-
124 findall(Count-Graph,
125 ( rdf_graph(Graph),
126 graph_triples(Graph, Count)
127 ),
128 Pairs),
129 keysort(Pairs, Sorted),
130 pairs_values(Sorted, UpCount),
131 reverse(UpCount, DownCount),
132 append(DownCount, [virtual(total)], Rows),
133 reply_html_page(cliopatria(default),
134 title('RDF Graphs'),
135 [ h1('Named graphs in the RDF store'),
136 \warn_volatile,
137 \graph_table(Rows, [])
138 ]).
139
140:- if(current_predicate(rdf_persistency_property/1)).
141warn_volatile -->
142 { rdf_persistency_property(access(read_only)), !,
143 rdf_persistency_property(directory(Dir))
144 },
145 html(div(class(msg_warning),
146 [ 'WARNING: The persistent store ', code(Dir), ' was loaded in ',
147 b('read-only'), ' mode. All changes will be lost when ',
148 'the server is stopped.'
149 ])).
150:- endif.
151warn_volatile --> [].
152
153:- if((rdf_version(V),V>=30000)).
154graph_triples(Graph, Count) :-
155 rdf_statistics(triples_by_graph(Graph, Count)).
156:- else.
157graph_triples(Graph, Count) :- 158 rdf_statistics(triples_by_file(Graph, Count)).
159:- endif.
160
161graph_table(Graphs, Options) -->
162 { option(top_max(TopMax), Options, 500),
163 option(top_max(BottomMax), Options, 500),
164 http_link_to_id(multigraph_action, [], Action),
165 graph_actions(Options, ActionOptions)
166 },
167 html_requires(css('rdf.css')),
168 html(form([ action(Action),
169 class('graph-table')
170 ],
171 [ table(class(block),
172 [ \graph_table_header
173 | \table_rows_top_bottom(
174 graph_row(ActionOptions), Graphs,
175 TopMax, BottomMax)
176 ]),
177 \multigraph_actions(ActionOptions)
178 ])),
179 mgraph_action_script.
180
-->
182 html(tr([ th('RDF Graph'),
183 th('Triples'),
184 th('Modified'),
185 th('Persistency')
186 ])).
187
188graph_row(_, virtual(total)) --> !,
189 { rdf_statistics(triples(Count))
190 },
191 html([ th(class(total), 'Total #triples:'),
192 \nc('~D', Count, [class(total)]),
193 td([],[]), 194 td([],[]) 195 ]).
196graph_row(Options, Graph) -->
197 { graph_triples(Graph, Count)
198
199 },
200 html([ td(\graph_link(Graph)),
201 \nc('~D', Count),
202 \modified(Graph),
203 td(style('text-align:center'), \persistency(Graph)),
204 \graph_checkbox(Graph, Options)
205 ]).
206
207modified(Graph) -->
208 { rdf_graph_property(Graph, source_last_modified(Time)),
209 format_time(string(Modified), '%+', Time), !
210 },
211 html(td([class('file-time')], Modified)).
212modified(Graph) -->
213 { rdf_journal_file(Graph, File),
214 time_file(File, Time),
215 format_time(string(Modified), '%+', Time)
216 },
217 html(td([class('file-time')], Modified)).
218modified(_Graph) -->
219 html(td([class('file-time')], '')).
220
221graph_link(Graph) -->
222 { http_link_to_id(list_graph, [graph=Graph], URI)
223 },
224 html(a(href(URI), Graph)).
225
226persistency(Graph) -->
227 { rdf_graph_property(Graph, persistent(true)) }, !,
228 snapshot(Graph),
229 journal(Graph).
230persistency(_) -->
231 { http_absolute_location(icons('volatile.png'), Img, [])
232 },
233 html(img([ class('in-text'),
234 title('Graph is not persistent'),
235 src(Img)
236 ])).
237
238snapshot(Graph) -->
239 { rdf_snapshot_file(Graph, _),
240 http_absolute_location(icons('snapshot.png'), Img, [])
241 },
242 html(img([ class('in-text'),
243 title('Graph has persistent snapshot'),
244 src(Img)
245 ])).
246snapshot(_) --> [].
247
248journal(Graph) -->
249 { rdf_journal_file(Graph, _),
250 http_absolute_location(icons('journal.png'), Img, [])
251 },
252 html(img([ class('in-text'),
253 title('Graph has a journal'),
254 src(Img)
255 ])).
256journal(_) --> [].
257
262
263graph_actions(Options, [show_actions(true)|Options]) :-
264 logged_on(User), !,
265 catch(check_permission(User, write(_, unload(user))), _, fail), !.
266graph_actions(Options, Options).
267
268graph_checkbox(Graph, Options) -->
269 { option(show_actions(true), Options) }, !,
270 html(td(class('no-border'),
271 input([type(checkbox),name(graph),value(Graph),
272 class('graph-select')]))).
273graph_checkbox(_, _) --> [].
274
275multigraph_actions(Options) -->
276 { option(show_actions(true), Options), !,
277 findall(Action-Format,
278 clause(graph_action(Action,Format,_), _),
279 Pairs)
280 },
281 html([ ul([ class('multi-graph-actions')
282 ],
283 \li_graph_actions(Pairs))
284 ]).
285multigraph_actions(_) --> [].
286
287li_graph_actions([]) --> [].
288li_graph_actions([H|T]) --> li_graph_action(H), li_graph_actions(T).
289
290li_graph_action(Action-Format) -->
291 { atomic_list_concat([Pre,Post], '~w', Format) },
292 html(li([ Pre,
293 input([ type(submit), name(action), value(Action) ]),
294 Post
295 ])).
296
297mgraph_action_script -->
298 html_requires(jquery),
299 js_script({|javascript||
300function showActions(time) {
301 if ( time === undefined ) time = 400;
302 var val = [];
303 $('.graph-table :checkbox:checked').each(function(i) {
304 val[i] = $(this).val();
305 });
306 if ( val.length == 0 )
307 $(".multi-graph-actions").hide(time);
308 else
309 $(".multi-graph-actions").show(time);
310}
311
312$(function() {
313 showActions(0);
314 $(".graph-table .graph-select").on('click', showActions);
315});
316 |}).
317
321
322multigraph_action(Request) :-
323 findall(Action, clause(graph_action(Action,_,_), _), Actions),
324 http_parameters(Request,
325 [ graph(Graphs, [list(atom)]),
326 action(Action, [oneof(Actions)])
327 ]),
328 clause(graph_action(Action,Format,_), _),
329 api_action(Request, multigraph_action(Action, Graphs), html,
330 Format-[Action]).
331
332multigraph_action(Action, Graphs) :-
333 forall(member(Graph, Graphs),
334 ( print_message(informational,
335 format('Processing ~w ...', [Graph])),
336 graph_action(Action, _, Graph))).
337
338graph_action('Delete', '~w selected graphs', Graph) :-
339 rdf_unload_graph(Graph).
340graph_action(volatile, 'Make selected graphs ~w', Graph) :-
341 rdf_persistency(Graph, false).
342graph_action(persistent, 'Make selected graphs ~w', Graph) :-
343 rdf_persistency(Graph, true).
344graph_action('Merge journals', '~w for selected graphs', Graph) :-
345 rdf_flush_journals([graph(Graph)]).
346
347
352
353list_graph(Request) :-
354 http_parameters(Request,
355 [ graph(Graph,
356 [description('Name of the graph to describe')])
357 ]),
358 ( rdf_graph(Graph)
359 -> true
360 ; http_404([], Request)
361 ),
362 reply_html_page(cliopatria(default),
363 title('RDF Graph ~w'-[Graph]),
364 [ h1('Summary information for graph "~w"'-[Graph]),
365 \simple_search_form([ id(ac_find_in_graph),
366 filter(graph(Graph)),
367 label('Search this graph')
368 ]),
369 \graph_info(Graph),
370 \graph_as_resource(Graph, []),
371 \graph_persistency(Graph),
372 \graph_actions(Graph),
373 \uri_info(Graph, Graph)
374 ]).
375
380
381graph_info(Graph) -->
382 html_property_table(row(P,V),
383 graph_property(Graph,P,V)).
384
385:- dynamic
386 graph_property_cache/3.
387
388graph_property(Graph, P, V) :-
389 graph_property_cache(Graph, MD5, Pairs),
390 rdf_md5(Graph, MD5), !,
391 member(P0-V, Pairs),
392 P =.. [P0,Graph].
393graph_property(Graph, P, V) :-
394 retractall(graph_property_cache(Graph, _, _)),
395 findall(P-V, graph_property_nc(Graph, P, V), Pairs),
396 rdf_md5(Graph, MD5),
397 assert(graph_property_cache(Graph, MD5, Pairs)),
398 member(P0-V, Pairs),
399 P =.. [P0,Graph].
400
401graph_property_nc(Graph, source, Source) :-
402 rdf_source(Graph, Source).
403graph_property_nc(Graph, triples, int(Triples)) :-
404 graph_triples(Graph, Triples).
405graph_property_nc(Graph, predicate_count, int(Count)) :-
406 aggregate_all(count, predicate_in_graph(Graph, _P), Count).
407graph_property_nc(Graph, subject_count, int(Count)) :-
408 aggregate_all(count, subject_in_graph(Graph, _P), Count).
409graph_property_nc(Graph, bnode_count, int(Count)) :-
410 aggregate_all(count, bnode_in_graph(Graph, _P), Count).
411graph_property_nc(Graph, type_count, int(Count)) :-
412 aggregate_all(count, type_in_graph(Graph, _P), Count).
413
414predicate_in_graph(Graph, P) :-
415 rdf_current_predicate(P),
416 once(rdf(_,P,_,Graph)).
417
424
425subject_in_graph(Graph, S) :-
426 graph_triples(Graph, Count),
427 rdf_statistics(triples(Total)),
428 Count * 10 > Total, !, 429 rdf_subject(S),
430 once(rdf(S, _, _, Graph)).
431subject_in_graph(Graph, S) :-
432 findall(S, rdf(S,_,_,Graph), List),
433 sort(List, Subjects),
434 member(S, Subjects).
435
436bnode_in_graph(Graph, S) :-
437 graph_triples(Graph, Count),
438 rdf_statistics(triples(Total)),
439 Count * 10 > Total, !,
440 rdf_subject(S),
441 rdf_is_bnode(S),
442 once(rdf(S, _, _, Graph)).
443bnode_in_graph(Graph, S) :-
444 findall(S, (rdf(S,_,_,Graph), rdf_is_bnode(S)), List),
445 sort(List, Subjects),
446 member(S, Subjects).
447
448
449
453
454:- thread_local
455 type_seen/1.
456
457type_in_graph(Graph, Class) :-
458 call_cleanup(type_in_graph2(Graph, Class),
459 retractall(type_seen(_))).
460
461type_in_graph2(Graph, Class) :-
462 subject_in_graph(Graph, S),
463 ( rdf_has(S, rdf:type, Class)
464 *-> true
465 ; rdf_equal(Class, rdfs:'Resource')
466 ),
467 ( type_seen(Class)
468 -> fail
469 ; assert(type_seen(Class))
470 ).
471
472
476
477graph_persistency(Graph) -->
478 { rdf_graph_property(Graph, persistent(true)),
479 ( rdf_journal_file(Graph, _)
480 ; rdf_snapshot_file(Graph, _)
481 )
482 }, !,
483 html([ h1('Persistency information'),
484 table(class(block),
485 [ tr([ td(class('no-border'),[]),
486 th('File'), th('Size'),th('Modified'),
487 td(class('no-border'),[])
488 ]),
489 \graph_shapshot(Graph),
490 \graph_journal(Graph)
491 ])
492 ]).
493graph_persistency(Graph) -->
494 { rdf_graph_property(Graph, persistent(true))
495 }, !,
496 html([ h1('Persistency information'),
497 p('The graph has no associated persistency files')
498 ]).
499graph_persistency(_Graph) -->
500 [].
501
502graph_shapshot(Graph) -->
503 { rdf_snapshot_file(Graph, File)
504 },
505 html(tr([ th(class('file-role'), 'Snapshot'),
506 \file_info(File)
507 ])).
508graph_shapshot(_) --> [].
509
510
511graph_journal(Graph) -->
512 { rdf_journal_file(Graph, File)
513 },
514 html(tr([ th(class('file-role'), 'Journal'),
515 \file_info(File),
516 \flush_journal_button(Graph)
517 ])).
518graph_journal(_) --> [].
519
520flush_journal_button(Graph) -->
521 { http_link_to_id(flush_journal, [], HREF)
522 },
523 html(td(class('no-border'),
524 form(action(HREF),
525 [ input([type(hidden), name(graph), value(Graph)]),
526 input([type(hidden), name(resultFormat), value(html)]),
527 input([type(submit), value('Merge journal')])
528 ]))).
529
530
531file_info(File) -->
532 { size_file(File, Size),
533 time_file(File, Time),
534 format_time(string(Modified), '%+', Time)
535 },
536 html([ td(class('file-name'), File),
537 td(class('int'), \n(human, Size)),
538 td(class('file-time'), Modified)
539 ]).
540
541
545
546graph_actions(Graph) -->
547 html([ h2('Actions'),
548 ul(class(graph_actions),
549 [ \li_export_graph(Graph, show),
550 \li_export_graph(Graph, download),
551 \li_schema_graph(Graph),
552 \li_delete_graph(Graph),
553 \li_persistent_graph(Graph)
554 ])
555 ]).
556
557li_delete_graph(Graph) -->
558 { logged_on(User),
559 catch(check_permission(User, write(_, unload(Graph))), _, fail), !,
560 http_link_to_id(unload_graph, [], Action)
561 },
562 html(li(form(action(Action),
563 [ input([type(hidden), name(graph), value(Graph)]),
564 input([type(hidden), name(resultFormat), value(html)]),
565 input([class(gaction), type(submit), value('Delete')]),
566 ' this graph'
567 ]))).
568li_delete_graph(_) --> [].
569
570li_persistent_graph(Graph) -->
571 { logged_on(User),
572 catch(check_permission(User, write(_, persistent(Graph))), _, fail), !,
573 http_link_to_id(modify_persistency, [], Action),
574 ( rdf_graph_property(Graph, persistent(true))
575 -> Op = (volatile), Value = off
576 ; Op = (persistent), Value = on
577 )
578 }, !,
579 html(li(form(action(Action),
580 [ input([type(hidden), name(graph), value(Graph)]),
581 input([type(hidden), name(resultFormat), value(html)]),
582 input([type(hidden), name(persistent), value(Value)]),
583 'Make this graph ',
584 input([class(gaction), type(submit), value(Op)])
585 ]))).
586li_persistent_graph(_) --> [].
587
588li_schema_graph(Graph) -->
589 { http_link_to_id(export_graph_schema, [], Action),
590 download_options(show, Label, MimeType, Title)
591 },
592 html(li(form(action(Action),
593 [ input([type(hidden), name(graph), value(Graph)]),
594 input([type(hidden), name(mimetype), value(MimeType)]),
595 'Compute a schema for this graph and ',
596 input([class(saction), type(submit), value(Label),
597 title(Title)
598 ]),
599 ' the result as ',
600 \dl_format_menu
601 ]))).
602
603li_export_graph(Graph, How) -->
604 { http_link_to_id(export_graph, [], Action),
605 download_options(How, Label, MimeType, Title)
606 },
607 html(li(form(action(Action),
608 [ input([type(hidden), name(graph), value(Graph)]),
609 input([type(hidden), name(mimetype), value(MimeType)]),
610 input([class(gaction), type(submit), value(Label),
611 title(Title)
612 ]),
613 ' this graph as ',
614 \dl_format_menu
615 ]))).
616
617download_options(show, 'Show', 'text/plain',
618 'Returns graph with MIME-type text/plain, \n\c
619 so it will be displayed in your browser').
620download_options(download, 'Download', default,
621 'Return graph with its RDF MIME-type, \n\c
622 so most browsers will save it').
623
-->
625 html(select(name(format),
626 [ option([value(turtle),selected], 'Turtle'),
627 option([value(canonical_turtle)], 'Canonical Turtle'),
628 option([value(rdfxml)], 'RDF/XML')
629 ])).
630
631
637
638list_classes(Request) :-
639 http_parameters(Request,
640 [ graph(Graph, [description('Name of the graph')])
641 ]),
642 types_in_graph(Graph, Map),
643 sort_pairs_by_label(Map, Sorted),
644 reply_html_page(cliopatria(default),
645 title('Classes in graph ~w'-[Graph]),
646 [ h1(['Classes in graph ', \graph_link(Graph)]),
647 \class_table(Sorted, Graph, [])
648 ]).
649
650class_table(Pairs, Graph, Options) -->
651 { option(top_max(TopMax), Options, 500),
652 option(top_max(BottomMax), Options, 500)
653 },
654 html_requires(css('rdf.css')),
655 html(table(class(block),
656 [ \class_table_header
657 | \table_rows_top_bottom(class_row(Graph), Pairs,
658 TopMax, BottomMax)
659 ])).
660
-->
662 html(tr([ th('Class'),
663 th('#Instances')
664 ])).
665
666class_row(Graph, Class) -->
667 { atom(Class), !,
668 findall(I, rdf_has(I, rdf:type, Class, Graph), IL),
669 sort(IL, Classes),
670 length(Classes, InstanceCount)
671 },
672 class_row(Graph, Class-InstanceCount).
673class_row(Graph, Class-InstanceCount) -->
674 { ( var(Graph)
675 -> Params = [class(Class)]
676 ; Params = [graph(Graph), class(Class)]
677 ),
678 http_link_to_id(list_instances, Params, ILink)
679 },
680 html([ td(\rdf_link(Class)),
681 td(class(int), a(href(ILink), InstanceCount))
682 ]).
683
688
689types_in_graph(Graph, Map) :-
690 findall(S, subject_in_graph(Graph, S), Subjects),
691 types(Subjects, Pairs),
692 transpose_pairs(Pairs, TypeSubj),
693 group_pairs_by_key(TypeSubj, TypeSubjs),
694 maplist(instance_count, TypeSubjs, Map).
695
696types([], []).
697types([S|T0], Types) :-
698 call_det(type_of(S,C), Det), !,
699 ( Det == true
700 -> Types = [S-C|T],
701 types(T0, T)
702 ; findall(C2, type_of(S,C2), Cs),
703 multi_class(Cs, S, Types, PT),
704 types(T0, PT)
705 ).
706
707multi_class([], _, Pairs, Pairs).
708multi_class([H|T], S, [S-H|Pairs], PT) :-
709 multi_class(T, S, Pairs, PT).
710
711
712type_of(Subject, Type) :-
713 ( rdf_has(Subject, rdf:type, Type)
714 *-> true
715 ; rdf_equal(Type, rdfs:'Resource')
716 ).
717
718:- meta_predicate
719 call_det(0, -).
720
721call_det(G, Det) :-
722 call(G),
723 deterministic(Det).
724
725instance_count(Type-Instances, Type-Count) :-
726 length(Instances, Count).
727
733
734instance_in_graph(Graph, Class, any, S, C) :- !,
735 instance_in_graph(Graph, Class, S, C).
736instance_in_graph(Graph, Class, bnode, S, C) :- !,
737 freeze(S, rdf_is_bnode(S)),
738 instance_in_graph(Graph, Class, S, C).
739
740
741instance_in_graph(Graph, Class, S, C) :-
742 var(Class), !,
743 subject_in_graph(Graph, S),
744 property_count(Graph, S, C).
745instance_in_graph(Graph, Class, S, C) :-
746 rdf_equal(Class, rdfs:'Resource'), !,
747 ( rdf_has(S, rdf:type, Class),
748 once(rdf(S, _, _, Graph))
749 ; subject_in_graph(Graph, S),
750 \+ rdf_has(S, rdf:type, _)
751 ),
752 property_count(Graph, S, C).
753instance_in_graph(Graph, Class, S, C) :-
754 rdf_has(S, rdf:type, Class),
755 once(rdf(S, _, _, Graph)),
756 property_count(Graph, S, C).
757
758property_count(Graph, S, Count) :-
759 aggregate_all(count, rdf(S, _, _, Graph), Count).
760
764
765graph_as_resource(Graph, Options) -->
766 { ( rdf(Graph, _, _)
767 ; rdf(_, Graph, _)
768 ; rdf(_, _, Graph)
769 ), !
770 },
771 html([ h2([ 'Local view for "',
772 \location(Graph, _), '"'
773 ]),
774 \local_view(Graph, _, Options)
775 ]).
776graph_as_resource(_, _) --> [].
777
778
779 782
786
787list_instances(Request) :-
788 http_parameters(Request,
789 [ class(Class,
790 [ optional(true),
791 description('Limit to instances of this class')
792 ]),
793 graph(Graph,
794 [ optional(true),
795 description('Limit to have at least \c
796 one property in graph')
797 ]),
798 type(Type,
799 [ oneof([any, bnode]),
800 default(any),
801 description('Any instance or only bnodes?')
802 ]),
803 sortBy(Sort,
804 [ oneof([label,properties]),
805 default(label),
806 description('How to sort the result-table')
807 ])
808 ]),
809 findall(I-PC, instance_in_graph(Graph, Class, Type, I, PC), IPairs),
810 sort_pairs_by_label(IPairs, TableByName),
811 ( Sort == properties
812 -> reverse(TableByName, RevTableByName),
813 transpose_pairs(RevTableByName, FPairsUp),
814 reverse(FPairsUp, FPairsDown),
815 flip_pairs(FPairsDown, Table)
816 ; Table = TableByName
817 ),
818
819 reply_html_page(cliopatria(default),
820 title(\instance_table_title(Graph, Class, Sort)),
821 [ h1(\html_instance_table_title(Graph, Class, Sort)),
822 \instance_table(Table, [resource_format(nslabel)])
823 ]).
824
825instance_table_title(Graph, Class, Sort) -->
826 { var(Class) }, !,
827 html('Instances in ~w sorted by ~w'-
828 [Graph, Sort]).
829instance_table_title(Graph, Class, Sort) -->
830 { rdf_display_label(Class, Label) },
831 html('Instances of ~w in ~w sorted by ~w'-
832 [Label, Graph, Sort]).
833
834html_instance_table_title(Graph, Class, Sort) -->
835 html([ 'Instances',
836 \of_class(Class),
837 \in_graph(Graph),
838 \sorted_by(Sort)
839 ]).
840
841of_class(Class) -->
842 { var(Class) }, !.
843of_class(Class) -->
844 html([' of class ', \rdf_link(Class)]).
845
846in_graph(Graph) -->
847 { var(Graph) }, !.
848in_graph(Graph) -->
849 html([' in graph ', \graph_link(Graph)]).
850
851sorted_by(Sort) -->
852 html(' sorted by ~w'-[Sort]).
853
854
855instance_table(Pairs, Options) -->
856 { option(top_max(TopMax), Options, 500),
857 option(top_max(BottomMax), Options, 500)
858 },
859 html_requires(css('rdf.css')),
860 html(table(class(block),
861 [ \instance_table_header
862 | \table_rows_top_bottom(instance_row(Options), Pairs,
863 TopMax, BottomMax)
864 ])).
865
-->
867 html(tr([ th('Instance'),
868 th('#Properties')
869 ])).
870
871instance_row(Options, R-C) -->
872 html([ td(\rdf_link(R, Options)),
873 td(class(int), C)
874 ]).
875
876
877 880
884
885list_predicates(Request) :-
886 http_parameters(Request,
887 [ graph(Graph, [])
888 ]),
889 findall(Pred, predicate_in_graph(Graph, Pred), Preds),
890 sort_by_label(Preds, Sorted),
891 reply_html_page(cliopatria(default),
892 title('Predicates in graph ~w'-[Graph]),
893 [ h1(['Predicates in graph ', \graph_link(Graph)]),
894 \predicate_table(Sorted, Graph, [])
895 ]).
896
897predicate_table(Preds, Graph, Options) -->
898 { option(top_max(TopMax), Options, 500),
899 option(bottom_max(BottomMax), Options, 500)
900 },
901 html_requires(css('rdf.css')),
902 html(table(class(block),
903 [ \predicate_table_header
904 | \table_rows_top_bottom(predicate_row(Graph), Preds,
905 TopMax, BottomMax)
906 ])).
907
-->
909 html(tr([ th('Predicate'),
910 th('#Triples'),
911 th('#Distinct subjects'),
912 th('#Distinct objects'),
913 th('Domain(s)'),
914 th('Range(s)')
915 ])).
916
918
919predicate_row(Graph, Pred) -->
920 { predicate_statistics(Graph, Pred, Triples,
921 Subjects, Objects, Doms, Ranges),
922 ( var(Graph)
923 -> Params = [predicate(Pred)]
924 ; Params = [graph(Graph), predicate(Pred)]
925 ),
926 http_link_to_id(list_triples, Params, PLink)
927 },
928 html([ td(\rdf_link(Pred)),
929 td(class(int), a(href(PLink), Triples)),
930 \resources(Subjects, subject, Params, []),
931 \resources(Objects, object, Params, []),
932 \resources(Doms, domain, Params, []),
933 \resources(Ranges, range, Params, [])
934 ]).
935
936resources([], _, _, _) --> !,
937 html(td(class(empty), -)).
938resources([One], _, _, Options) --> !,
939 html(td(\rdf_link(One, Options))).
940resources(Many, What, Params, _) --> !,
941 { ( integer(Many)
942 -> Count = Many
943 ; length(Many, Count)
944 ),
945 http_link_to_id(list_predicate_resources, [side(What)|Params], Link)
946 },
947 html(td(class(int_c), a(href(Link), Count))).
948
949:- dynamic
950 predicate_statistics_cache/8.
951
952predicate_statistics(Graph, P, C, Subjects, Objects, Domains, Ranges) :-
953 var(Graph), !,
954 predicate_statistics_(Graph, P, C, Subjects, Objects, Domains, Ranges).
955predicate_statistics(Graph, P, C, Subjects, Objects, Domains, Ranges) :-
956 rdf_md5(Graph, MD5),
957 predicate_statistics_cache(MD5, Graph, P, C,
958 Subjects, Objects, Domains, Ranges), !.
959predicate_statistics(Graph, P, C, Subjects, Objects, Domains, Ranges) :-
960 rdf_md5(Graph, MD5),
961 debug(rdf_browse, 'Recomputing pred stats for ~p in ~w, MD5=~w',
962 [P, Graph, MD5]),
963 retractall(predicate_statistics_cache(MD5, Graph, P, _,
964 _, _, _, _)),
965 predicate_statistics_(Graph, P, C, SubjectL, ObjectL, DomainL, RangeL),
966 res_summary(SubjectL, Subjects),
967 res_summary(ObjectL, Objects),
968 res_summary(DomainL, Domains),
969 res_summary(RangeL, Ranges),
970 assertz(predicate_statistics_cache(MD5, Graph, P, C,
971 Subjects, Objects, Domains, Ranges)).
972
973
974res_summary([], []) :- !.
975res_summary([One], [One]) :- !.
976res_summary(Many, Count) :-
977 length(Many, Count).
978
979
980predicate_statistics_(Graph, P, C, Subjects, Objects, Domains, Ranges) :-
981 findall(S-O, rdf(S,P,O,Graph), Pairs),
982 length(Pairs, C),
983 pairs_keys_values(Pairs, Ss, Os),
984 sort(Ss, Subjects),
985 sort(Os, Objects),
986 resources_types(Subjects, Graph, Domains),
987 resources_types(Objects, Graph, Ranges).
988
989resources_types(URIs, Graph, Types) :-
990 findall(T, resource_type_in(URIs, Graph, T), TList),
991 sort(TList, Types).
992
993resource_type_in(List, Graph, T) :-
994 member(URI, List),
995 resource_type(URI, Graph, T).
996
998
999resource_type(URI, Graph, T) :-
1000 ( URI = literal(Lit)
1001 -> ( Lit = type(T, _)
1002 -> true
1003 ; rdf_equal(T, rdfs:'Literal')
1004 )
1005 ; rdf(URI, rdf:type, T, Graph)
1006 *-> true
1007 ; rdf_equal(T, rdfs:'Resource')
1008 ).
1009
1010
1011 1014
1032
1033list_predicate_resources(Request) :-
1034 http_parameters(Request,
1035 [ graph(Graph,
1036 [ optional(true),
1037 description('Limit search to this graph')
1038 ]),
1039 predicate(Pred,
1040 [ description('Predicate to list')
1041 ]),
1042 side(Which,
1043 [ oneof([subject,object,domain,range]),
1044 description('Relation to the predicate (see docs)')
1045 ]),
1046 sortBy(Sort,
1047 [ oneof([label,frequency]),
1048 default(frequency),
1049 description('How to sort results')
1050 ]),
1051 skosmap(SkosMap,
1052 [ boolean,
1053 optional(true),
1054 description('Show SKOS concepts for literals')
1055 ])
1056 ]),
1057 do_skos(SkosMap, Which, Pred),
1058 findall(R, predicate_resource(Graph, Pred, Which, R), Set),
1059 term_frequency_list(Set, FPairs),
1060 sort_pairs_by_label(FPairs, TableByName),
1061 ( Sort == frequency
1062 -> reverse(TableByName, RevTableByName),
1063 transpose_pairs(RevTableByName, FPairsUp),
1064 reverse(FPairsUp, FPairsDown),
1065 flip_pairs(FPairsDown, Table)
1066 ; Table = TableByName
1067 ),
1068
1069 pred_resource_options(Pred, Which, Options),
1070
1071 reply_html_page(cliopatria(default),
1072 title(\resource_table_title(Graph, Pred, Which, Sort)),
1073 [ h1(\html_resource_table_title(Graph, Pred, Which,
1074 Sort, SkosMap)),
1075 \resource_frequency_table(Table,
1076 [ skosmap(SkosMap),
1077 predicate(Pred),
1078 side(Which),
1079 sort(Sort)
1080 | Options
1081 ])
1082 ]).
1083
1084pred_resource_options(_, domain, [label('Class')]) :- !.
1085pred_resource_options(_, range, [label('Class')]) :- !.
1086pred_resource_options(_, _, []).
1087
1088do_skos(SkosMap, _, _) :-
1089 nonvar(SkosMap), !.
1090do_skos(SkosMap, object, Pred) :-
1091 \+ rdf(_, Pred, literal(_)), !,
1092 SkosMap = false.
1093do_skos(SkosMap, object, _) :-
1094 rdfs_individual_of(_, skos:'ConceptScheme'), !,
1095 SkosMap = true.
1096do_skos(false, _, _).
1097
1098
1099resource_table_title(Graph, Pred, Which, Sort) -->
1100 { rdf_display_label(Pred, PLabel)
1101 },
1102 html('Distinct ~ws for ~w in ~w sorted by ~w'-
1103 [Which, PLabel, Graph, Sort]
1104 ).
1105
1106html_resource_table_title(Graph, Pred, Which, Sort, SkosMap) -->
1107 html([ 'Distinct ~ws'-[Which],
1108 \for_predicate(Pred),
1109 \in_graph(Graph),
1110 \sorted_by(Sort),
1111 \showing_skosmap(SkosMap)
1112 ]).
1113
1114for_predicate(Pred) -->
1115 { var(Pred) }, !.
1116for_predicate(Pred) -->
1117 html([' for predicate ', \rdf_link(Pred, [])]).
1118
1119showing_skosmap(true) --> !,
1120 html(' with mapping to SKOS').
1121showing_skosmap(_) --> [].
1122
1123resource_frequency_table(Pairs, Options) -->
1124 { option(top_max(TopMax), Options, 500),
1125 option(top_max(BottomMax), Options, 500),
1126 option(predicate(Pred), Options, _),
1127 option(side(Side), Options)
1128 },
1129 html_requires(css('rdf.css')),
1130 html(table(class(block),
1131 [ \resource_table_header(Options)
1132 | \table_rows_top_bottom(resource_row(Pred, Side, Options), Pairs,
1133 TopMax, BottomMax)
1134 ])).
1135
(Options) -->
1137 { option(label(Label), Options, 'Resource'),
1138 ( option(sort(Sort), Options)
1139 -> ( Sort == frequency
1140 -> A1 = [],
1141 A2 = [class(sorted)]
1142 ; A1 = [class(sorted)],
1143 A2 = []
1144 )
1145 ; A1 = [],
1146 A2 = []
1147 )
1148 },
1149 html(tr([ th(A1, Label),
1150 th(A2, 'Count'),
1151 \skosmap_head(Options)
1152 ])).
1153
1154skosmap_head(Options) -->
1155 { option(skosmap(true), Options) }, !,
1156 html(th('SKOS mapping')).
1157skosmap_head(_) --> [].
1158
1159resource_row(Pred, object, Options, R-C) --> !,
1160 { object_param(R, Param),
1161 http_link_to_id(list_triples_with_object,
1162 [ p(Pred),
1163 Param
1164 ], HREF)
1165 },
1166 html([ td(\rdf_link(R, Options)),
1167 td(class(int), a(href(HREF), C)),
1168 \skosmap(R, Options)
1169 ]).
1170resource_row(Pred, Side, Options, R-C) -->
1171 { domain_range_parameter(Side, R, Param), !,
1172 http_link_to_id(list_triples,
1173 [ predicate(Pred),
1174 Param
1175 ], HREF)
1176 },
1177 html([ td(\rdf_link(R, Options)),
1178 td(class(int), a(href(HREF), C)),
1179 \skosmap(R, Options)
1180 ]).
1181resource_row(_, _, Options, R-C) -->
1182 html([ td(\rdf_link(R, Options)),
1183 td(class(int), C),
1184 \skosmap(R, Options)
1185 ]).
1186
1187object_param(R, r=R) :-
1188 atom(R), !.
1189object_param(L, l=A) :-
1190 term_to_atom(L, A).
1191
1192domain_range_parameter(domain, R, domain(R)).
1193domain_range_parameter(range, R, range(R)).
1194
1199
1200skosmap(Literal, Options) -->
1201 { Literal = literal(_),
1202 option(skosmap(true), Options),
1203 findall(Concept-Scheme, skos_find(Literal, Concept, Scheme), Pairs),
1204 Pairs \== [],
1205 sort_pairs_by_label(Pairs, Sorted)
1206 },
1207 html(td(\skos_references(Sorted))).
1208skosmap(_, _) --> [].
1209
1210skos_find(Literal, Concept, Scheme) :-
1211 rdf_has(Concept, skos:prefLabel, Literal),
1212 rdf_has(Concept, skos:inScheme, Scheme).
1213
1214skos_references([]) --> [].
1215skos_references([H|T]) -->
1216 skos_reference(H),
1217 ( { T == [] }
1218 -> []
1219 ; html('; '),
1220 skos_references(T)
1221 ).
1222
1223skos_reference(Concept-Scheme) -->
1224 html([\rdf_link(Concept), ' in ', \rdf_link(Scheme)]).
1225
1226
1227flip_pairs([], []).
1228flip_pairs([Key-Val|Pairs], [Val-Key|Flipped]) :-
1229 flip_pairs(Pairs, Flipped).
1230
1231predicate_resource(Graph, Pred, subject, R) :- !,
1232 rdf(R, Pred, _, Graph).
1233predicate_resource(Graph, Pred, object, R) :- !,
1234 rdf(_, Pred, R, Graph).
1235predicate_resource(Graph, Pred, domain, D) :- !,
1236 rdf(R, Pred, _, Graph),
1237 rdf(R, rdf:type, D, Graph).
1238predicate_resource(Graph, Pred, range, R) :-
1239 rdf(_, Pred, O, Graph),
1240 resource_type(O, Graph, R).
1241
1247
1248term_frequency_list(Resources, Pairs) :-
1249 msort(Resources, Sorted),
1250 fpairs(Sorted, Pairs).
1251
1252fpairs([], []).
1253fpairs([H|T0], [H-C|T]) :-
1254 pick_same(T0, T1, H, 1, C),
1255 fpairs(T1, T).
1256
1257pick_same([H1|T0], L, H, F0, F) :-
1258 H == H1, !,
1259 F1 is F0 + 1,
1260 pick_same(T0, L, H, F1, F).
1261pick_same(L, L, _, F, F).
1262
1263
1264 1267
1275
1276list_resource(Request) :-
1277 http_parameters(Request,
1278 [ r(URI,
1279 [ description('URI to describe')]),
1280 sorted(Sorted,
1281 [ oneof([default,none]),
1282 default(default),
1283 description('How to sort properties')
1284 ]),
1285 graph(Graph,
1286 [ optional(true),
1287 description('Limit to properties from graph')
1288 ]),
1289 raw(Raw,
1290 [ default(false),
1291 boolean,
1292 description('If true, omit application hook')
1293 ])
1294 ]),
1295 rdf_display_label(URI, Label),
1296 reply_html_page(cliopatria(default),
1297 title('Resource ~w'-[Label]),
1298 \list_resource(URI,
1299 [ graph(Graph),
1300 sorted(Sorted),
1301 raw(Raw),
1302 resource_format(nslabel)
1303 ])).
1304
1322
1323:- multifile
1324 cliopatria:list_resource//1.
1325
1326list_resource(URI, Options) -->
1327 { \+ option(raw(true), Options) },
1328 ( cliopatria:list_resource(URI, Options)
1329 -> []
1330 ; cliopatria:list_resource(URI) 1331 ).
1332list_resource(URI, Options) -->
1333 { option(graph(Graph), Options, _)
1334 },
1335 html([ h1([ 'Local view for "',
1336 \location(URI, Graph), '"'
1337 ]),
1338 \define_prefix(URI),
1339 \local_view(URI, Graph, Options),
1340 p(\as_object(URI, Graph)),
1341 p(\as_graph(URI)),
1342 \uri_info(URI, Graph)
1343 ]).
1344
1349
1350define_prefix(URI) -->
1351 { rdf_global_id(_Prefix:_Local, URI) }, !.
1352define_prefix(URI) -->
1353 { iri_xml_namespace(URI, Namespace, LocalName),
1354 LocalName \== '',
1355 http_link_to_id(add_prefix, [], Action)
1356 },
1357 html(form(action(Action),
1358 ['No prefix for ', a(href(Namespace),Namespace), '. ',
1359 \hidden(uri, Namespace),
1360 input([name(prefix), size(8),
1361 title('Short unique abbreviation')
1362 ]),
1363 input([type(submit), value('Add prefix')])
1364 ])).
1365define_prefix(_) --> 1366 [].
1367
1368
1373
1374location(URI, _Graph) -->
1375 { rdf_is_bnode(URI), !,
1376 findall(Path, path_to_non_bnode(URI, Path), Paths),
1377 sort_by_length(Paths, PathsByLen),
1378 partition(starts_bnode, PathsByLen, StartsBNode, StartsReal),
1379 ( StartsReal = [Path|_]
1380 -> true
1381 ; last(StartsBNode, Path)
1382 )
1383 },
1384 bnode_location(Path).
1385location(URI, _) -->
1386 html(URI).
1387
1388bnode_location([P-URI]) --> !,
1389 html([ '[', \rdf_link(P, []), ' ',
1390 \rdf_link(URI),
1391 ']'
1392 ]).
1393bnode_location([P-URI|More]) --> !,
1394 html([ '[', div(class(bnode_attr),
1395 [ div(\rdf_link(P, [])),
1396 div(\rdf_link(URI))
1397 ]), ' ',
1398 \bnode_location(More),
1399 ']'
1400 ]).
1401bnode_location([URI|More]) --> !,
1402 rdf_link(URI),
1403 html(' '),
1404 bnode_location(More).
1405bnode_location([]) -->
1406 [].
1407
1408path_to_non_bnode(URI, Path) :-
1409 path_to_non_bnode_rev(URI, [URI], RevPath),
1410 reverse(RevPath, Path).
1411
1412path_to_non_bnode_rev(URI, Seen, [P-URI|Path]) :-
1413 ( rdf_is_bnode(URI),
1414 rdf(S, P, URI),
1415 \+ memberchk(S, Seen)
1416 *-> path_to_non_bnode_rev(S, [S|Seen], Path)
1417 ; fail
1418 ).
1419path_to_non_bnode_rev(URI, _, [URI]).
1420
1421starts_bnode([URI|_]) :-
1422 rdf_is_bnode(URI).
1423
1424sort_by_length(ListOfLists, ByLen) :-
1425 map_list_to_pairs(length, ListOfLists, Pairs),
1426 keysort(Pairs, Sorted),
1427 pairs_values(Sorted, ByLen).
1428
1432
1433as_graph(URI) --> { \+ rdf_graph(URI) }, !.
1434as_graph(URI) -->
1435 html([ 'This resource is also a ',
1436 a([href(location_by_id(list_graph)+'?graph='+encode(URI))],
1437 'named graph'),
1438 '.']).
1439
1440
1444
1445as_object(URI, Graph) -->
1446 { findall(S-P, rdf(S,P,URI,Graph), Pairs),
1447 sort(Pairs, Unique)
1448 },
1449 as_object_locations(Unique, URI, Graph).
1450
1451as_object_locations([], _URI, _) --> !,
1452 html([ 'The resource does not appear as an object' ]).
1453as_object_locations([S-P], URI, _) --> !,
1454 html([ 'The resource appears as object in one triple:',
1455 blockquote(class(triple),
1456 [ '{ ',
1457 \rdf_link(S), ', ',
1458 \rdf_link(P, []), ', ',
1459 \rdf_link(URI),
1460 ' }'
1461 ])
1462 ]).
1463as_object_locations(List, URI, Graph) --> !,
1464 { length(List, Len),
1465 ( var(Graph)
1466 -> Extra = []
1467 ; Extra = [graph=Graph]
1468 ),
1469 http_link_to_id(list_triples_with_object, [r=URI|Extra], Link)
1470 },
1471 html([ 'The resource appears as object in ',
1472 a(href(Link), [Len, ' triples'])
1473 ]).
1474
1488
1489local_view(URI, Graph, Options) -->
1490 { option(top_max(TopMax), Options, 500),
1491 option(bottom_max(BottomMax), Options, 500),
1492 po_pairs(URI, Graph, Pairs, Options),
1493 lview_graphs(URI, Graph, Graphs)
1494 },
1495 ( { Pairs \== []
1496 }
1497 -> html_requires(css('rdf.css')),
1498 html(table(class(block),
1499 [ \lview_header(Options)
1500 | \table_rows_top_bottom(lview_row(Options, URI, Graphs),
1501 Pairs,
1502 TopMax, BottomMax)
1503 ])),
1504 graph_footnotes(Graphs, Options)
1505 ; { lod_uri_graph(URI, LODGraph),
1506 rdf_graph(LODGraph)
1507 }
1508 -> html(p([ 'No triples for ', \show_link(URI), '. ',
1509 'Linked Data was loaded into ', \graph_link(LODGraph),
1510 '.'
1511 ]))
1512 ; { sane_uri(URI) }
1513 -> { http_link_to_id(lod_crawl, [], FetchURL),
1514 http_current_request(Request),
1515 memberchk(request_uri(Here), Request)
1516 },
1517 html(form(action(FetchURL),
1518 [ \hidden(r, URI),
1519 \hidden(return_to, Here),
1520 'No triples for ', \show_link(URI),
1521 '. Would you like to ',
1522 input([ type(submit),
1523 value('Query the Linked Data cloud')
1524 ]),
1525 '?'
1526 ]))
1527 ; html_requires(css('rdf.css')),
1528 html(p([ 'No triples for ', \show_link(URI),
1529 ' (unknown URI scheme).']))
1530 ).
1531
1532show_link(URI) -->
1533 { sane_uri(URI) }, !,
1534 html(a(href(URI), 'this URI')).
1535show_link(URI) -->
1536 html(span(class('insecure-uri'), URI)).
1537
1538sane_uri(URI) :-
1539 uri_components(URI, Components),
1540 uri_data(scheme, Components, Scheme),
1541 valid_scheme(Scheme),
1542 uri_data(authority, Components, Authority),
1543 nonvar(Authority).
1544
1545valid_scheme(http).
1546valid_scheme(https).
1547valid_scheme(ftp).
1548valid_scheme(ftps).
1549
(Options) -->
1551 { option(sorted(Sorted), Options, default),
1552 alt_sorted(Sorted, Alt),
1553 http_current_request(Request),
1554 http_reload_with_parameters(Request, [sorted(Alt)], HREF)
1555 },
1556 html(tr([ th('Predicate'),
1557 th(['Value (sorted: ', a(href(HREF), Sorted), ')'])
1558 ])).
1559
1560alt_sorted(default, none).
1561alt_sorted(none, default).
1562
1563
1564lview_row(Options, S, Graphs, P-OList) -->
1565 html([ td(class(predicate), \rdf_link(P, Options)),
1566 td(class(object), \object_list(OList, S, P, Graphs, Options, 1))
1567 ]).
1568
1569object_list([], _, _, _, _, _) --> [].
1570object_list([H|T], S, P, Graphs, Options, Row) -->
1571 { NextRow is Row + 1,
1572 obj_class(Row, Class)
1573 },
1574 html(div(class(Class),
1575 [ \rdf_link(H, Options),
1576 \graph_marks(S, P, H, Graphs)
1577 ])),
1578 object_list(T, S, P, Graphs, Options, NextRow).
1579
1580obj_class(N, Class) :-
1581 ( N mod 2 =:= 0
1582 -> Class = even
1583 ; Class = odd
1584 ).
1585
1586graph_marks(_,_,_,[_]) --> !.
1587graph_marks(S,P,O,Graphs) -->
1588 html(sup(class(graph), \graphs(S,P,O,Graphs))).
1589
1590graphs(S, P, O, Graphs) -->
1591 { findall(G, rdf(S,P,O,G:_), GL) },
1592 graphs(GL, Graphs).
1593
1594graphs([], _) --> [].
1595graphs([H|T], Graphs) -->
1596 { nth1(N, Graphs, H) -> true },
1597 html(N),
1598 ( { T == [] }
1599 -> []
1600 ; html(','),
1601 graphs(T, Graphs)
1602 ).
1603
1608
([], _Options) --> !.
1610graph_footnotes([Graph], _Options) --> !,
1611 html(p(class('graphs-used'),
1612 [ 'All properties reside in the graph ',
1613 \graph_link(Graph)
1614 ])).
1615graph_footnotes(Graphs, Options) -->
1616 html(p(class('graphs-used'),
1617 'Named graphs describing this resource:')),
1618 graph_footnotes(Graphs, 1, Options).
1619
([], _, _) --> [].
1621graph_footnotes([H|T], N, Options) -->
1622 html(div(class('graph-fn'),
1623 [ sup(class(graph), N),
1624 \graph_link(H)
1625 ])),
1626 { N2 is N + 1 },
1627 graph_footnotes(T, N2, Options).
1628
1630
1631lview_graphs(_Subject, Graph, Graphs) :-
1632 nonvar(Graph), !,
1633 Graphs = [Graph].
1634lview_graphs(Subject, Graph, Graphs) :-
1635 findall(Graph, rdf(Subject, _, _, Graph:_), Graphs0),
1636 sort(Graphs0, Graphs).
1637
1643
1644po_pairs(S, Graph, Pairs, Options) :-
1645 option(sorted(none), Options), !,
1646 findall(P-[O], rdf(S,P,O,Graph), Pairs).
1647po_pairs(S, Graph, Pairs, _Options) :-
1648 var(Graph), !,
1649 findall(P-OL,
1650 setof(O, rdf(S,P,O), OL),
1651 Pairs0),
1652 sort_po(Pairs0, Pairs).
1653po_pairs(S, Graph, Pairs, _Options) :-
1654 findall(P-OL,
1655 setof(O, rdf(S,P,O,Graph), OL),
1656 Pairs0),
1657 sort_po(Pairs0, Pairs).
1658
1664
1665sort_po(Pairs, Sorted) :-
1666 map_list_to_pairs(po_key, Pairs, Keyed),
1667 keysort(Keyed, KeySorted),
1668 exclude(=(0-_), KeySorted, Remaining),
1669 pairs_values(Remaining, Sorted).
1670
1671po_key(P-_, Key) :-
1672 p_order(P, Key), !.
1673po_key(P-_, Key) :-
1674 label_sort_key(P, Key).
1675
1681
1682:- rdf_meta
1683 p_order(r,?).
1684
1685p_order(P, Order) :-
1686 cliopatria:predicate_order(P, Order), !.
1687p_order(P, 100) :-
1688 label_property(P), !.
1689p_order(P, 110) :-
1690 rdfs_subproperty_of(P, skos:altLabel), !.
1691p_order(rdf:type, 210).
1692p_order(rdfs:subClassOf, 220).
1693p_order(rdfs:domain, 230).
1694p_order(rdfs:range, 240).
1695p_order(rdfs:comment, 310).
1696p_order(rdfs:isDefinedBy, 320).
1697
1698
1703
1704uri_info(URI, Graph) -->
1705 uri_class_info(URI, Graph),
1706 uri_predicate_info(URI, Graph),
1707 html(h2('Context graph')),
1708 context_graph(URI, []).
1709
1710uri_class_info(URI, Graph) -->
1711 { rdf_current_predicate(URI)
1712 }, !,
1713 html(h2('Predicate statistics')),
1714 predicate_table([URI], Graph, []).
1715uri_class_info(_,_) --> [].
1716
1717uri_predicate_info(URI, Graph) -->
1718 { \+ \+ rdf(_, rdf:type, URI, Graph)
1719 }, !,
1720 html(h2('Class statistics')),
1721 class_table([URI], Graph, []).
1722uri_predicate_info(_, _) --> [].
1723
1724
1739
1740context_graph(URI, Options) -->
1741 { merge_options(Options, [style(_)], GraphOption),
1742 rdf_equal(owl:sameAs, SameAs)
1743 },
1744 html([ \graphviz_graph(context_graph(URI, GraphOption),
1745 [ object_attributes([width('100%')]),
1746 wrap_url(resource_link),
1747 graph_attributes([ rankdir('RL')
1748 ]),
1749 shape_hook(shape(URI, GraphOption)),
1750 bag_shape_hook(bag_shape(GraphOption)),
1751 smash([SameAs])
1752 ])
1753 ]).
1754
1755:- public
1756 shape/4,
1757 bag_shape/3.
1758
1763
1764shape(Start, Options, URI, Shape) :-
1765 cliopatria:node_shape(URI, Shape, [start(Start)|Options]), !.
1766shape(Start, _Options, Start,
1767 [ shape(tripleoctagon),style(filled),fillcolor('#ff85fd'),id(start) ]).
1768
1772
1773bag_shape(Options, Members, Shape) :-
1774 cliopatria:bag_shape(Members, Shape, Options), !.
1775bag_shape(_, _, []).
1776
1777
1778
1788
1789context_graph(URI, Options, RDF) :-
1790 cliopatria:context_graph(URI, RDF, Options), !.
1791context_graph(URI, _Options, RDF) :- 1792 cliopatria:context_graph(URI, RDF), !.
1793context_graph(URI, _, RDF) :-
1794 findall(T, context_triple(URI, T), RDF0),
1795 sort(RDF0, RDF1),
1796 minimise_graph(RDF1, RDF2), 1797 bagify_graph(RDF2, RDF3, Bags, []), 1798 append(RDF3, Bags, RDF).
1799
1800:- rdf_meta
1801 transitive_context(r),
1802 context(r).
1803
1804context_triple(URI, Triple) :-
1805 transitive_context(CP),
1806 parents(URI, CP, Triples, [URI], 3),
1807 member(Triple, Triples).
1808context_triple(URI, Triple) :-
1809 cliopatria:context_predicate(URI, R),
1810 rdf_has(URI, R, O, P),
1811 normalize_triple(rdf(URI, P, O), Triple).
1812context_triple(URI, Triple) :-
1813 context(R),
1814 rdf_has(URI, R, O, P),
1815 normalize_triple(rdf(URI, P, O), Triple).
1816context_triple(URI, Triple) :-
1817 context(R),
1818 rdf_has(S, R, URI, P),
1819 normalize_triple(rdf(S, P, URI), Triple).
1820
1821normalize_triple(rdf(S, inverse_of(P0), O),
1822 rdf(O, P, S)) :- !,
1823 rdf_predicate_property(P0, inverse_of(P)).
1824normalize_triple(RDF, RDF).
1825
1826
1827
1828parents(URI, Up, [Triple|T], Visited, MaxD) :-
1829 succ(MaxD2, MaxD),
1830 rdf_has(URI, Up, Parent, P),
1831 normalize_triple(rdf(URI, P, Parent), Triple),
1832 \+ memberchk(Parent, Visited),
1833 parents(Parent, Up, T, [Parent|Visited], MaxD2).
1834parents(_, _, [], _, _).
1835
1836transitive_context(owl:sameAs).
1837transitive_context(rdfs:subClassOf).
1838transitive_context(rdfs:subPropertyOf).
1839transitive_context(skos:broader).
1840transitive_context(P) :-
1841 rdfs_individual_of(P, owl:'TransitiveProperty'),
1842 rdf_predicate_property(P, rdfs_subject_branch_factor(BF)),
1843 BF < 2.0.
1844
1845context(skos:related).
1846context(skos:mappingRelation).
1847
1853
1854list_triples(Request) :-
1855 http_parameters(Request,
1856 [ predicate(P,
1857 [ optional(true),
1858 description('Limit triples to this pred')]),
1859 graph(Graph, [ optional(true),
1860 description('Limit triples to this graph')
1861 ]),
1862 domain(Dom, [ optional(true),
1863 description('Restrict to subjects of this class')
1864 ]),
1865 range(Range, [ optional(true),
1866 description('Restrict to objects of this class')
1867 ])
1868 ]),
1869 ( atom(Dom)
1870 -> findall(rdf(S,P,O), rdf_in_domain(S,P,O,Dom,Graph), Triples0)
1871 ; atom(Range)
1872 -> findall(rdf(S,P,O), rdf_in_range(S,P,O,Range,Graph), Triples0)
1873 ; findall(rdf(S,P,O), rdf(S,P,O,Graph), Triples0)
1874 ),
1875 sort(Triples0, Triples),
1876 sort_triples_by_label(Triples, Sorted),
1877 length(Sorted, Count),
1878 ( var(P)
1879 -> Title = 'Triples in graph ~w'-[Graph]
1880 ; rdf_display_label(P, PLabel),
1881 Title = 'Triples for ~w in graph ~w'-[PLabel, Graph]
1882 ),
1883 reply_html_page(cliopatria(default),
1884 title(Title),
1885 [ h1(\triple_header(Count, P, Dom, Range, Graph)),
1886 \triple_table(Sorted, P, [resource_format(nslabel)])
1887 ]).
1888
1889rdf_in_domain(S,P,O,Dom,Graph) :-
1890 rdf(S, P, O, Graph),
1891 rdf_has(S, rdf:type, Dom).
1892
1893rdf_in_range(S,P,O,Lit,Graph) :-
1894 rdf_equal(rdfs:'Literal', Lit), !,
1895 O = literal(_),
1896 rdf(S, P, O, Graph).
1897rdf_in_range(S,P,O,Rng,Graph) :-
1898 rdf_equal(rdfs:'Resource', Rng), !,
1899 rdf(S, P, O, Graph),
1900 atom(O).
1901rdf_in_range(S,P,O,Rng,Graph) :-
1902 rdf(S, P, O, Graph),
1903 rdf_has(O, rdf:type, Rng).
1904
1905
(Count, Pred, Dom, Range, Graph) -->
1907 html([ 'Table for the ~D triples'-[Count],
1908 \for_predicate(Pred),
1909 \with_domain(Dom),
1910 \with_range(Range),
1911 \in_graph(Graph)
1912 ]).
1913
1914with_domain(Dom) -->
1915 { var(Dom) }, !.
1916with_domain(Dom) -->
1917 html([' with domain ', \rdf_link(Dom, [])]).
1918
1919with_range(Range) -->
1920 { var(Range) }, !.
1921with_range(Range) -->
1922 html([' with range ', \rdf_link(Range, [])]).
1923
1928
1929triple_table(Triples, Pred, Options) -->
1930 { option(top_max(TopMax), Options, 500),
1931 option(top_max(BottomMax), Options, 500)
1932 },
1933 html(table(class(block),
1934 [ \spo_header(Pred)
1935 | \table_rows_top_bottom(spo_row(Options, Pred), Triples,
1936 TopMax, BottomMax)
1937 ])).
1938
(P) -->
1940 { nonvar(P) },
1941 html(tr([ th('Subject'),
1942 th('Object')
1943 ])).
1944spo_header(_) -->
1945 html(tr([ th('Subject'),
1946 th('Predicate'),
1947 th('Object')
1948 ])).
1949
1950spo_row(Options, Pred, rdf(S,_,O)) -->
1951 { nonvar(Pred) }, !,
1952 html([ td(class(subject), \rdf_link(S, Options)),
1953 td(class(object), \rdf_link(O, Options))
1954 ]).
1955spo_row(Options, _, rdf(S,P,O)) -->
1956 html([ td(class(subject), \rdf_link(S, Options)),
1957 td(class(predicate), \rdf_link(P, Options)),
1958 td(class(object), \rdf_link(O, Options))
1959 ]).
1960
1961
1968
1969list_triples_with_object(Request) :-
1970 http_parameters(Request,
1971 [ r(RObject, [optional(true),
1972 description('Object as resource (URI)')
1973 ]),
1974 l(LObject, [optional(true),
1975 description('Object as literal (Prolog notation)')
1976 ]),
1977 p(P, [optional(true),
1978 description('Limit to a given predicate (URI)')
1979 ]),
1980 graph(Graph, [optional(true),
1981 description('Limit to a given graph (URI)')
1982 ]),
1983 sortBy(Sort,
1984 [ oneof([label, subject, predicate]),
1985 default(label),
1986 description('How to sort the result')
1987 ])
1988 ]),
1989 target_object(RObject, LObject, Object),
1990 list_triples_with_object(Object, P, Graph, [sortBy(Sort)]).
1991
1992target_object(RObject, _LObject, RObject) :-
1993 atom(RObject), !.
1994target_object(_, LObject, Object) :-
1995 atom(LObject), !,
1996 term_to_atom(Object, LObject).
1997target_object(_, _, _) :-
1998 throw(existence_error(http_parameter, r)).
1999
2005
2006list_triples_with_literal(Request) :-
2007 http_parameters(Request,
2008 [ q(Text,
2009 [optional(true),
2010 description('Object as resource (URI)')
2011 ])
2012 ]),
2013 list_triples_with_object(literal(Text), _, _, [sortBy(subject)]).
2014
2015
2016list_triples_with_object(Object, P, Graph, Options) :-
2017 findall(S-P, rdf(S,P,Object,Graph), Pairs),
2018 ( option(sortBy(label), Options)
2019 -> sort_pairs_by_label(Pairs, Sorted)
2020 ; option(sortBy(predicate), Options)
2021 -> transpose_pairs(Pairs, Transposed), 2022 flip_pairs(Transposed, Sorted) 2023 ; sort(Pairs, Sorted)
2024 ),
2025 length(Pairs, Count),
2026 label_of(Object, OLabel),
2027 reply_html_page(cliopatria(default),
2028 title('Triples with object ~w'-[OLabel]),
2029 [ h1(\otriple_header(Count, Object, P, Graph, Options)),
2030 \otriple_table(Sorted, Object, [resource_format(nslabel)])
2031 ]).
2032
(Count, Object, Pred, Graph, Options) -->
2034 { option(sortBy(SortBy), Options) },
2035 html([ 'Table for the ~D triples'-[Count],
2036 \with_object(Object),
2037 \on_predicate(Pred),
2038 \in_graph(Graph),
2039 \sorted_by(SortBy)
2040 ]).
2041
2042with_object(Obj) -->
2043 { var(Obj)}, !.
2044with_object(Obj) -->
2045 html([' with object ', \rdf_link(Obj)]).
2046
2047on_predicate(P) -->
2048 { var(P) }, !.
2049on_predicate(P) -->
2050 html([' on predicate ', \rdf_link(P, [])]).
2051
2052
2053otriple_table(SPList, Object, Options) -->
2054 { option(top_max(TopMax), Options, 500),
2055 option(top_max(BottomMax), Options, 500)
2056 },
2057 html(table(class(block),
2058 [ \sp_header(Object)
2059 | \table_rows_top_bottom(sp_row(Options,Object), SPList,
2060 TopMax, BottomMax)
2061 ])).
2062
(_) -->
2064 html(tr([ th('Subject'),
2065 th('Predicate')
2066 ])).
2067
2068sp_row(Options, _O, S-P) -->
2069 html([ td(class(subject), \rdf_link(S, Options)),
2070 td(class(predicate), \rdf_link(P, Options))
2071 ]).
2072
2073
2074
2075
2076
2077 2080
2084
2085sort_by_label(URIs, Sorted) :-
2086 map_list_to_pairs(label_sort_key, URIs, LabelPairs),
2087 keysort(LabelPairs, SortedPairs),
2088 pairs_values(SortedPairs, Sorted).
2089
2090label_sort_key(URI, Key) :-
2091 label_of(URI, Label),
2092 ( atom(Label)
2093 -> collation_key(Label, Key)
2094 ; Key = Label
2095 ).
2096
2097label_of(URI, Label) :-
2098 rdf_is_resource(URI), !,
2099 rdf_display_label(URI, Label).
2100label_of(Literal, Label) :-
2101 literal_text(Literal, Label).
2102
2103
2107
2108sort_triples_by_label(Pairs, Sorted) :-
2109 map_list_to_pairs(key_triple_by_label, Pairs, LabelPairs),
2110 keysort(LabelPairs, SortedPairs),
2111 pairs_values(SortedPairs, Sorted).
2112
2113key_triple_by_label(rdf(S,P,O), rdf(SK,PK,OK)) :-
2114 label_sort_key(S, SK),
2115 label_sort_key(P, PK),
2116 label_sort_key(O, OK).
2117
2121
2122sort_pairs_by_label(Pairs, Sorted) :-
2123 map_list_to_pairs(key_label_sort_key, Pairs, LabelPairs),
2124 keysort(LabelPairs, SortedPairs),
2125 pairs_values(SortedPairs, Sorted).
2126
2127key_label_sort_key(R-_, Key) :-
2128 label_sort_key(R, Key).
2129
2130
2131 2134
2140
2141p_label(source(_), 'Source URL').
2142p_label(triples(G),
2143 ['# ', a(href(Link), triples)]) :-
2144 http_link_to_id(list_triples, [graph=G], Link).
2145p_label(subject_count(G),
2146 ['# ', a(href(Link), subjects)]) :-
2147 http_link_to_id(list_instances, [graph=G], Link).
2148p_label(bnode_count(G),
2149 ['# ', a(href(Link), 'bnode subjects')]) :-
2150 http_link_to_id(list_instances, [graph=G, type=bnode], Link).
2151p_label(predicate_count(G),
2152 ['# ', a(href(Link), predicates)]) :-
2153 http_link_to_id(list_predicates, [graph=G], Link).
2154p_label(type_count(G),
2155 ['# Referenced ', a(href(Link), classes)]) :-
2156 http_link_to_id(list_classes, [graph=G], Link).
2157
2158
2159 2162
2169
2170search(Request) :-
2171 http_parameters(Request,
2172 [ q(QueryText,
2173 [ description('Query to search for')
2174 ]),
2175 filter(FilterAtom,
2176 [ optional(true),
2177 description('Filter on raw matches (a Prolog term)')
2178 ])
2179 ]),
2180 ( var(FilterAtom)
2181 -> Filter = true
2182 ; atom_to_term(FilterAtom, Filter0, []),
2183 rdf_global_term(Filter0, Filter)
2184 ),
2185
2186 find_literals(QueryText, Literals, Query),
2187 literal_triples(Literals, Filter, Triples),
2188 reply_html_page(cliopatria(default),
2189 title('Search results for ~q'-[Query]),
2190 [ h1('Search results for token "~q"'-[Query]),
2191 \rdf_table(Triples, [])
2192 ]).
2193
2194find_literals(QueryText, [Query], exact(Query)) :-
2195 2196 sub_atom(QueryText,0,1,Remainder,'"'),
2197 sub_atom(QueryText,Remainder,1,0,'"'),!,
2198 sub_atom(QueryText,1,_,1,Query).
2199find_literals(QueryText, Literals, Query) :-
2200 2201 tokenize_atom(QueryText, Tokens),
2202 once(phrase(query(Query), Tokens)),
2203 rdf_find_literals(Query, Literals).
2204
2205query(Query) -->
2206 simple_query(Q1),
2207 ( eos
2208 -> {Query = Q1}
2209 ; query(Q2),
2210 {Query = and(Q1,Q2)}
2211 ).
2212
2213eos([],[]).
2214
2215simple_query(Token) -->
2216 ['"',Token,'"'], !.
2217simple_query(not(Token)) -->
2218 [-, Token].
2219simple_query(case(Token)) -->
2220 [Token].
2221
2226
2227literal_triples(Literals, Filter, Triples) :-
2228 sub_term(graph(Graph), Filter), !,
2229 phrase(ltriples(Literals, Graph, Filter), Triples).
2230literal_triples(Literals, Filter, Triples) :-
2231 phrase(ltriples(Literals, Filter), Triples).
2232
2233
2234ltriples([], _, _) --> [].
2235ltriples([H|T], G, F) -->
2236 findall(rdf(S,P,literal(L)),
2237 ( rdf(S,P,literal(exact(H), L),G),
2238 search_filter(F, S)
2239 )),
2240 ltriples(T, G, F).
2241
2242ltriples([], _) --> [].
2243ltriples([H|T], F) -->
2244 findall(rdf(S,P,literal(L)),
2245 ( rdf(S,P,literal(exact(H), L)),
2246 search_filter(F, S)
2247 )),
2248 ltriples(T, F).
2249
2255
2256rdf_table(Triples, Options) -->
2257 { option(top_max(TopMax), Options, 500),
2258 option(top_max(BottomMax), Options, 500)
2259 },
2260 html(table(class(block),
2261 [ tr([ th('Subject'), th('Predicate'), th('Object') ])
2262 | \table_rows_top_bottom(triple, Triples,
2263 TopMax, BottomMax)
2264 ])).
2265
2266triple(rdf(S,P,O)) -->
2267 html([ td(class(subject), \rdf_link(S)),
2268 td(class(predicate), \rdf_link(P)),
2269 td(class(object), \rdf_link(O))
2270 ]).
2271
2272
2273 2276
2284
2285html_property_table(Template, Goal) -->
2286 { findall(Template, Goal, Rows) },
2287 html(table(class(block),
2288 \table_rows(prow, Rows))).
2289
2290prow(Row) -->
2291 { Row =.. [_,H|Cells],
2292 ( p_label(H, Label0)
2293 -> true
2294 ; functor(H, Label0, _)
2295 ),
2296 ( is_list(Label0)
2297 -> append(Label0, [:], Label)
2298 ; Label = [Label0, :]
2299 )
2300 },
2301 html([ th(class(p_name), Label)
2302 | \pcells(Cells)
2303 ]).
2304
2305pcells([]) --> [].
2306pcells([H|T]) -->
2307 pcell(H),
2308 pcells(T).
2309
2310pcell(int(Value)) -->
2311 { integer(Value) }, !,
2312 nc('~D', Value).
2313pcell(H) -->
2314 { compound(H),
2315 H =.. [Class,Value], !
2316 },
2317 html(td(class(Class), Value)).
2318pcell(H) -->
2319 html(td(H)).
2320
2321
2335
2336table_rows(Goal, Rows) -->
2337 table_rows(Rows, Goal, 1, -1).
2338
2339table_rows_top_bottom(Goal, Rows, inf, inf) --> !,
2340 table_rows(Rows, Goal, 1, -1).
2341table_rows_top_bottom(Goal, Rows, MaxTop, MaxBottom) -->
2342 { length(Rows, Count) },
2343 ( { MaxTop+MaxBottom >= Count }
2344 -> table_rows(Rows, Goal, 1, -1)
2345 ; { Skip is Count-MaxBottom,
2346 delete_list_prefix(Skip, Rows, BottomRows),
2347 Skipped is Count-(MaxTop+MaxBottom)
2348 },
2349 table_rows(Rows, Goal, 1, MaxTop),
2350 html(tr(class(skip),
2351 [ th(colspan(10), 'Skipped ~D rows'-[Skipped])
2352 ])),
2353 table_rows(BottomRows, Goal, 1, -1)
2354 ).
2355
2356table_rows(_, _, _, 0) --> !, [].
2357table_rows([], _, _, _) --> [].
2358table_rows([H|T], Goal, N, Left) -->
2359 { N2 is N + 1,
2360 ( N mod 2 =:= 0
2361 -> Class = even
2362 ; Class = odd
2363 ),
2364 Left2 is Left - 1
2365 },
2366 html(tr(class(Class), \call(Goal, H))),
2367 table_rows(T, Goal, N2, Left2).
2368
2369delete_list_prefix(0, List, List) :- !.
2370delete_list_prefix(_, [], []) :- !.
2371delete_list_prefix(N, [_|T], List) :-
2372 N2 is N - 1,
2373 delete_list_prefix(N2, T, List).
2374
2378
2379list_prefixes(Request) :-
2380 Formats = [html,turtle],
2381 http_parameters(Request,
2382 [ format(Format,
2383 [ oneof(Formats),
2384 description('Output format'),
2385 default(html)
2386 ])
2387 ]),
2388 findall(Prefix-URI,
2389 rdf_current_ns(Prefix, URI),
2390 Pairs),
2391 keysort(Pairs, Sorted),
2392 reply_html_page(cliopatria(default),
2393 title('RDF prefixes (namespaces)'),
2394 [ h1('Known RDF prefixes (namespaces)'),
2395 \explain_prefixes,
2396 \ns_table(Format, Sorted),
2397 \prefix_formats(Formats, Format, Request)
2398 ]).
2399
2400explain_prefixes -->
2401 html(p([ 'The following prefixes are known and may be used \c
2402 without declaration in SPARQL queries to this server.'
2403 ])).
2404
2405prefix_formats(Formats, Format, Request) -->
2406 { select(Format, Formats, Alt)
2407 },
2408 html(p(class('prefix-format'),
2409 [ 'Also available in ',
2410 \alt_formats(Alt, Request)
2411 ])).
2412
2413alt_formats([], _) --> [].
2414alt_formats([H|T], Request) -->
2415 { http_reload_with_parameters(Request, [format(H)], HREF)
2416 },
2417 html(a(href(HREF), H)),
2418 ( {T==[]}
2419 -> []
2420 ; html(' and '),
2421 alt_formats(T, Request)
2422 ).
2423
2424ns_table(html, Pairs) -->
2425 html(table(class(block),
2426 [ \prefix_table_header,
2427 \table_rows(prefix_row, Pairs)
2428 ])).
2429ns_table(turtle, Pairs) -->
2430 html(pre(class(code),
2431 \turtle_prefixes(Pairs))).
2432
-->
2434 html(tr([ th('Prefix'),
2435 th('URI')
2436 ])).
2437
2438prefix_row(Prefix-URI) -->
2439 html([ td(Prefix),
2440 td(URI)
2441 ]).
2442
2443turtle_prefixes(Pairs) -->
2444 { longest_prefix(Pairs, 0, Length),
2445 PrefixCol is Length+10
2446 },
2447 turtle_prefixes(Pairs, PrefixCol).
2448
2449longest_prefix([], L, L).
2450longest_prefix([Prefix-_|T], L0, L) :-
2451 atom_length(Prefix, L1),
2452 L2 is max(L0, L1),
2453 longest_prefix(T, L2, L).
2454
2455turtle_prefixes([], _) --> [].
2456turtle_prefixes([Prefix-URI|T], Col) -->
2457 html('@prefix ~t~w: ~*|<~w> .~n'-[Prefix, Col, URI]),
2458 turtle_prefixes(T, Col).