#+title: ELOT library of babel
# SPDX-License-Identifier: GPL-3.0-or-later

# This file is part of ELOT's supported, user-facing API.  It
# collects =#+call:=-style helpers (most notably =rdfpuml-block=,
# =kill-prefixes=, the theme/setupfile helpers, =robot-metrics=,
# =robot-sparql-select=, and the date helpers) that ELOT users
# invoke from their ontology Org files.  =elot-mode= auto-ingests
# this file via =org-babel-lob-ingest= when it is enabled, so the
# named blocks below are available without any manual setup.
#
# The two named blocks =resource-declarations= and
# =resource-taxonomy= that previously lived under "export" have
# been retired together with the legacy LOB pipeline functions in
# =elot.el= (see ELPA-SUBMISSION-PLAN.org Milestone 2).  The new
# tangle pipeline in =elot-tangle.el= produces OMN directly from
# the Org outline and does not need those blocks.

* export
** readtheorg HTML setup file
#+name: theme-readtheorg
#+begin_src emacs-lisp :results drawer
(concat "#+SETUPFILE: " (file-name-directory (locate-library "elot")) "theme-readtheorg.setup")
#+end_src
** ELOT HTML setup file
#+name: theme-elot
#+begin_src emacs-lisp :results drawer
(concat "#+SETUPFILE: " (file-name-directory (locate-library "elot")) "theme-elot.setup")
#+end_src
* in document
** Dates for insertion into ontology on save
#+name: current-date
#+begin_src emacs-lisp :exports none
(format-time-string "%Y-%m-%d")
#+end_src

#+name: current-datetime
#+begin_src emacs-lisp :exports none
  (format-time-string "%Y-%m-%dT%H:%M:%SZ" nil t)
