about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md32
-rw-r--r--gn/data/dataset.scm61
-rw-r--r--gn/data/genotype.scm1
-rw-r--r--gn/db/mysql.scm14
-rwxr-xr-x[-rw-r--r--]gn/db/sparql.scm83
-rw-r--r--gn/runner/gemma.scm15
-rw-r--r--manifest.scm24
-rw-r--r--web/README.md1
-rw-r--r--web/view/brand/aging.scm7
-rw-r--r--web/view/brand/msk.scm2
-rw-r--r--web/view/doc.scm2
-rw-r--r--web/view/view.scm12
-rw-r--r--web/webserver.scm86
13 files changed, 270 insertions, 70 deletions
diff --git a/README.md b/README.md
index ad98170..923b64e 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,16 @@ The current repository lives at
 git clone tux02.genenetwork.org:/home/git/public/gn-guile
 ```
 
+or
+
+```
+git remote add gn git.genenetwork.org:/home/git/public/gn-guile
+```
+
 GNU Guile allows you to develop against a live running web server using emacs-geiser on port 1970. To try this fire up the web server from the `web` directory as
 
 ```sh
-guix shell -L ~/guix-bioinformatics --container --network --file=guix.scm -- guile -L . --fresh-auto-compile --listen=1970 -e main web/webserver.scm 8091
+guix shell -L ~/guix-bioinformatics -m manifest.scm --container --network --file=guix.scm -- guile -L . --fresh-auto-compile --listen=1970 -e main web/webserver.scm 8091
 ```
 
 By default the root points to the API:
@@ -42,7 +48,15 @@ curl http://127.0.0.1:8091/home/aging
 
 The `--container` option runs the code in an isolated container, and the `--network` option connects that container's networking to the host to allow you to access the running service.
 
-If you get an error `no code for module (gn packages guile)` add the appropriate guix-bioinformatics repo to the load path (`-L` switch).
+If you get an error `no code for module (gn packages guile)` add the appropriate guix-bioinformatics repo to the load path (`-L` switch). Note that, instead of hard-wiring guix-bioinformatics, the recommended way is to use a guix channel as defined in [guix-channel](./.guix-channel).
+
+We recommend checking the Guix documentation for manifests, channels and guix.scm definitions.
+
+To run a standalone server you should run without the listener on port 1970:
+
+```
+guix shell -L ~/guix-bioinformatics -m manifest.scm --container --network --file=guix.scm -- guile -L . --fresh-auto-compile -e main web/webserver.scm 8091
+```
 
 ## Welcome to the world of interactive Lisp programming
 
@@ -50,15 +64,21 @@ Next fire up emacs with `emacs-geiser-guile` and connect to the running web serv
 
 Note that you may have to try different versions of guile+emacs to succeed.
 
+Also these days it may be a better bet to use Andrew Tropin's alternatives ares and arei that are very powerful alternatives to geiser (with a more common lisp type interactive experience):
+
+=> https://git.sr.ht/~abcdw/guile-ares-rs
+=> https://git.sr.ht/~abcdw/emacs-arei
+
+
 # Tools
 
 Some tooling and scripts that run independently are stored in `./scripts`.
 
-# Development
+# Forwarding a MySQL port
 
-```
-git remote add gn git.genenetwork.org:/home/git/public/gn-guile
-```
+You may want to forward a mysql port if there is no DB locally you can do something like:
+
+ssh -L 3306:127.0.0.1:3306 -f -N tux02.genenetwork.org
 
 # Topics
 
diff --git a/gn/data/dataset.scm b/gn/data/dataset.scm
index c28cf25..afe75ba 100644
--- a/gn/data/dataset.scm
+++ b/gn/data/dataset.scm
@@ -4,14 +4,21 @@
   #:use-module (ice-9 iconv)
   #:use-module (ice-9 receive)
   #:use-module (ice-9 string-fun)
+  #:use-module (srfi srfi-1)
   #:use-module (dbi dbi)
   #:use-module (gn db mysql)
+  #:use-module (gn data genotype)
   #:use-module (gn data group)
   #:use-module (gn util convert)
   #:use-module (web gn-uri)
+  #:use-module (rnrs base) ; for assert
 
   #:export (
             dataset-name
+            get-bxd-publish-list
+            get-bxd-publish-values-list
+            get-bxd-publish-name-value-dict
+            get-bxd-publish-dataid-name-value-dict
             ))
 
 (define (get-dataset db probesetfreeze-id)
@@ -22,3 +29,57 @@
 
 (define (dataset-name db probesetfreeze-id)
   (assoc-ref (get-dataset db probesetfreeze-id) "Name"))
+
+(define (get-dataid-from-publishxrefid id)
+  "Get the internal dataid from publishxref - which is the same as used in the GN2 web interface"
+  (call-with-db
+   (lambda (db)
+     (let [(query (string-append "SELECT Id,PhenotypeId,DataId FROM PublishXRef WHERE Id=" id " AND InbredSetId=1 LIMIT 1"))]
+       (dbi-query db query)
+       (pk (int-to-string (assoc-ref (get-row db) "DataId")))))))
+
+(define (get-bxd-publish-list)
+  (call-with-db
+   (lambda (db)
+     (let [(query "SELECT Id,PhenotypeId,DataId FROM PublishXRef WHERE InbredSetId=1")]
+       (dbi-query db query)
+       (get-rows db '())))))
+
+(define* (get-bxd-publish-values-list dataid #:optional used-for-mapping?)
+  "Returns dict of name values , e.g. [{\"Name\":\"C57BL/6J\",\"value\":9.136},{\"Name\":\"DBA/2J\",\"value\":4.401},{\"Name\":\"BXD9\",\"value\":4.36}, ... used-for-mapping? skips the founders and maybe other unmappable inds. Note, currently unused."
+  (call-with-db
+   (lambda (db)
+     (let [(query (string-append "SELECT Strain.Name, PublishData.value FROM Strain, PublishData WHERE PublishData.Id=" dataid " and Strain.Id=StrainID;"))]
+       (dbi-query db query)
+       (if used-for-mapping?
+           (remove null? (pk (get-rows-apply db
+                                             (lambda (r)
+                                               (if (string-contains (assoc-ref r "Name") "BXD")
+                                                   `(("Name" . ,(assoc-ref r "Name")) ("value" . ,(assoc-ref r "value")))
+                                                   '() ) ;; return empty on no match
+                                               ) '())))
+           (get-rows db '())
+           )))))
+
+(define* (get-bxd-publish-dataid-name-value-dict dataid #:optional used-for-mapping?)
+  "Returns dict of name values, e.g. (((\"C57BL/6J\" . 9.136) (\"DBA/2J\" . 4.401) (\"BXD9\" . 4.36) ... used-for-mapping? skips the founders and maybe other unmappable inds."
+  (call-with-db
+   (lambda (db)
+     (let [(query (string-append "SELECT Strain.Name, PublishData.value FROM Strain, PublishData WHERE PublishData.Id=" dataid " and Strain.Id=StrainID;"))]
+       (dbi-query db query)
+       (if used-for-mapping?
+           (remove null? (pk (get-rows-apply db
+                                             (lambda (r)
+                                               (if (string-contains (assoc-ref r "Name") "BXD")
+                                                   `(,(assoc-ref r "Name") . ,(assoc-ref r "value"))
+                                                   '() ) ;; return empty on no match
+                                               ) '())))
+           (remove null? (pk (get-rows-apply db
+                                             (lambda (r)
+                                               `(,(assoc-ref r "Name") . ,(assoc-ref r "value"))
+                                               ) '())))
+           )))))
+
+(define* (get-bxd-publish-name-value-dict id #:optional used-for-mapping?)
+  "Same as above function, but starting from data id"
+  (get-bxd-publish-dataid-name-value-dict (get-dataid-from-publishxrefid id) used-for-mapping?))
diff --git a/gn/data/genotype.scm b/gn/data/genotype.scm
index c7cb63c..5574382 100644
--- a/gn/data/genotype.scm
+++ b/gn/data/genotype.scm
@@ -16,6 +16,7 @@
             ))
 
 (define (geno-inds-bxd fn)
+  "Returns information from GN's BXD.json, note it fetches the first geno file info, now BXD.8.geno"
   (let [(js (call-with-input-file fn
               (lambda (port)
                 (json->scm port))))]
diff --git a/gn/db/mysql.scm b/gn/db/mysql.scm
index ccd414a..8da7b60 100644
--- a/gn/db/mysql.scm
+++ b/gn/db/mysql.scm
@@ -32,22 +32,26 @@
     ;; (display "===> OPENING DB")
     ;; (newline)
     (let [(db (dbi-open "mysql" "webqtlout:webqtlout:db_webqtl:tcp:127.0.0.1:3306"))]
-      (ensure db)
+      (ensure db "Can't open connection")
       db
     )))
 
 (define (call-with-db thunk)
-  (thunk (db-open)))
+  (let* [(db (db-open))
+         (result (thunk db))]
+    (dbi-close db)
+    result))
 
-(define (ensure db)
+(define (ensure db msg1)
   "Use DBI-style handle to report an error. On error the program will stop."
   (match (dbi-get_status db)
     ((stat . msg) (if (= stat 0)
                      #t
                      (begin
-                       (display msg)
+                       (display "SQL Connection ERROR! ")
+                       (display (string-append msg1 " - " msg)
                        (newline)
-                       (assert stat))))))
+                       (assert #f)))))))
 
 (define (has-result? db)
   "Return #t or #f if result is valid"
diff --git a/gn/db/sparql.scm b/gn/db/sparql.scm
index bd7a306..8722966 100644..100755
--- a/gn/db/sparql.scm
+++ b/gn/db/sparql.scm
@@ -18,10 +18,11 @@ the case.
   #:use-module (json)
   #:use-module (srfi srfi-1)
   #:use-module (web client)
+  #:use-module (web http)
   #:use-module (web gn-uri)
   #:use-module (web request)
+  #:use-module (web response)
   #:use-module (web uri)
-
   #:export (memo-sparql-species
             memo-sparql-species-meta
             sparql-species-meta
@@ -37,9 +38,11 @@ the case.
             strip-lang
             make-table
             make-pairs
-            )
-)
+	    sparql-http-get
+	    sparql-by-term))
 
+(define virtuoso-endpoint
+  (or (getenv "SPARQL-ENDPOINT") "http://localhost:8890/sparql/"))
 
 (define (strip-lang s)
   "Strip quotes and language tag (@en) from RDF entries"
@@ -58,9 +61,9 @@ the case.
 (define (gn-sparql-prefix query)
   (string-append
   "
-PREFIX gn:  <http://genenetwork.org/id/>
-PREFIX gnt: <http://genenetwork.org/term/>
-PREFIX gnc: <http://genenetwork.org/category/>
+PREFIX gn:  <http://rdf.genenetwork.org/v1/id/>
+PREFIX gnt: <http://rdf.genenetwork.org/v1/term/>
+PREFIX gnc: <http://rdf.genenetwork.org/v1/category/>
 PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
 
 " query))
@@ -108,46 +111,7 @@ Note this procedure works for GN, but does not yet work for wikidata"
 (define (tsv->scm text)
   "Split a TSV string into a list of fields. Returns list of names header) and rows"
   (let ([lst (map (lambda (f) (string-split f #\tab) ) (delete "" (string-split text #\newline)))])
-    (values (car lst) (cdr lst))
-  ))
-
-#!
-(define-values (names res) (sparql-species-meta))
-(define table (get-rows names res))
-(define recs '())
-(define h (compile-species recs table))
-(assoc "http://genenetwork.org/species_drosophila_melanogaster" h)
-(assoc-ref h "http://genenetwork.org/id/Drosophila_melanogaster")
-(define d (car h))
-(assoc-ref (list d) "http://genenetwork.org/species_drosophila_melanogaster")
-
-(scm->json #(1  (("2" . 3))))
-;; [1,{"2":3}]
-(scm->json #("http://genenetwork.org/species_drosophila_melanogaster" (("http://genenetwork.org/menuName" . "Drosophila") ("http://genenetwork.org/binomialName" . "Drosophila melanogaster") )))
-;; ["http://genenetwork.org/species_drosophila_melanogaster",{"http://genenetwork.org/menuName":"Drosophila","http://genenetwork.org/binomialName":"Drosophila melanogaster"}]
-l
-;; (("http://genenetwork.org/menuName" "Drosophila") ("http://genenetwork.org/name" "Drosophila") ("http://genenetwork.org/binomialName" "Drosophila melanogaster"))
-(scm->json (map (lambda (i) (cons (car i) (car (cdr i)))) l))
-;; {"http://genenetwork.org/menuName":"Drosophila","http://genenetwork.org/name":"Drosophila","http://genenetwork.org/binomialName":"Drosophila melanogaster"}
-
-
-curl -G https://query.wikidata.org/sparql -H "Accept: application/json; charset=utf-8" --data-urlencode query="SELECT DISTINCT * where {
-  wd:Q158695 wdt:P225 ?o .
-} limit 5"
-{
-  "head" : {
-    "vars" : [ "o" ]
-  },
-  "results" : {
-    "bindings" : [ {
-      "o" : {
-        "type" : "literal",
-        "value" : "Arabidopsis thaliana"
-      }
-    } ]
-  }
-}
-!#
+    (values (car lst) (cdr lst))))
 
 (define (sparql-wd-species-info species)
   "Returns wikidata entry for species, e.g.:
@@ -326,3 +290,30 @@ dump-species-metadata.ttl:gn:Axbxa gnt:belongsToSpecies gn:Mus_musculus .
             " gnid " ?key ?value .
             # FILTER ( !EXISTS{ " gnid " gnt:hasTissue ?value })
 }")))
+
+
+(define* (sparql-http-get endpoint-url query #:optional (mime-type "text/microdata+html"))
+  (receive (response-status response-body)
+      (http-request
+       (format #f "~a?default-graph-uri=&query=~a&format=~a"
+	       endpoint-url (uri-encode query) (uri-encode mime-type))
+       #:method 'GET)
+    (values
+     (build-response
+      #:code (response-code response-status)
+      #:headers `((content-type . ,(parse-header 'content-type mime-type))))
+     response-body)))
+
+(define (sparql-by-term prefix val)
+  (let ((url-alist '((gn . "<http://rdf.genenetwork.org/v1/id/>")
+		     (gnc . "<http://rdf.genenetwork.org/v1/category/>")
+		     (gnt . "<http://rdf.genenetwork.org/v1/term/>"))))
+    (format #f "PREFIX ~a: ~a
+
+CONSTRUCT {
+  ~a:~a ?p ?o .
+} FROM <http://rdf.genenetwork.org/v1>
+WHERE {
+  ~a:~a ?p ?o .
+  ?s ?p ?o .
+}" prefix (assoc-ref url-alist prefix) prefix val prefix val)))
diff --git a/gn/runner/gemma.scm b/gn/runner/gemma.scm
index 9a5c0fc..c577305 100644
--- a/gn/runner/gemma.scm
+++ b/gn/runner/gemma.scm
@@ -10,11 +10,24 @@
   #:use-module (rnrs base)
 
   #:export (
-            write-pheno-file
+            gemma-pheno-txt
             invoke-gemma-wrapper-loco
             run-gemma
             ))
 
+(define (gemma-pheno-txt family traits)
+  "Return a list of values for GEMMA"
+  (assert (string=? family "BXD")) ; only supported right now
+  (define bxd-inds (geno-inds-bxd "BXD.json"))
+  (assert (= 235 (length bxd-inds)))
+  (map (lambda (ind)
+         (let [(value (assoc-ref traits ind))]
+           (if value
+               (format #f "~a" value)
+               "NA\n")
+           ))
+       bxd-inds))
+
 (define (write-pheno-file fn traits)
   (define bxd-inds (geno-inds-bxd "BXD.json"))
   (assert (= 235 (length bxd-inds)))
diff --git a/manifest.scm b/manifest.scm
new file mode 100644
index 0000000..bc8f699
--- /dev/null
+++ b/manifest.scm
@@ -0,0 +1,24 @@
+;; please do not remove
+(specifications->manifest
+ '("coreutils"
+   "guile"
+   "guile-dbi"
+   "guile-dbd-mysql"
+   "guile-fibers"
+   "guile-json"
+   "guile-gnutls"
+   "guile-readline"
+   "guile-redis"
+   "openssl"
+   "nss-certs"
+   "gemma"
+   "parallel"
+   "tar"
+   "xz"
+   "python"
+   "python-lmdb"
+   "python-cffi"
+   "guile-gcrypt"
+   "guile-hashing"
+   "time"
+   "gemma-gn2"))
diff --git a/web/README.md b/web/README.md
new file mode 100644
index 0000000..fc7e158
--- /dev/null
+++ b/web/README.md
@@ -0,0 +1 @@
+Run the webserver from one directory up.
diff --git a/web/view/brand/aging.scm b/web/view/brand/aging.scm
index 19db4d7..f1c48c9 100644
--- a/web/view/brand/aging.scm
+++ b/web/view/brand/aging.scm
@@ -45,15 +45,14 @@
           ))
       ,@head)
      (body
-      ;; (header (p "TEST"))
       (main (@ (class "container"))
        (h1 ,title)
        (article
-               (img (@ (src "/static/images/ole-farmer.jpg") (alt "ol farmer by hohumhobo is licensed under CC BY 2.0") (width "400") (align "right")))
-,info)
+       ;;        (img (@ (src "/static/images/ole-farmer.jpg") (alt "ol farmer by hohumhobo is licensed under CC BY 2.0") (width "400") (align "right")))
+        ,info)
       (footer
        (hr)
-       (p "Copyright © 2005-2023 "
+       (p "Copyright © 2005-2025 "
           (a (@ (href "https://genenetwork.org/")) "GeneNetwork Webservices") " | GeneNetwork and this website runs fully on free software. See status and download the "
           (a (@ (href "https://ci.genenetwork.org/"))
              "source code") ".")))
diff --git a/web/view/brand/msk.scm b/web/view/brand/msk.scm
index 69c1253..4cbcec4 100644
--- a/web/view/brand/msk.scm
+++ b/web/view/brand/msk.scm
@@ -51,7 +51,7 @@
         (p ,info)
         (footer
        (hr)
-       (p "Copyright © 2005-2023 "
+       (p "Copyright © 2005-2025 "
           (a (@ (href "https://genenetwork.org/")) "GeneNetwork Webservices") " | GeneNetwork and this website runs fully on free software. See status and download the "
           (a (@ (href "https://ci.genenetwork.org/"))
              "source code") ".")))
diff --git a/web/view/doc.scm b/web/view/doc.scm
index 71112eb..cec4400 100644
--- a/web/view/doc.scm
+++ b/web/view/doc.scm
@@ -44,7 +44,7 @@
            ,(scm->json-string body #:pretty #t))
            ; (p ,(parse-html "<b>some raw really <i>text</i> here</b>"))
            (footer
-            (p "Copyright © 2005—2023 by the GeneNetwork community with a touch of " (span (@ (class "lambda")) "λ") "!")
+            (p "Copyright © 2005—2025 by the GeneNetwork community with a touch of " (span (@ (class "lambda")) "λ") "!")
             (p "This is free software. Download the "
                (a (@ (href "https://ci.genenetwork.org/"))
                   "source code") "."))
diff --git a/web/view/view.scm b/web/view/view.scm
index 4300863..a7592ad 100644
--- a/web/view/view.scm
+++ b/web/view/view.scm
@@ -15,7 +15,7 @@
   #:export (view-brand))
 
 
-(define (view-aging)
+(define (view-aging-home)
   (aging-html #:info
               `(
 		,(markdown-github->sxml "genenetwork/gn-docs/general/brand/aging/home.md")
@@ -45,7 +45,15 @@ data to benefit from the power of integrated datasets, please contact:")
 
 (define* (view-brand path)
   (match path
-    ("aging" (view-aging))
+    ("aging/um-het3" (aging-html #:info
+              `(,(markdown-github->sxml "genenetwork/gn-docs/general/brand/aging/home.md"))))
+    ("aging/UM-HET3" (aging-html #:info
+              `(,(markdown-github->sxml "genenetwork/gn-docs/general/brand/aging/home.md"))))
+    ("aging/UMHET-3" (aging-html #:info
+              `(,(markdown-github->sxml "genenetwork/gn-docs/general/brand/aging/home.md"))))
+    ("aging/umhet-3" (aging-html #:info
+              `(,(markdown-github->sxml "genenetwork/gn-docs/general/brand/aging/home.md"))))
+    ("aging" (view-aging-home))
     ("gnqa" (default-gn-template
                    "genenetwork/gn-docs/general/brand/gnqa/gnqa.md"
                    "GeneNetwork Question and Answer System"))
diff --git a/web/webserver.scm b/web/webserver.scm
index 880ab49..6a0bd37 100644
--- a/web/webserver.scm
+++ b/web/webserver.scm
@@ -7,6 +7,7 @@
              (ice-9 exceptions)
              (srfi srfi-1)
              (srfi srfi-11)
+             (srfi srfi-13)
              (srfi srfi-19)
              (srfi srfi-26)
              (rnrs io ports)
@@ -16,17 +17,24 @@
              (web request)
              (web response)
              (web uri)
-             (fibers web server)
+             (web server)
              (gn cache memoize)
              (web gn-uri)
              (gn db sparql)
+             (gn data dataset)
              (gn data species)
              (gn data group)
+             (gn runner gemma)
              (web sxml)
              (web view view)
              (web view doc)
              (web view markdown))
 
+(define (get-extension filename)
+  (let ((dot-pos (string-rindex filename #\.)))
+    (if dot-pos
+        (substring filename dot-pos) "")))
+
 (define +current-repo-path+
   (getenv "CURRENT_REPO_PATH"))
 
@@ -56,6 +64,16 @@ otherwise search for set/group data"
     (if taxoninfo taxoninfo
         (cdr (get-group-data id)))))
 
+(define (get-bxd-publish)
+  "Return a list of published datasets by their record ID. We add the dataset ID and phenotype ID for quick reference"
+  (list->vector (get-bxd-publish-list)))
+
+(define* (get-bxd-publish-dataid-values dataid #:optional used-for-mapping?)
+  (get-bxd-publish-dataid-name-value-dict dataid used-for-mapping?))
+
+(define* (get-bxd-publish-values dataid #:optional used-for-mapping?)
+  (get-bxd-publish-name-value-dict dataid used-for-mapping?))
+
 (define (get-gene-aliases genename)
   "Return a vector of aliases for genename."
   (list->vector (memo-sparql-wd-gene-aliases (memo-sparql-wd-geneids genename))))
@@ -83,7 +101,7 @@ otherwise search for set/group data"
     ("html" text/html)))
 
 (define (file-extension file-name)
-  (last (string-split file-name #\.)))
+  (last (string-split file-name #\.))) ;; FIXME: does not handle files with multiple dots
 
 (define* (render-static-image file-name
                               #:key (extra-headers '()))
@@ -127,6 +145,11 @@ otherwise search for set/group data"
         (lambda (port)
           (sxml->html (view-brand path) port))))
 
+(define (render-string str)
+  (list '((content-type application/txt))
+        (lambda (port)
+          (put-string port str))))
+
 (define (render-json json)
   (list '((content-type application/json))
         (lambda (port)
@@ -179,6 +202,24 @@ otherwise search for set/group data"
                                   `(("error" . ,key)
                                     ("msg" . ,msg)))))))
 
+(define (render-sparql request prefix val)
+  (let* ((mime (negotiate-mime request))
+	 (resp-mime (if (or (string-contains (symbol->string mime) "html")
+			    (string-contains (symbol->string mime) "microdata"))
+			'text/html
+			mime)))
+    (receive (sparql-header sparql-resp)
+	(sparql-http-get
+	 (or (getenv "SPARQL-ENDPOINT") "http://localhost:8890/sparql/")
+	 (sparql-by-term prefix val)
+	 (symbol->string mime))
+      (list `((content-type ,resp-mime))
+	    (lambda (port)
+	      (let ((resp (if (string? sparql-resp)
+			      sparql-resp
+			      (utf8->string sparql-resp))))
+		(put-string port resp)))))))
+
 (define (invalid-data? data target)
   (if (string? (assoc-ref data target))
       (if (string-null? (assoc-ref data target))
@@ -221,6 +262,14 @@ otherwise search for set/group data"
                                   `(("error" . ,key)
                                     ("msg" . ,msg)))))))
 
+(define (negotiate-mime request)
+  (let* ((headers (request-headers request))
+	 (accept (caar (assoc-ref headers 'accept))))
+    (if (or (eq? (string->symbol "*/*") accept)
+	    (eq? (string->symbol "text/html") accept))
+	'application/x-nice-microdata
+	accept)))
+
 (define (controller request body)
   (match-lambda
     (('GET)
@@ -235,6 +284,24 @@ otherwise search for set/group data"
      (render-static-image (string-append (dirname (current-filename)) "/static/images/" fn)))
     (('GET "home" path)
      (render-brand path)) ; branding route for /home/aging, /home/msk etc
+    (('GET "home" "aging" path)
+     (render-brand (string-append "aging/" path))) ; branding route subs of /home/aging/...
+    (('GET "dataset" "bxd-publish" "list")
+     (render-json (get-bxd-publish)))
+    (('GET "dataset" "bxd-publish" "dataid" "values" page)
+     (match (get-extension page)
+       (".json"
+        (render-json (get-bxd-publish-dataid-values (basename page ".json"))))
+       (else    (display "ERROR: unknown file extension"))))
+    (('GET "dataset" "bxd-publish" "values" page)
+     (match (get-extension page)
+       (".json"
+        (render-json (get-bxd-publish-values (basename page ".json"))))
+       ;; (".tsv"  (render-string "TEST1\nTEST2"))
+       ;; (".gemma" (render-string (string-join (gemma-pheno-txt "BXD" (get-bxd-publish-values (basename page ".gemma"))) "")))
+       (else    (display "ERROR: unknown file extension"))))
+    (('GET "dataset" "bxd-publish" "mapping" "values" (string-append dataid ".json"))
+     (render-json (get-bxd-publish-values dataid #t)))
     (('GET "doc" "species.html")
      (render-doc "doc" "species.html"
                  (get-species-meta)))
@@ -286,6 +353,16 @@ otherwise search for set/group data"
                 #\n)
           (render-json (get-id-data (list->string name))))
          (rest (render-json "NOP")))))
+    ;; RDF End-points
+    (('GET "v1" "id" id)
+     (render-sparql request 'gn id))
+
+    (('GET "v1" "category" category)
+     (render-sparql request 'gnc category))
+
+    (('GET "v1" "term" term)
+     (render-sparql request 'gnt term))
+
     (_ (not-found (request-uri request)))))
 
 (define (request-path-components request)
@@ -309,8 +386,9 @@ otherwise search for set/group data"
   ;; only way to update the handler reference held by the web server
   ;; would be to restart the web server.
   (run-server (cut handler <> <>)
-              #:addr (inet-pton AF_INET address)
-              #:port port))
+              'http
+              (list #:addr (inet-pton AF_INET address)
+                    #:port port)))
 
 (define (main args)
   (write (string-append "Starting Guile REST API " get-version " server!"))