1:- module(dataframe,
    2          [dataframe_header/2,
    3           dataframe_header/3,
    4           dataframe_row/2,
    5           dataframe_row/3,
    6           dataframe_row/4,
    7
    8           dataframe_to_csv/2,
    9           dataframe_to_csv/3]).   10
   11:- use_module(library(semweb/rdf11)).
dataframe(foo, [id=ID, name=Name]-person(ID,Name), [street=Street, city=City]-address(ID,Street,City), ...], [description('person and location report'), entity(city)]).

TODO

frame('person location report') << foo(id=ID :: entity, name=Name) where person(ID,Name), foo(street=Street, city=City :: entity) where address(ID,Street,City).

*/

   35:- multifile dataframe/2.   36:- multifile dataframe/3.   37
   38dataframe_specs_opts(Name,Specs,Opts) :- dataframe(Name,Specs,Opts).
   39dataframe_specs_opts(Name,Specs,[]) :- dataframe(Name,Specs).
   40
   41:- module_transparent(dataframe_header/2).   42:- module_transparent(dataframe_header/3).   43dataframe_header(Name,Header) :-
   44        dataframe_header(Name,Header,[]).
   45dataframe_header(Name,Header,Opts) :-
   46        dataframe_specs_opts(Name,Specs,SpecOpts),
   47        findall(K,
   48                (   member(Sub-_,Specs),
   49                    member(K=_,Sub)),
   50                Header1),
   51        inject_labels_to_header(Header1,Header,SpecOpts,Opts).
   52
   53inject_labels_to_header([],[],_,_).
   54inject_labels_to_header([K|L],[K,KN|L2],SpecOpts,Opts) :-
   55        member(entity(K),SpecOpts),
   56        !,
   57        atom_concat(K,' label',KN),
   58        inject_labels_to_header(L,L2,SpecOpts,Opts).
   59inject_labels_to_header([K|L],[K|L2],SpecOpts,Opts) :-
   60        inject_labels_to_header(L,L2,SpecOpts,Opts).
   61
   62
   63
   64:- module_transparent(dataframe_row/2).   65:- module_transparent(dataframe_row/3).   66:- module_transparent(dataframe_row/4).   67dataframe_row(Name,Row) :-
   68        dataframe_row(Name,Row,[]).
   69dataframe_row(Name,Row,Opts) :-
   70        dataframe_specs_opts(Name,_Specs,SpecOpts),
   71        dataframe_row(Name,Row,Opts,SpecOpts).
   72dataframe_row(Name,Row,Opts,SpecOpts) :-
   73        dataframe_specs_opts(Name,[Spec|Specs],SpecOpts),
   74        spec_bindings_goal(Spec,Bs,G),
   75        dataframe_header(Name,Header,Opts),
   76        call_wrap(G,SpecOpts),
   77        maplist([_=V,V]>>true,Bs,Row1),
   78        apply_specs(Specs,Row2),
   79        append(Row1,Row2,Row3),
   80        flatten_row(Row3,Row,Header,[Spec|Specs],SpecOpts,Opts).
   81
   82:- module_transparent(call_wrap/2).   83call_wrap(G,Opts) :-
   84        member(endpoint(Endpoint),Opts),
   85        !,
   86        ??(Endpoint, G).
   87call_wrap(G,_) :-
   88        G.
   89
   90:- module_transparent(apply_specs/2).   91apply_specs([],[]).
   92apply_specs([Spec|Specs],Row) :-
   93        spec_bindings_goal(Spec,Bs,G),
   94        (   setof(Bs,G,BsSet)
   95        ->  true
   96        ;   BsSet=[]),
   97        maplist({BsSet}/[Var=_,Vals] >> findall(X,(member(Bs1,BsSet),member(Var=X,Bs1)),Vals),Bs,Row1),
   98        append(Row1,Row2,Row),
   99        apply_specs(Specs,Row2).
  100
  101:- module_transparent(spec_bindings_goal/3).  102spec_bindings_goal(Bs-G,Bs,G).
  103
  104
  105
  106flatten_row([],[],_,_,_,_).
  107flatten_row([V|Row],Row2,Keys,Specs,SpecOpts,Opts) :-
  108        Keys=[K|_],
  109        select(entity(K),SpecOpts,SpecOpts2),
  110        !,
  111        get_labels(V,Ns,Opts),
  112        contract_uris(V,V2,Opts),
  113        flatten_row([V2,Ns|Row],Row2,Keys,Specs,SpecOpts2,Opts).
  114flatten_row([V|Row],[V2|Row2],Keys,Specs,SpecOpts,Opts) :-
  115        Keys=[K|Keys2],
  116        select(iri(K),SpecOpts,SpecOpts2),
  117        !,
  118        contract_uris(V,V1,Opts),
  119        serialize_value(V1,V2,Opts),
  120        flatten_row(Row,Row2,Keys2,Specs,SpecOpts2,Opts).
  121flatten_row([V|Row],[V2|Row2],[_K|Keys],Specs,SpecOpts,Opts) :-
  122        serialize_value(V,V2,Opts),
  123        flatten_row(Row,Row2,Keys,Specs,SpecOpts,Opts).
  124
  125% todo: hook
  126get_labels(L,Vs,Opts) :-
  127        is_list(L),
  128        !,
  129        maplist({Opts}/[X,X2]>>get_labels(X,X2,Opts),L,L2),
  130        flatten(L2,Vs).
  131get_labels(X,V,_Opts) :-
  132        rdf_is_iri(X),
  133        rdf(X,rdfs:label,V),
  134        !.
  135get_labels(_,'',_).
  136
  137contract_uris(L,L2,Opts) :- is_list(L),!,maplist({Opts}/[A,B]>>contract_uri(A,B,Opts),L,L2).
  138contract_uris(A,B,Opts) :- contract_uri(A,B,Opts).
  139contract_uri(A,B,_) :- rdf_is_iri(A),rdf_global_id(Pre:Local,A),!,concat_atom([Pre,Local],':',B).
  140contract_uri(A,A,_).
  141
  142
  143serialize_value(V,'_',_) :-
  144        var(V),
  145        !.
  146serialize_value(L,V,Opts) :-
  147        is_list(L),
  148        !,
  149        maplist({Opts}/[X,X2]>>serialize_value(X,X2,Opts),L,L2),
  150        option(internal_sep(Sep),Opts,'|'),
  151        concat_atom(L2,Sep,V).
  152serialize_value(S@_,V,_) :-
  153        !,
  154        atom_string(V,S).
  155serialize_value(S^^_,V,_) :-
  156        !,
  157        atom_string(V,S).
  158serialize_value(literal(type(_,V)),V,_) :-
  159        !.
  160serialize_value(literal(lang(_,V)),V,_) :-
  161        !.
  162serialize_value(literal(V),V,_) :-
  163        !.
  164serialize_value(S,V,_) :-
  165        string(S),
  166        !,
  167        atom_string(V,S).
  168serialize_value(X,V,_) :-
  169        sformat(S,'~w',[X]),
  170        string_to_atom(S,V).
  171
  172
  173:- module_transparent(dataframe_to_csv/2).  174:- module_transparent(dataframe_to_csv/3).  175dataframe_to_csv(Name, Opts) :-
  176        Stream = current_output,
  177        dataframe_to_csv(Name, Stream, Opts).
  178dataframe_to_csv(Name, Stream, Opts) :-
  179        dataframe_header(Name, Header, Opts),
  180        option(separator(Sep), Opts, 0'\t),
  181        HTerm =.. [row|Header],
  182        csv_write_stream(Stream, [HTerm], [separator(Sep)]),
  183        dataframe_specs_opts(Name,_Specs,SpecOpts),
  184        (   member(sort(_),SpecOpts)
  185        ->  findall(Row,dataframe_row(Name, Row, Opts, SpecOpts),Rows),
  186            % TODO sort by key
  187            sort(Rows,RowsSorted),
  188            forall((member(Row,RowsSorted), RTerm =.. [row|Row]),
  189                   csv_write_stream(Stream, [RTerm], [separator(Sep)]))
  190        ;   forall((dataframe_row(Name, Row, Opts, SpecOpts), RTerm =.. [row|Row]),
  191                   csv_write_stream(Stream, [RTerm], [separator(Sep)]))),
  192        !.
  193dataframe_to_csv(Name, Stream, Opts) :-
  194        throw(exception(no_dataframe(Name, Stream, Opts)))