#+end_src
** ROBOT metrics
#+name: robot-metrics
#+begin_src emacs-lisp :var omnfile="pizza.omn" 
  (let ((measure-file
         (concat (org-babel-temp-directory) "/"
                 (file-name-sans-extension omnfile)
                 ".measure")))
  (elot-robot-command 
   (concat "measure --input " omnfile
           " --output " measure-file))
  (let* ((lines (with-temp-buffer
                 (insert-file-contents measure-file)
                 (split-string (buffer-string) "\n")))
         (header (split-string (car lines) "\t"))
         (body (mapcar
                (lambda (line) (split-string line "\t"))
                (butlast (cdr lines)))))
    (cons header (cons 'hline body))))
#+end_src
** ROBOT sparql
#+name: robot-sparql-select
#+begin_src emacs-lisp :var omnfile="pizza.omn" query="myquery"
  (let* ((query-file
          (concat (org-babel-temp-directory) "/"
                  query
                  ".sparql"))
         (result-file (concat (file-name-sans-extension omnfile) ".tsv"))
         (qryblock (org-babel-lob--src-info query))
         (qrytext (cadr qryblock)))
    (setcar (nthcdr 1 qryblock)
            (concat "<<sparql-prefixes()>>\n" qrytext))
    (let ((prefixedquery
           (org-babel-expand-noweb-references qryblock)))
      (with-temp-file query-file (insert prefixedquery)))
    (elot-robot-command
     (concat "query --input " omnfile
             " --format TSV"
             " --query " query-file
             " " result-file))
    (elot-tsv-to-table result-file))
#+end_src

** Class hierarchy as SPARQL construct
#+name: ttl-Class-hierarchy
#+header: :var top="owl:Thing" :noweb yes :format ttl
#+begin_src sparql :url "pizza.omn" :wrap "src ttl" :cache yes :eval never-export
  construct {
    ?c a owl:Class .
    ?subC a owl:Class .
    ?subC rdfs:subClassOf ?c .
  }{
    values ?cTop { $top }
    ?c a owl:Class ;
      rdfs:subClassOf* ?cTop .
    optional { ?subC rdfs:subClassOf ?c }
  }
#+end_src
* sparql and rdfpuml
** Diagrams with rdfpuml
*** Config for rdfpuml colours and directions
#+name: rdfpuml-config
#+begin_src ttl
# customization
@prefix puml: <http://plantuml.com/ontology#> .
owl:Class      puml:stereotype "(C,Orange)".
rdfs:subClassOf puml:arrow puml:up-tri .
rdfs:subPropertyOf puml:arrow puml:up-tri .
owl:Restriction      puml:stereotype "(C,Orange)".
owl:ObjectProperty      puml:stereotype "(O,LightBlue)".
owl:DatatypeProperty      puml:stereotype "(D,LightGreen)".
owl:NamedIndividual      puml:stereotype "(i,White)".
owl:Axiom      puml:stereotype "(X,LightYellow)".
owl:annotatedSource puml:arrow puml:gray .
owl:annotatedProperty puml:arrow puml:gray .
owl:annotatedTarget puml:arrow puml:gray .
rdf:in puml:arrow puml:up .
rdfs:domain puml:arrow puml:left-gray .
rdfs:range puml:arrow puml:right-gray .
owl:onProperty puml:arrow puml:up .
owl:someValuesFrom puml:arrow puml:right .
owl:allValuesFrom puml:arrow puml:right .
owl:equivalentClass puml:arrow puml:left-none .
owl:equivalentProperty puml:arrow puml:left-none .
#+end_src

To make the =lis:hasPart= diagram more compact, make =rdfs:domain= and
=rdfs:range= point downwards with this alternative configuration.
#+name: rdfpuml-config-rangedomaindown
#+begin_src ttl :noweb yes
<<rdfpuml-config>>
rdfs:domain puml:arrow puml:down .
rdfs:range puml:arrow puml:down .
#+end_src

Another alternative, making range point down, but not domain.
#+name: rdfpuml-config-rangedown
#+begin_src ttl :noweb yes
<<rdfpuml-config>>
rdfs:range puml:arrow puml:down .
#+end_src
*** PDF or SVG images?
When exporting to a PDF document, it's much easier to use PDF. Support
for SVG in LaTeX is not great.

On the other hand, inclusion in MS Word or export to HTML calls for
SVGs. Conversion is described in 
*** rdfpuml-block will read a ttl block and output a diagram
#+name: rdfpuml-block
#+header: :var ttlblock="example-ttl"
#+header: :var prefixes=(elot-prefix-block-from-alist org-link-abbrev-alist-local 'ttl)
#+header: :var config=rdfpuml-config
#+header: :var add-options="left to right direction"
#+header: :var epilogue='()
#+header: :var format="svg"
#+begin_src emacs-lisp :results file
  (let ((ttl (org-babel-ref-resolve ttlblock)))
    (message (concat "Converting " ttlblock " to PlantUML ..."))
    (let ((puml-file 
          (elot-rdfpuml-execute ttl prefixes config add-options epilogue)))
      (elot-plantuml-execute puml-file ttlblock format)))
#+end_src


#+RESULTS[fd2e7835ef9c2388a22067ffadf7e94d13a5052f]: rdfpuml-block
[[file:./images/example-ttl.png]]

*** Test ttl block
#+name: example-ttl
#+BEGIN_SRC ttl :exports none
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix skos:  <http://www.w3.org/2004/02/skos/core#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ex:    <http://example.org/> .

ex:House  a         ex:Classifier ;
        rdfs:label  "House" .

ex:ArchitecturalTerms
        skos:member  ex:House .

#+END_SRC
To get the ttl block to process correctly: add this to startup.
#+begin_src emacs-lisp
(defun elot-org-babel-execute:passthrough (body params) body)
(unless (fboundp 'org-babel-execute:ttl)                
  (defalias 'org-babel-execute:ttl 'elot-org-babel-execute:passthrough))
#+end_src
*** Test run
#+call: rdfpuml-block(ttlblock="example-ttl", config="rdfpuml-config", format="png") :dir ~ :cache yes

#+RESULTS[5c2001466d943d4188759afde0d6f6e5a23fd62e]:
[[file:example-ttl.png]]

*** Use sparql as ttl source
Querying a local Fuseki endpoint. Note that this isn't going to be
useful if the Turtle data comes from template expansions.
#+name: fuseki-for-rdfpuml-ttl
#+begin_src sparql :url http://localhost:3030/ELOT/sparql :wrap "src ttl" :results output code :cache yes
  PREFIX owl: <http://www.w3.org/2002/07/owl#>
  PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
  construct { ?x rdfs:subClassOf ?z
  }{
    ?x a owl:Class ; rdfs:subClassOf ?z
  } limit 2
#+end_src

#+RESULTS:
#+begin_src ttl
@prefix :         <http://example.org/elot-template#> .
@prefix dc:       <http://purl.org/dc/elements/1.1/> .
@prefix dcmitype: <http://purl.org/dc/dcmitype/> .
@prefix dcterms:  <http://purl.org/dc/terms/> .
@prefix dol:      <http://www.loa-cnr.it/ontologies/DOLCE-Lite.owl#> .
@prefix foaf:     <http://xmlns.com/foaf/0.1/> .
@prefix iof-av:   <https://spec.industrialontologies.org/ontology/core/meta/AnnotationVocabulary/> .
@prefix lis:      <http://rds.posccaesar.org/ontology/lis14/rdl/> .
@prefix obo:      <http://purl.obolibrary.org/obo/> .
@prefix om:       <http://www.ontology-of-units-of-measure.org/resource/om-2/> .
@prefix owl:      <http://www.w3.org/2002/07/owl#> .
@prefix pav:      <http://purl.org/pav/> .
@prefix prov:     <http://www.w3.org/ns/prov#> .
@prefix rdf:      <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:     <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos:     <http://www.w3.org/2004/02/skos/core#> .
@prefix ssn:      <http://www.w3.org/ns/ssn/> .
@prefix time:     <http://www.w3.org/2006/time#> .
@prefix xml:      <http://www.w3.org/XML/1998/namespace> .
@prefix xsd:      <http://www.w3.org/2001/XMLSchema#> .

lis:MaterialCompositionQuality
        rdfs:subClassOf  lis:Quality .

lis:Organism  rdfs:subClassOf  lis:PhysicalObject .
#+end_src

The =ttlblock= argument can point to a named query.
#+call: rdfpuml-block(ttlblock="fuseki-for-rdfpuml-ttl", config="rdfpuml-config", format="pdf") :dir ~ :cache yes

#+ATTR_LATEX: :width 8cm
#+RESULTS[fa175dd1ac5b97a422a799a4e749956f0e116096]:
[[file:fuseki-for-rdfpuml-ttl.pdf]]

** Kill prefixes in Turtle output
This is useful when there's too many prefix definitions
#+name: kill-prefixes
#+begin_src emacs-lisp :var data=""
  (replace-regexp-in-string "^[[:blank:]]*[@]*prefix.*:.*\n\n*" "" data)
#+end_src

