(in-package :cl-user)
;(untrace)
(register-namespace "obim:" "http://obi.org/metadata#")
(defun ontology-setup ()
(define-ontology metadata (:base "http://obi.org/metadata")
(class !obi:OBI1002 :partial (label "Digital document"))
(class !publication :partial !obi:OBI1002)
(annotation-property !metadataFor)
(annotation-property !metadataOnProperty)
(class !book :partial !publication)
(datatype-property !identifiedByISBN (domain !book) (range !xsd:string))
(datatype-property !identifiedByPubmedId (domain !article) (range !xsd:string))
(datatype-property !identifiedByDOI (domain !article) (range !xsd:string))
(class !webPageOnDate :partial !publication)
(class !webPage :partial !publication)
(class !article :partial !publication)
(class !personalCommunication :partial !publication)
(class !meshTerm :partial !publication)
(annotation-property !definition_source_string)
(annotation-property !definition_source)
(annotation-property !alternative_term_string)
(annotation-property !alternative_term)
(class !myexample :partial
(annotation !definition_source_string "ISBN:0743222091")
(label "My Example")
(annotation !alternative_term_string "[French:mon exemple|Web:][Norwegian:min eksempel|Web:]"))
(class !taggedTerm :partial)
(class !tag :partial)
(object-property !isTagged (domain !taggedTerm) (range !tag))
(datatype-property !isTerm (domain !taggedTerm) (range !xsd:string))
(object-property !fromSource (domain !taggedTerm) (range !publication))
))
(defun fill-definition-source (kb)
(let ((*default-kb* kb))
(loop for (thing string) in (sparql `(:select (?thing ?string) ()
(?thing !obim:definition_source_string ?string))
:use-reasoner :none)
for current-metadata = (sparql `(:select (?meta) ()
(?meta !obim:metadataFor ?thing)
(?meta !obim:metadataOnProperty !obim:definition_source_string))
:flatten t :use-reasoner :none )
do (when (> (length current-metadata) 1)
(warn "~a has more than one ~a metadata objects" thing !obim:definition_source_string))
(update-definition-source thing string current-metadata))))
(defun update-definition-source (thing string current)
(loop for (source identification) in (all-matches string "([^:]*):(.*)" 1 2)
do
(cond ((equal source "ISBN")
(update-ds-book thing current :isbn identification))
((equal source "Book")
(update-ds-book current :url identification))
((equal source "Article")
(update-ds-article thing current :url identification))
((equal source "PMID")
(update-ds-article thing current :pmid identification))
((equal source "DOI")
(update-ds-article thing current :doi identification))
((equal source "MESH")
(update-ds-mesh thing current identification))
((equal source "Person")
(update-ds-person thing current identification))
((equal source "Web")
(update-ds-web thing current identification))
((equal source "Other")
(warn "~a has definition source Other:~a" thing identification)
(and current (remove-metadata thing current !obim:definition_source)))
(t (warn "~a has improperly formatted definition_source_string: '~a'" thing string)))))
(defun update-ds-book (thing current &key isbn)
(if (not (#"matches" isbn "^\\d+"))
(progn
(remove-metadata thing current !obim:definition_source)
(warn "Definition source for ~a, a book's isbn, doesn't look right: ~a" thing isbn))
(if (not current)
(create-book-metadata thing !obim:definition_source isbn)
(when (not (equal (list current)
(sparql
`(select (?meta) ()
(thing !obim:definition_source ?meta)
(?meta !rdf:type !obim:book )
(?meta !obim:identifiedByISBN ,isbn)
(?meta !obim:metadataOnProperty !obim:definition_source_string)
(?meta !obim:metadataFor ,thing)))))
(remove-metadata thing current !obim:definition_source)
(create-book-metadata thing !obim:definition_source isbn)))))
(defun update-ds-article (thing current &key doi pmid)
(if (or (and doi (not (#"matches" doi ".+/.+")))
(and pmid (not (#"matches" pmid "^\\d+$"))))
(progn
(remove-metadata thing current !obim:definition_source)
(warn "Definition source for ~a, an article's ~a doesn't look right: ~a" thing (if doi "doi" "pmid") (or doi pmid)))
(if (not current)
(progn
(remove-metadata thing current !obim:definition_source)
(create-article-metadata thing !obim:definition_source :doi doi :pmid pmid))
(when (not (equal (list current)
(sparql
`(select (?meta) ()
(thing !obim:definition_source ?meta)
(?meta !rdf:type !obim:article)
(?meta ,(if doi !obim:identifiedByDOI !obim:identifiedByPubmedId) (or doi pmid))
(?meta !obim:metadataOnProperty !obim:definition_source_string)
(?meta !obim:metadataFor ,thing)))))
(remove-metadata thing current !obim:definition_source)
(create-article-metadata thing !obim:definition_source :doi doi :pmid pmid)))))
(defmacro ji (uri) `(#"getIndividual" jena (uri-full ,uri)))
(defmacro jp (uri) `(#"getProperty" jena (uri-full ,uri)))
(defparameter null (make-immediate-object nil :ref))
(defun remove-metadata (thing current property)
(declare (ignore thing property))
(let ((jena (kb-jena-model *default-kb*))
(old (sparql `(:select (?meta) () (,thing ,property ?meta)) :use-reasoner :jena :flatten t :trace "Find old metadata, so we can delete it")))
(dolist (old1 old)
(#"removeAll" jena (ji thing) (jp property) (ji old1))
(#"removeAll" jena (ji old1) null null))
))
(defun create-book-metadata(thing property isbn)
(create-metadata thing property !obim:book `((,!obim:identifiedByISBN ,isbn))))
(defun create-article-metadata(thing property &key doi pmid)
(create-metadata thing property !obim:article
(if doi
`((,!obim:identifiedByDOI ,doi))
`((,!obim:identifiedByPubmedId ,pmid)))))
(defun localname (uri)
(when (uri-p uri) (setq uri (uri-full uri)))
(#"replaceAll" uri "(.*)#" ""))
(defun create-metadata (thing property type props)
(let* ((name (uri-full (make-uri nil (format nil "obim:~a_~a" (localname thing) (localname property)))))
(jena (kb-jena-model *default-kb*))
(new (#"createIndividual" jena name (ji type))))
(#"add" jena new (jp !obim:metadataFor) (ji thing))
(#"add" jena new (jp !obim:metadataOnProperty) (jp property))
(loop for (p v) in props
do (#"add" jena new (jp p) v))
(#"add" jena (ji thing) (jp !obim:definition_source) new)))
(defun show (kb note)
(write-string note t) (terpri t)
(sparql '(:select (?string)()
(!obim:myexample !obim:definition_source_string ?string))
:kb kb :use-reasoner :jena :trace "What's the definition_source_string?")
(sparql '(:select (?meta ?prop ?value)()
(?meta !rdf:type !obim:publication)
(!obim:myexample !obim:definition_source ?meta)
(?meta ?prop ?value))
:kb kb :use-reasoner :jena :trace "Show the definition_source"))
(defmacro with-updated-kb ((new old &optional saveas) &body body)
`(let* ((rdf (let ((sw (new 'stringwriter)))
(#"write" (kb-jena-model (load-from-jena (kb-jena-model ,old))) sw "RDF/XML-ABBREV")
(#"toString" sw)))
(,new (load-kb-jena-from-string rdf))
(jena (kb-jena-model ,new)))
(when ,saveas
(with-open-file (f ,saveas :if-exists :supersede :direction :output)
(write-string rdf f)))
(let ((*default-kb* ,new))
,@body)))
(defun process-alternative-terms (&optional (kb (kb (eval (ontology-setup)))))
(let ((*default-kb* kb))
(let ((jena (kb-jena-model kb)))
(loop for (thing string) in (sparql '(:select (?thing ?string) ()
(?thing !obim:alternative_term_string ?string))
:use-reasoner :jena :kb kb)
do
(loop for (tag term source) in (all-matches string "\\[(.*?):(.*?)\\|(.*?)\\]" 1 2 3)
for tag-instance = (maybe-define-tag tag)
for source-instance = (maybe-define-source source)
for tagged-term = (#"createIndividual"
jena
(format nil "http://obi.org/metadata#~a_~a_~a" (localname thing) tag term)
(ji !obim:taggedTerm))
do
(#"add" jena (ji thing) (jp !obim:alternative_term) tagged-term)
(#"add" jena tagged-term (jp !obim:isTagged) tag-instance)
(#"add" jena tagged-term (jp !obim:isTerm) term)
(#"add" jena tagged-term (jp !obim:fromSource) source-instance)))
(with-updated-kb (new kb "~/Desktop/metadata-tagged.owl")
(sparql '(:select (?thing ?term ?tag) ()
(?thing !obim:alternative_term ?tt)
(?tt !obim:isTagged ?tag)
(?tt !obim:isTerm ?term))
:kb new :use-reasoner :jena :trace "List alternative terms and tags")))))
(defun maybe-define-tag (tag)
"Just make it, can't hurt"
(let ((jena (kb-jena-model *default-kb*)))
(let ((tag-instance (#"createIndividual" jena (format nil "http://obi.org/metadata#Tag_~a" tag) (ji !obim:tag))))
(#"add" jena tag-instance (jp !rdfs:label) tag)
tag-instance)))
(defun maybe-define-source (source)
"Only handle Web:<..> for the demo. Don't deal with date yet"
(let ((jena (kb-jena-model *default-kb*)))
(let* ((address (caar (all-matches source "Web:<(.*)>" 1)))
(instance (#"createIndividual" jena address (ji !obim:webPage))))
instance)))
(defun demo ()
(ontology-setup)
(write-rdfxml metadata "~/Desktop/metadata-initial.owl")
(format t "Initial file is ~a, ~a~%"
(if (check metadata) "consistent" "inconsistent")
(get-owl-species (kb metadata)))
(show (kb metadata) "before metadata created")
(fill-definition-source (kb metadata))
(with-updated-kb (new-kb (kb metadata) "~/Desktop/metadata-after-book.owl")
(show new-kb "after metadata generated")
(#"removeAll" jena (ji !obim:myexample) (jp !obim:definition_source_string) null)
(#"add" jena (ji !obim:myexample) (jp !obim:definition_source_string) "DOI:10.1002/ijc.20376")
(with-updated-kb (new-new new-kb)
(fill-definition-source new-new)
(with-updated-kb (new-new-new new-new "~/Desktop/metadata-after-article.owl")
(show new-new-new "after definition_source_string changed")
(format t "~%Final file is ~a, ~a~%"
(if (check new-new-new) "consistent" "inconsistent")
(get-owl-species new-new-new))
(process-alternative-terms new-new-new)
))))
;; a) "ISBN:"the isbn number pp203
;; b) ["BOOK"|"PAPER"|"Web"]"<"the url">@"DD/MM/YYYY
;; c) "PMID:"the pubmed id
;; d) "MESH:" The mesh id(term).
;; e) "Person:"["<"foaf uri">"|person name as string(without "|" (pipe) character)]@DD/MM/YYYY
;; f) "Other:" unstructured text (without "|" (pipe) character)
;; g) "OntologyTerm": (for term)
;; h) "OntologyTerm":|identifier (when term doesn't have a URI)
Add comment character at the end
e.g.
ISBN:129083098pp112-113#this was a really good book.
Action: Send back to list.
;; ------------
;; Definition Source.
;; Representation: New classes:
;; Superclass take from appropriate place in OBI (information object or whatever we called it)
;; Book: properties: identifiedByISBN(if), availableAtURI
;; WebPageOnDate: properties: page(a WebPage), asOfDate
;; WebPage: URI is the rdf:ID of the instance
;; Article: properties: identifiedByPMID(if), identifiedByDOI(if), availableAtURI
;; PersonalCommunication: property: fromPerson (foaf instance would be appropriate filler)
;; MeSH Term: use label (is ID ok too?)
;; NoneOfTheAbove: use comment (I don't like this but it is a sacrifice to ease of use). Periodically review and add common cases to above list.
;; Note: All of instances can have "sourceOf" property to link back to thing having the source.
;; (if) means inversefunctional
;; UI:
;; First idea(less work): User fills in as text, script creates instances to match. Text one of:
;; a) "ISBN:"the isbn number
;; b) ["BOOK"|"PAPER"|""]"<"the url">@"DD/MM/YYYY
;; c) "PMID:"the pubmed id
;; d) "MESH:" The mesh term
;; e) "Person:"["<"foaf uri">"|person name as string(without "|" (pipe) character)]
;; f) "Other:" unstructured text (without "|" (pipe) character)
;; Script could run at time of entry(depends on protege chops), or by maintainers periodically. Report malformed entries.
;; Second idea(more work): Dialog is presented with different tabs for each of the different sources. Selecting the tag offers the appropriate
;; properties which they fill in. Entry is checked for nonsense/typos and instance is created and populated immediately.
;; ------------
;; alternative_term,tag,source: Issue here is that these need to be maintained in parallel.
;; Representation:
;; New class
;; Tag. Specific tags are instances of this class.
;; TaggedTerm: properties: term(string without commas or colons or quotes), taggedWith(a Tag), fromSource(A Source)
;; Source has same representation as definition_source.
;; UI: Same two choices as with definitions source textual entry followed by script, or dialog. Form of text:
;; [TAG[,TAG]*":"source"|""'"term"'"]+
;; If multiple tags given here, then it means from the same source.