aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.guix-channel8
-rwxr-xr-x.guix-shell7
-rw-r--r--.guix/modules/gn-guile.scm34
-rw-r--r--README.md138
-rw-r--r--doc/git-markdown-editor.md117
-rw-r--r--doc/gn-guile.md1
-rwxr-xr-xgn-guile.sh3
-rw-r--r--gn/db/sources/wikidata.scm29
-rw-r--r--gn/db/sparql.scm3
l---------[-rw-r--r--]guix.scm72
-rwxr-xr-xscripts/lmdb-publishdata-export.scm229
-rw-r--r--web/.guix-shell8
-rw-r--r--web/css/gn-template-style.css39
-rw-r--r--web/templates/genenetwork.scm18
-rw-r--r--web/view/view.scm4
-rw-r--r--web/webserver.scm34
17 files changed, 526 insertions, 219 deletions
diff --git a/.gitignore b/.gitignore
index 55bfd36..5f81cf8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ BXD.*
pheno.txt
GWA.json
K.json
+.aider*
diff --git a/.guix-channel b/.guix-channel
new file mode 100644
index 0000000..6e0982a
--- /dev/null
+++ b/.guix-channel
@@ -0,0 +1,8 @@
+(channel
+ (version 0)
+ (directory ".guix/modules")
+ (dependencies
+ (channel
+ (name guix-bioinformatics)
+ (url "https://git.genenetwork.org/guix-bioinformatics")
+ (branch "master")))) \ No newline at end of file
diff --git a/.guix-shell b/.guix-shell
deleted file mode 100755
index bc81e06..0000000
--- a/.guix-shell
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-#
-# . .guix-shell -- guile -L . --fresh-auto-compile --listen=1970
-
-echo "Create a shell to run tools."
-
-guix shell -L ~/guix-bioinformatics -C -D -F --network 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/.guix/modules/gn-guile.scm b/.guix/modules/gn-guile.scm
new file mode 100644
index 0000000..03f2b14
--- /dev/null
+++ b/.guix/modules/gn-guile.scm
@@ -0,0 +1,34 @@
+;; To use this file to build HEAD of gn-guile:
+;;
+;; guix build -f guix.scm
+;;
+;; To get a development container
+;;
+;; guix shell -C -D -f guix.scm
+;;
+
+(define-module (gn-guile)
+ #:use-module ((gn packages guile) #:select (gn-guile) #:prefix gn:)
+ #:use-module (guix gexp)
+ #:use-module (guix utils)
+ #:use-module (guix packages)
+ #:use-module (guix git-download))
+
+(define %source-dir (dirname (dirname (current-source-directory))))
+
+(define vcs-file?
+ (or (git-predicate %source-dir)
+ (const #t)))
+
+(define-public gn-guile
+ (package
+ (inherit gn:gn-guile)
+ (source
+ (local-file "../.."
+ "gn-guile-checkout"
+ #:recursive? #t
+ #:select? vcs-file?))))
+
+;; Add definition for tests should you need it, here.
+
+gn-guile
diff --git a/README.md b/README.md
index 8724a7e..d3326bd 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,17 @@
This directory provides a Guile web service incl. the new REST API. It is used in conjunction with the Python web services and (very much) WIP.
+## Run
+
+1. **Navigate to the Web Directory and Start the Server**
+
+Running the web server is documented in [guix script](./web/.guix-shell).
+
+```
+curl http://127.0.0.1:8091/version
+"4.0.0"
+```
+
# Documentation
Start with this file and then the documentation in [doc](./doc/gn-guile.md).
@@ -17,130 +28,22 @@ git clone tux02.genenetwork.org:/home/git/public/gn-guile
GNU Guile allows you to develop against a live running web server using emacs-geiser. To try this fire up the web server from the `web` directory as
```sh
-cd web
-unset GUIX_PROFILE
-. .guix-shell -- guile -L .. --listen=1970 -e main ./webserver.scm 8091
-```
-
-Note the leading dot. The .guix-shell is defined in `gn-guile/web` and loads required packages using GNU Guix.
-If you are on Debian you may need to unset GUIX_PROFILE first.
-
-Next fire up emacs with `emacs-geiser-guile` and connect to the running web server with `M-x geiser-connect` and the port `1970`. Now you can not only inspect procedures, but also update any prodedure on the live server using `C-M-x` and get updated output from the webserver!
-
-# Tools
-
-Some tooling and scripts that run independently are stored in `./scripts`.
-
-Here’s the entire markdown content combined into a single, copyable file:
-
-
-# Gn-Markdown
-
-Gn-Markdown is an API endpoint to edit, parse, and commit markdown files for gn-docs.
-
-## How to Test the APIs
-
-1. **Navigate to the Web Directory and Start the Server**
-
-```sh
-cd web
-export REPO_PATH=<repo_path>
-. .guix-shell -- guile -L .. --listen=1970 -e main ./webserver.scm 8091
+guix shell --container --network --file=guix.scm -- guile -L . --fresh-auto-compile --listen=1970 -e main web/webserver.scm 8091
```
-2. **Test Endpoints**
-
-The main endpoints provided are `/edit` and `/commit`. More endpoints may be added in the future.
-
-## Edit (GET)
+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.
-This is a GET request to retrieve a file's details. Make sure you pass a valid file_path as search_query (the path should be relative to the repo)
+## Welcome to the world of interactive Lisp programming
-**Request Example:**
+Next fire up emacs with `emacs-geiser-guile` and connect to the running web server with `M-x geiser-connect` and the port `1970`. Now you can not only inspect procedures, but also update any prodedure on the live server using `C-M-x` on code, call and get updated output from the webserver! No need to save/reload files and all that.
-```bash
-
-curl -G -d "file_path=test.md" localhost:8091/edit
-
-```
+Note that you may have to try different versions of guile+emacs to succeed.
-**Expected Success Response:**
-
-```json
-{
-"file_path": "test.md",
-"content": "Test for new user\n test 2 for line\n test 3 for new line\n ## real markdown two test\n",
-"hash": "ecd96f27c45301279150fbda411544687db1aa45"
-}
-```
-
-**Expected Error Response (Status 400):**
-
-```json
-{
-"error": <error_type>,
-"msg": <error_reason>
-}
-```
-
-## Commit (POST)
-
-This is a POST request to commit changes to a file.
-
-**Request URL:**
-
-```bash
-
-curl -X POST http://127.0.0.1:8091/commit \
--H 'Content-Type: application/json' \
--d '{
-"content": "make test commit",
-"filename": "test.md",
-"email": "test@gmail.com",
-"username": "test",
-"commit_message": "init commit",
-"prev_commit": "7cbfc40d98b49a64e98e7cd562f373053d0325bd"
-}'
-
-```
-
-
-
-**Expected Response for success:**
-
-```json
-{
-"status": "201",
-"message": "Committed file successfully",
-"content": "Test for new user\n test 2 for line\n test 3 for new line\n ## real markdown two test\n",
-"commit_sha": "47df3b7f13a935d50cc8b40e98ca9e513cba104c",
-"commit_message": "commit by genetics"
-}
-
-```
-
-**If No Changes to File:**
-
-```json
-{
-"status": "200",
-"message": "Nothing to commit, working tree clean",
-"commit_sha": "ecd96f27c45301279150fbda411544687db1aa45"
-}
-```
-
-**Expected Error Response:**
+# Tools
-```json
-{
-"error": "system-error",
-"msg": "Commits do not match. Please pull in the latest changes for the current commit *ecd96f27c45301279150fbda411544687db1aa45* and previous commits."
-}
-```
+Some tooling and scripts that run independently are stored in `./scripts`.
-## Notes
-This is meant to be used as api endpoint only to edit any local repo; Clients are expected to handle other service e.g User Interface, authentication
# Development
@@ -148,6 +51,11 @@ This is meant to be used as api endpoint only to edit any local repo; Clients ar
git remote add gn git.genenetwork.org:/home/git/public/gn-guile
```
+# Topics
+
+* More on [gn-guile](./doc/gn-guile.md)
+* Markdown editor with git backend see [markdown](./doc/git-markdown-editor.md).
+
# LICENSE
This software is published by the GeneNetwork team under the AGPL3. See [LICENSE](LICENSE.txt).
diff --git a/doc/git-markdown-editor.md b/doc/git-markdown-editor.md
new file mode 100644
index 0000000..f286284
--- /dev/null
+++ b/doc/git-markdown-editor.md
@@ -0,0 +1,117 @@
+# Gn-Markdown
+
+Gn-Markdown is an API endpoint to edit, parse, and commit markdown files for gn-docs.
+
+## How to Test the APIs
+
+1. **Navigate to the Web Directory and Start the Server**
+
+Running the web server is documented in [guix script](./web/.guix-shell).
+
+```
+curl http://127.0.0.1:8091/version
+"4.0.0"
+```
+
+```sh
+cd web
+export CURRENT_REPO_PATH=<path-to-git-repo-with-files>
+export CGIT_REPO_PATH=<path-to-git-bare-repo>
+. .guix-shell -- guile -L .. --listen=1970 -e main ./webserver.scm 8091
+```
+
+
+
+2. **Test Endpoints**
+
+The main endpoints provided are `/edit` and `/commit`. More endpoints may be added in the future.
+
+## Edit (GET)
+
+This is a GET request to retrieve a file's details. Make sure you pass a valid file_path as search_query (the path should be relative to the repo)
+
+**Request Example:**
+
+```bash
+
+curl -G -d "file_path=test.md" localhost:8091/edit
+
+```
+
+**Expected Success Response:**
+
+```json
+{
+"file_path": "test.md",
+"content": "Test for new user\n test 2 for line\n test 3 for new line\n ## real markdown two test\n",
+"hash": "ecd96f27c45301279150fbda411544687db1aa45"
+}
+```
+
+**Expected Error Response (Status 400):**
+
+```json
+{
+"error": <error_type>,
+"msg": <error_reason>
+}
+```
+
+## Commit (POST)
+
+This is a POST request to commit changes to a file.
+
+**Request URL:**
+
+```bash
+
+curl -X POST http://127.0.0.1:8091/commit \
+-H 'Content-Type: application/json' \
+-d '{
+"content": "make test commit",
+"filename": "test.md",
+"email": "test@gmail.com",
+"username": "test",
+"commit_message": "init commit",
+"prev_commit": "7cbfc40d98b49a64e98e7cd562f373053d0325bd"
+}'
+
+```
+
+
+
+**Expected Response for success:**
+
+```json
+{
+"status": "201",
+"message": "Committed file successfully",
+"content": "Test for new user\n test 2 for line\n test 3 for new line\n ## real markdown two test\n",
+"commit_sha": "47df3b7f13a935d50cc8b40e98ca9e513cba104c",
+"commit_message": "commit by genetics"
+}
+
+```
+
+**If No Changes to File:**
+
+```json
+{
+"status": "200",
+"message": "Nothing to commit, working tree clean",
+"commit_sha": "ecd96f27c45301279150fbda411544687db1aa45"
+}
+```
+
+**Expected Error Response:**
+
+```json
+{
+"error": "system-error",
+"msg": "Commits do not match. Please pull in the latest changes for the current commit *ecd96f27c45301279150fbda411544687db1aa45* and previous commits."
+}
+```
+
+## Notes
+
+This is meant to be used as api endpoint only to edit any local repo; Clients are expected to handle other service e.g User Interface, authentication
diff --git a/doc/gn-guile.md b/doc/gn-guile.md
index 7a86c13..9e84e23 100644
--- a/doc/gn-guile.md
+++ b/doc/gn-guile.md
@@ -4,4 +4,5 @@ The GeneNetwork Guile web server serves an exploratory REST API as well as HTML
Topics are:
+* [Markdown editor](./git-markdown-editor.md).
* [Branding GN](./branding.md)
diff --git a/gn-guile.sh b/gn-guile.sh
new file mode 100755
index 0000000..9341b26
--- /dev/null
+++ b/gn-guile.sh
@@ -0,0 +1,3 @@
+#! @SHELL@
+
+guile -e main web/webserver.scm "$@"
diff --git a/gn/db/sources/wikidata.scm b/gn/db/sources/wikidata.scm
new file mode 100644
index 0000000..7397426
--- /dev/null
+++ b/gn/db/sources/wikidata.scm
@@ -0,0 +1,29 @@
+#!
+
+Wikidata queries
+
+!#
+
+(define-module (gn db sources wikidata)
+)
+
+(define ps-encoded-by "ps:P702")
+(define wdt-instance-of "wdt:P31")
+(define wdt-in-taxon "wdt:P703")
+(define wd-human "wd:Q15978631")
+(define wd-mouse "wd:Q83310")
+(define wd-rat "wd:Q184224")
+(define wd-gene "wd:Q7187")
+
+(define (wikidata_query_geneids gene_name)
+ "Return the wikidata identifiers pointing to genes of listed species"
+ (string-append
+ "SELECT DISTINCT ?wikidata_id
+ WHERE {
+ ?wikidata_id " wdt-instance-of " " wd-gene ";
+ " wdt-in-taxon " ?species .
+ VALUES (?species) { (" wd-human " ) ( " wd-mouse" ) ( " wd-rat" ) } .
+ ?wikidata_id rdfs:label \"" gene_name "\"@en .
+ }
+"
+ ))
diff --git a/gn/db/sparql.scm b/gn/db/sparql.scm
index b7d94f3..f03389b 100644
--- a/gn/db/sparql.scm
+++ b/gn/db/sparql.scm
@@ -2,7 +2,7 @@
Module for handling SPARQL primitives.
-Note that GN queries should go into gn/data - this is currently not
+Note that GN queries should go into gn/db/sources - this is currently not
the case.
!#
@@ -18,6 +18,7 @@ the case.
#:use-module (web request)
#:use-module (web uri)
#:use-module (gn cache memoize)
+ #:use-module (gn db sources wikidata)
#:use-module (web gn-uri)
#:export (memo-sparql-species
diff --git a/guix.scm b/guix.scm
index 4417452..973f44f 100644..120000
--- a/guix.scm
+++ b/guix.scm
@@ -1,71 +1 @@
-;; To use this file to build HEAD of gn-guile:
-;;
-;; guix build -f guix.scm
-;;
-;; To get a development container
-;;
-;; guix shell -C -D -f guix.scm
-;;
-
-(use-modules
- ((guix licenses) #:prefix license:)
- (guix gexp)
- (guix packages)
- (guix git-download)
- (guix build-system guile)
- (gnu packages algebra)
- (gnu packages base)
- (gnu packages bash)
- (gnu packages compression)
- (gnu packages bioinformatics)
- (gnu packages build-tools)
- (gnu packages certs)
- (gnu packages curl)
- (gnu packages gcc)
- (gnu packages guile)
- (gnu packages guile-xyz)
- (gnu packages llvm)
- (gnu packages ninja)
- (gnu packages parallel)
- (gnu packages perl)
- (gnu packages perl6)
- (gnu packages pkg-config)
- (gnu packages python)
- (gnu packages tls)
- (srfi srfi-1)
- (ice-9 popen)
- (ice-9 rdelim))
-
-(define %source-dir (dirname (current-filename)))
-
-(define %git-commit
- (read-string (open-pipe "git show HEAD | head -1 | cut -d ' ' -f 2" OPEN_READ)))
-
-(define-public gn-guile-git
- (package
- (name "gn-guile-git")
- (version (git-version "4.0.0-" "HEAD" %git-commit))
- (source (local-file %source-dir #:recursive? #t))
- (build-system guile-build-system)
-
- (inputs
- (list guile-3.0-latest bash-minimal perl
- guile-dbi guile-dbd-mysql guile-fibers guile-gnutls guile-readline guile-redis openssl nss-certs gemma parallel))
- (propagated-inputs
- (list guile-json-4))
-
-#!
- (arguments
- `(#:compile-flags '("--r6rs" "-Wunbound-variable" "-Warity-mismatch")
- #:modules ((guix build guile-build-system)
- (guix build utils)
- (srfi srfi-26)
- (ice-9 ftw)
- (json))))
-!#
- (home-page "https://git.genenetwork.com/gn-guile")
- (synopsis "Next generation GN code in guile")
- (description "Use of guile.")
- (license license:gpl3)))
-
-gn-guile-git
+.guix/modules/gn-guile.scm \ No newline at end of file
diff --git a/scripts/lmdb-publishdata-export.scm b/scripts/lmdb-publishdata-export.scm
new file mode 100755
index 0000000..8427112
--- /dev/null
+++ b/scripts/lmdb-publishdata-export.scm
@@ -0,0 +1,229 @@
+#! /usr/bin/env guile
+!#
+;; To run this script run:
+;;
+;; $ guix shell guile guile-hashing guile3-dbi guile3-dbd-mysql \
+;; guile-lmdb -- guile lmdb-publishdata-export.scm
+;; conn.scm
+;;
+;; Example conn.scm:
+;; ((sql-username . "webqtlout")
+;; (sql-password . "xxxx")
+;; (sql-database . "db_webqtl")
+;; (sql-host . "localhost")
+;; (sql-port . 3306)
+;; (output-dir . "/tmp/data")
+;; (log-file . "/tmp/export.log"))
+
+(use-modules (dbi dbi)
+ (rnrs bytevectors)
+ (system foreign)
+ (ice-9 match)
+ (srfi srfi-1)
+ (srfi srfi-26)
+ (srfi srfi-43)
+ (rnrs io ports)
+ (hashing md5)
+ ((lmdb lmdb) #:prefix mdb:)
+ (ice-9 format)
+ (ice-9 exceptions)
+ (json)
+ (logging logger)
+ (logging rotating-log)
+ (logging port-log)
+ (oop goops))
+
+
+;; Set up logging
+(define* (setup-logging #:key (log-file "lmdb-dump-log"))
+ "Initialize the logging system with rotating file logs and error port output.
+ Creates a new logger, adds rotating and error port handlers,
+ sets it as the default logger, and opens the log for writing."
+ (let ((lgr (make <logger>))
+ (rotating (make <rotating-log>
+ #:num-files 3
+ #:size-limit 1024
+ #:file-name log-file))
+ (err (make <port-log> #:port (current-error-port))))
+ ;; add the handlers to our logger
+ (add-handler! lgr rotating)
+ (add-handler! lgr err)
+ ;; make this the application's default logger
+ (set-default-logger! lgr)
+ (open-log! lgr)))
+
+(define (shutdown-logging)
+ "Properly shutdown the logging system.
+ Flushes any pending log messages, closes the log handlers,
+ and removes the default logger reference."
+ (flush-log) ;; since no args, it uses the default
+ (close-log!) ;; since no args, it uses the default
+ (set-default-logger! #f))
+
+(define (call-with-database backend connection-string proc)
+ "Execute PROC with an open database connection. BACKEND is the
+database type (e.g. \"mysql\"). CONNECTION-STRING is the database
+connection string."
+ (let ((db #f))
+ (dynamic-wind
+ (lambda ()
+ (set! db (dbi-open backend connection-string)))
+ (cut proc db)
+ (lambda ()
+ (when db
+ (dbi-close db))))))
+
+(define (call-with-target-database connection-settings proc)
+ "Connect to the target database using CONNECTION-SETTINGS and execute
+PROC."
+ (call-with-database "mysql" (string-join
+ (list (assq-ref connection-settings 'sql-username)
+ (assq-ref connection-settings 'sql-password)
+ (assq-ref connection-settings 'sql-database)
+ "tcp"
+ (assq-ref connection-settings 'sql-host)
+ (number->string
+ (assq-ref connection-settings 'sql-port)))
+ ":")
+ proc))
+
+(define* (lmdb-save path key value)
+ "Save a NUM with KEY to PATH."
+ (mdb:with-env-and-txn
+ (path) (env txn)
+ (let ((dbi (mdb:dbi-open txn #f 0)))
+ (mdb:put txn dbi key
+ (if (number? value)
+ (number->string value)
+ value)))))
+
+(define (sql-exec db statement)
+ "Execute an SQL STATEMENT on database connection DB. Throws an error
+if the statement execution fails."
+ (dbi-query db statement)
+ (database-check-status db))
+
+(define (sql-fold proc init db statement)
+ "Fold over SQL query results."
+ (sql-exec db statement)
+ (let loop ((result init))
+ (let ((row (dbi-get_row db)))
+ (if row
+ (loop (proc row result))
+ result))))
+
+(define (sql-for-each proc db statement)
+ "Apply PROC to each row returned by STATEMENT."
+ (sql-fold (lambda (row _)
+ (proc row))
+ #f db statement))
+
+(define (sql-map proc db statement)
+ "Map PROC over rows returned by STATEMENT."
+ (sql-fold (lambda (row result)
+ (cons (proc row) result))
+ (list) db statement))
+
+(define (sql-find db statement)
+ (sql-exec db statement)
+ (dbi-get_row db))
+
+(define (database-check-status db)
+ "Check the status of the last database operation on DB. Throws an
+error if the status code is non-zero."
+ (match (dbi-get_status db)
+ ((code . str)
+ (unless (zero? code)
+ (error str)))))
+
+(define* (save-dataset-values settings)
+ "Main function to extract and save dataset values. Queries the
+database for datasets and their values, computes MD5 hashes for
+dataset-trait combinations, and saves strain values to LMDB files in
+/export5/lmdb-data-hashes/."
+ (dynamic-wind
+ (lambda ()
+ (setup-logging #:log-file (assq-ref settings 'log-file))
+ (log-msg 'INFO "Starting dataset value extraction"))
+ (lambda ()
+ (call-with-target-database
+ settings
+ (lambda (db)
+ (sql-for-each
+ (lambda (row)
+ (match row
+ ((("Name" . dataset-name)
+ ("Id" . trait-id))
+ (let* ((data-query (format #f "SELECT
+JSON_ARRAYAGG(JSON_ARRAY(Strain.Name, PublishData.Value)) AS data,
+ MD5(JSON_ARRAY(Strain.Name, PublishData.Value)) as md5hash
+FROM
+ PublishData
+ INNER JOIN Strain ON PublishData.StrainId = Strain.Id
+ INNER JOIN PublishXRef ON PublishData.Id = PublishXRef.DataId
+ INNER JOIN PublishFreeze ON PublishXRef.InbredSetId = PublishFreeze.InbredSetId
+LEFT JOIN PublishSE ON
+ PublishSE.DataId = PublishData.Id AND
+ PublishSE.StrainId = PublishData.StrainId
+LEFT JOIN NStrain ON
+ NStrain.DataId = PublishData.Id AND
+ NStrain.StrainId = PublishData.StrainId
+WHERE
+ PublishFreeze.Name = \"~a\" AND
+ PublishXRef.Id = ~a AND
+ PublishFreeze.public > 0 AND
+ PublishData.value IS NOT NULL AND
+ PublishFreeze.confidentiality < 1
+ORDER BY
+ LENGTH(Strain.Name), Strain.Name" dataset-name trait-id)))
+ (match (call-with-target-database
+ settings
+ (lambda (db2) (sql-find db2 data-query)))
+ ((("data" . data)
+ ("md5hash" . md5-hash))
+ (let* ((trait-name (format #f "~a~a" dataset-name trait-id))
+ (base-dir (assq-ref settings 'output-dir))
+ (out (format #f "~a-~a" trait-name
+ (substring md5-hash 0 12)))
+ (out-dir (format #f "~a/~a" base-dir out)))
+ (log-msg
+ 'INFO (format #f "Writing ~a to: ~a" trait-name out-dir))
+ (unless (file-exists? out-dir)
+ (mkdir out-dir))
+ (lmdb-save (format #f "~a/index" base-dir) trait-name out)
+ (vector-for-each
+ (lambda (_ x)
+ (match x
+ (#(strain value)
+ (lmdb-save out-dir strain value))))
+ (json-string->scm data)))))))))
+ db
+ "SELECT DISTINCT PublishFreeze.Name, PublishXRef.Id FROM
+PublishData INNER JOIN Strain ON PublishData.StrainId = Strain.Id
+INNER JOIN PublishXRef ON PublishData.Id = PublishXRef.DataId
+INNER JOIN PublishFreeze ON PublishXRef.InbredSetId = PublishFreeze.InbredSetId
+LEFT JOIN PublishSE ON
+ PublishSE.DataId = PublishData.Id AND
+ PublishSE.StrainId = PublishData.StrainId
+LEFT JOIN NStrain ON
+ NStrain.DataId = PublishData.Id AND
+ NStrain.StrainId = PublishData.StrainId
+WHERE
+ PublishFreeze.public > 0 AND
+ PublishFreeze.confidentiality < 1
+ORDER BY
+ PublishFreeze.Id, PublishXRef.Id"))))
+ (lambda ()
+ (shutdown-logging))))
+
+(define main
+ (match-lambda*
+ ((_ connection-settings-file)
+ (save-dataset-values (call-with-input-file connection-settings-file
+ read)))
+ ((arg0 _ ...)
+ (display (format "Usage: ~a CONNECTION-SETTINGS-FILE~%" arg0)
+ (current-error-port))
+ (exit #f))))
+
+(apply main (command-line))
diff --git a/web/.guix-shell b/web/.guix-shell
deleted file mode 100644
index b4aee2a..0000000
--- a/web/.guix-shell
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-#
-# run with options '-- ./webserver.scm 8091' e.g.
-# . .guix-shell -- guile -L .. --fresh-auto-compile --listen=1970 -e main ./webserver.scm 8091
-
-echo "Note run: running web-server"
-
-guix shell guile guile-commonmark guile-fibers guile-json guile-gnutls guile-readline guile-redis openssl nss-certs $*
diff --git a/web/css/gn-template-style.css b/web/css/gn-template-style.css
new file mode 100644
index 0000000..38893c6
--- /dev/null
+++ b/web/css/gn-template-style.css
@@ -0,0 +1,39 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0.7em;
+ display: grid;
+ grid-template-columns: 9fr 1fr;
+ grid-gap: 20px;
+
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-style: normal;
+ font-size: 20px;
+}
+
+#header {
+ grid-column-start: 1;
+ grid-column-end: 3;
+
+ background-color: #336699;
+ color: #FFFFFF;
+ border-radius: 3px;
+ min-height: 30px;
+}
+
+#header #header-text {
+ padding-left: 0.2em;
+}
+
+#main {
+ grid-column-start: 1;
+ grid-column-end: 2;
+
+ max-width: 650px;
+}
+
+#main img {
+ max-width: 650px;
+}
diff --git a/web/templates/genenetwork.scm b/web/templates/genenetwork.scm
new file mode 100644
index 0000000..64e9852
--- /dev/null
+++ b/web/templates/genenetwork.scm
@@ -0,0 +1,18 @@
+(define-module (web templates genenetwork)
+ #:use-module (web view markdown)
+
+ #:export (default-gn-template))
+
+(define* (default-gn-template path #:optional (title "Default Page Template"))
+ "Render `PATH' with a default template and styling that fits in with
+ GeneNetwork's look and feel."
+ `(html
+ (head
+ (meta (@ (charset "UTF-8")))
+ (meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0")))
+ (title ,title)
+ (link (@ (rel "stylesheet") (type "text/css")
+ (href "/css/gn-template-style.css"))))
+ (body
+ (header (@ (id "header")) (span (@ (id "header-text")) "GeneNetwork"))
+ (main (@ (id "main")) ,(markdown-github->sxml path)))))
diff --git a/web/view/view.scm b/web/view/view.scm
index 4584cf8..4300863 100644
--- a/web/view/view.scm
+++ b/web/view/view.scm
@@ -10,6 +10,7 @@
#:use-module (web view markdown)
#:use-module (web view brand msk)
#:use-module (web view brand aging)
+ #:use-module (web templates genenetwork)
#:export (view-brand))
@@ -45,6 +46,9 @@ data to benefit from the power of integrated datasets, please contact:")
(define* (view-brand path)
(match path
("aging" (view-aging))
+ ("gnqa" (default-gn-template
+ "genenetwork/gn-docs/general/brand/gnqa/gnqa.md"
+ "GeneNetwork Question and Answer System"))
( _ (msk-html #:info
`(
,(markdown-github->sxml "genenetwork/gn-docs/general/brand/msk/home.md")
diff --git a/web/webserver.scm b/web/webserver.scm
index c0fb9a1..d2a8c8d 100644
--- a/web/webserver.scm
+++ b/web/webserver.scm
@@ -34,14 +34,14 @@
(getenv "CGIT_REPO_PATH"))
(define +info+
- `(("name" . "GeneNetwork REST API") ("version" ,get-version)
+ `(("name" . "GeneNetwork REST API") ("version" . ,get-version)
("comment" . "This is the official REST API for the GeneNetwork service hosted at https://genenetwork.org/")
("license"
("source code (unless otherwise specified)" . "Affero GNU Public License 3.0 (AGPL3)")
("data (unless otherwise specified)" . "Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)"))
("note" . "This is work in progress (WIP). Note that the final base URL will change! The temporary prefix is:")
- ("prefix" ,(prefix))
- ("links" ("species" ,(mk-meta "species")))))
+ ("prefix" . ,(prefix))
+ ("links" ("species" . ,(mk-meta "species")))))
(define +info-meta+
`(("doc" ,(mk-html "info"))
@@ -87,9 +87,9 @@ otherwise search for set/group data"
(modified (and stat
(make-time time-utc 0
(stat:mtime stat)))))
- (list `((content-type ,(assoc-ref file-mime-types
- (file-extension file-name)))
- (last-modified ,(time-utc->date modified)))
+ (list `((content-type . ,(assoc-ref file-mime-types
+ (file-extension file-name)))
+ (last-modified . ,(time-utc->date modified)))
(call-with-input-file file-name
get-bytevector-all))))
@@ -100,9 +100,9 @@ otherwise search for set/group data"
(modified (and stat
(make-time time-utc 0
(stat:mtime stat)))))
- (list `((content-type ,(assoc-ref file-mime-types
- (file-extension path)))
- (last-modified ,(time-utc->date modified)))
+ (list `((content-type . ,(assoc-ref file-mime-types
+ (file-extension path)))
+ (last-modified . ,(time-utc->date modified)))
(call-with-input-file path
get-bytevector-all))))
@@ -172,8 +172,8 @@ otherwise search for set/group data"
(lambda (key . args)
(let ((msg (car args)))
(build-json-response 400
- `(("error" ,key)
- ("msg" ,msg)))))))
+ `(("error" . ,key)
+ ("msg" . ,msg)))))))
(define (invalid-data? data target)
(if (string? (assoc-ref data target))
@@ -214,8 +214,8 @@ otherwise search for set/group data"
(lambda (key . args)
(let ((msg (car args)))
(build-json-response 400
- `(("error" ,key)
- ("msg" ,msg)))))))
+ `(("error" . ,key)
+ ("msg" . ,msg)))))))
(define (controller request body)
(match-lambda
@@ -224,13 +224,13 @@ otherwise search for set/group data"
(('GET "version")
(render-json get-version))
(('GET "css" fn)
- (render-static-file (string-append "css/" fn)))
+ (render-static-file (string-append (dirname (current-filename)) "/css/" fn)))
(('GET "map" fn)
- (render-static-file (string-append "css/" fn)))
+ (render-static-file (string-append (dirname (current-filename)) "/css/" fn)))
(('GET "static" "images" fn)
- (render-static-image (string-append "static/images/" fn)))
+ (render-static-image (string-append (dirname (current-filename)) "/static/images/" fn)))
(('GET "home" path)
- (render-brand path))
+ (render-brand path)) ; branding route for /home/aging, /home/msk etc
(('GET "doc" "species.html")
(render-doc "doc" "species.html"
(get-species-meta)))