(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.