diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67e8c0ca6..c54716488 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ name: CI on: push: branches: '*' - release: + tags: 'v*' # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: diff --git a/Dockerfile b/Dockerfile index 12ede6d49..06ebf8f04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,35 +46,35 @@ RUN /bin/echo -e "\e[1;35mORIGIN BRANCH ------------ $originBranch\e[0m" &&\ # get geppetto RUN mkdir -p workspace &&\ cd workspace &&\ - ../copy.sh http://github.com/openworm/org.geppetto.git "${geppettoRelease}" "${geppettoRelease}" "${geppettoRelease}" + git clone http://github.com/openworm/org.geppetto.git -q -b "${geppettoRelease}" --single-branch WORKDIR $HOME/workspace -# checkout $targetBranch -RUN ../copy.sh https://github.com/openworm/org.geppetto.model.git "${geppettoModelRelease}" "${geppettoModelRelease}" "${geppettoModelRelease}" &&\ + +RUN git clone https://github.com/openworm/org.geppetto.model.git -q -b "${geppettoModelRelease}" --single-branch &&\ cd org.geppetto.model &&\ /bin/echo -e "\e[96mMaven install org.geppetto.model\e[0m" &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/openworm/org.geppetto.core.git "${geppettoCoreRelease}" "${geppettoCoreRelease}" "${geppettoCoreRelease}" &&\ +RUN git clone https://github.com/openworm/org.geppetto.core.git -q -b "${geppettoCoreRelease}" --single-branch &&\ cd org.geppetto.core &&\ /bin/echo -e "\e[96mMaven install org.geppetto.core\e[0m" &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/openworm/org.geppetto.simulation.git "${geppettoSimulationRelease}" "${geppettoSimulationRelease}" "${geppettoSimulationRelease}" &&\ +RUN git clone https://github.com/openworm/org.geppetto.simulation.git -q -b "${geppettoSimulationRelease}" --single-branch &&\ cd org.geppetto.simulation &&\ /bin/echo -e "\e[96mMaven install org.geppetto.simulation\e[0m" &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/openworm/org.geppetto.datasources.git "${geppettoDatasourceRelease}" "${geppettoDatasourceRelease}" "${geppettoDatasourceRelease}" &&\ +RUN git clone https://github.com/openworm/org.geppetto.datasources.git -q -b "${geppettoDatasourceRelease}" --single-branch &&\ cd org.geppetto.datasources &&\ /bin/echo -e "\e[96mMaven install org.geppetto.datasources\e[0m" &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/VirtualFlyBrain/uk.ac.vfb.geppetto.git "${ukAcVfbGeppettoRelease}" "${ukAcVfbGeppettoRelease}" "${ukAcVfbGeppettoRelease}" +RUN git clone https://github.com/VirtualFlyBrain/uk.ac.vfb.geppetto.git -q -b "${ukAcVfbGeppettoRelease}" --single-branch RUN export DEBUG=false; if test "$build_type" = "development" ; then export DEBUG=true; fi && \ echo "DEBUG=$DEBUG" && \ @@ -86,16 +86,16 @@ RUN cd uk.ac.vfb.geppetto &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/openworm/org.geppetto.model.swc.git "${geppettoModelSwcRelease}" "${geppettoModelSwcRelease}" "${geppettoModelSwcRelease}" &&\ +RUN git clone https://github.com/openworm/org.geppetto.model.swc.git -q -b "${geppettoModelSwcRelease}" --single-branch &&\ cd org.geppetto.model.swc &&\ /bin/echo -e "\e[96mMaven install org.geppetto.model.swc\e[0m" &&\ mvn ${mvnOpt} install &&\ rm -rf src -RUN ../copy.sh https://github.com/openworm/org.geppetto.frontend.git "${geppettoFrontendRelease}" "${geppettoFrontendRelease}" "${geppettoFrontendRelease}" +RUN git clone https://github.com/openworm/org.geppetto.frontend.git -q -b "${geppettoFrontendRelease}" --single-branch RUN cd $HOME/workspace/org.geppetto.frontend/src/main &&\ - $HOME/copy.sh https://github.com/VirtualFlyBrain/geppetto-vfb.git "${targetBranch}" "${originBranch}" "${defaultBranch}" &&\ + git clone https://github.com/VirtualFlyBrain/geppetto-vfb.git -q -b "${targetBranch}" --single-branch &&\ mv geppetto-vfb webapp RUN cd $HOME/workspace/org.geppetto.frontend/src/main/webapp &&\ diff --git a/components/VFBMain.js b/components/VFBMain.js index 86e38faf0..909da9d41 100644 --- a/components/VFBMain.js +++ b/components/VFBMain.js @@ -1181,6 +1181,30 @@ class VFBMain extends React.Component { document.querySelector('meta[property="og:description"]').setAttribute("content","VFB integrates data curated from the literature with image data from many bulk sources. The search system allows you to search for neurons and neuroanatomical structures using almost any name found in the literature. The query system can identify neurons innervating any specified neuropil or fasciculating with any specified tract. It also allows queries for genes, transgenes and phenotypes expressed in any brain region or neuron. Search and query results combine referenced textual descriptions with 3D images and links to originating data sources. VFB features tens of thousands of 3D images of neurons, clones and expression patterns, registered to standard template brains. Any combination of these images can be viewed together. A BLAST-type query system (NBLAST) allows you to find similar neurons and drivers starting from a registered neuron."); document.title = "Virtual Fly Brain, a data integrator for Drosophila neurobiology"; document.querySelector('meta[property="og:title"]').setAttribute("content",document.title); + var conlink = !!document.querySelector("link[rel='canonical']"); + conlink = conlink ? document.querySelector("link[rel='canonical']") : document.createElement('link'); + conlink.setAttribute('rel', 'canonical'); + conlink.setAttribute('href', 'https://virtualflybrain.org'); + document.head.appendChild(conlink); + var script = document.createElement('script'); + script.type = 'application/ld+json'; + script.id = 'metaDesc'; + script.innerHTML = '{"@context": "https://schema.org","@type": "Organization","url": "https://virtualflybrain.org","logo": "https://v2.virtualflybrain.org/images/vfbbrain_icon.png","name":"Virtual Fly Brain",' + + '"description":"VFB integrates data curated from the literature with image data from many bulk sources. The search system allows you to search for neurons and neuroanatomical structures using almost any name found in the literature. The query system can identify neurons innervating any specified neuropil or fasciculating with any specified tract. It also allows queries for genes, transgenes and phenotypes expressed in any brain region or neuron. Search and query results combine referenced textual descriptions with 3D images and links to originating data sources. VFB features tens of thousands of 3D images of neurons, clones and expression patterns, registered to standard template brains. Any combination of these images can be viewed together. A BLAST-type query system (NBLAST) allows you to find similar neurons and drivers starting from a registered neuron.",' + + '"affiliation":[' + + '{"@type": "Organization","name":"Institute for Adaptive and Neural Computation, School of Informatics, University of Edinburgh","logo":"https://v2.virtualflybrain.org/images/vfb/project/logos/InformaticsLogo.gif","url":"https://web.inf.ed.ac.uk/anc"},' + + '{"@type": "Organization","name":"Department of Genetics, University of Cambridge","logo":"https://v2.virtualflybrain.org/images/vfb/project/logos/CUnibig.png","url":"http://www.gen.cam.ac.uk/"},' + + '{"@type": "Organization","name":"Department of Physiology, Development and Neuroscience, University of Cambridge", "url":"https://www.pdn.cam.ac.uk/","logo":"https://www.pdn.cam.ac.uk/images/157071101713274785.png/image_logo"},' + + '{"@type": "Organization","name":"FlyBase","url":"https://flybase.org","logo":"http://flybase.org/images/fly_logo.png"},' + + '{"@type": "Organization","name":"MRC Laboratory of Molecular Biology, Cambridge","logo":"https://v2.virtualflybrain.org/images/vfb/project/logos/MRC-LMB_logo.png","url":"http://www2.mrc-lmb.cam.ac.uk/"},' + + '{"@type": "Organization","name":"European Bioinformatics Institute (EMBL-EBI), Cambridge","logo":"https://v2.virtualflybrain.org/images/vfb/project/logos/EMBL_EBI_logo_180pixels_RGB.png","url":"http://www.ebi.ac.uk/"}' + + '],"funder":{"@type": "Organization","name":"Wellcome Trust","logo":"https://v2.virtualflybrain.org/images/vfb/project/logos/wtvm050446.png","url":"https://wellcome.org/"},' + + '"sameAs":"https://en.wikipedia.org/wiki/Virtual_Fly_Brain","alternateName":"VFB",' + + '"subjectOf":[' + + '{"@type": "ScholarlyArticle","citation":"http://dx.doi.org/10.1093/bioinformatics/btr677","sameAs":"http://dx.doi.org/10.1093/bioinformatics/btr677","name":"The Virtual Fly Brain browser and query interface","datePublished":"2012","author":[{"@type":"person","name":"Milyaev, N."},{"@type":"person","name":"Osumi-Sutherland, D."},{"@type":"person","name":"Reeve, S."},{"@type":"person","name":"Burton, N."},{"@type":"person","name":"Baldock, R. A."},{"@type":"person","name":"Armstrong, J. D."}],"publisher":"Bioinformatics"},' + + '{"@type": "ScholarlyArticle","citation":"http://dx.doi.org/10.1098/rstb.2017.0380","sameAs":"http://dx.doi.org/10.1098/rstb.2017.0380","name":"Geppetto: a reusable modular open platform for exploring neuroscience data and models","author":"Cantarelli, Matteo and Marin, Boris and Quintana, Adrian and Earnshaw, Matt and Court, Robert and Gleeson, Padraig and Dura-Bernal, Salvador and Silver, R. Angus and Idili, Giovanni","publisher": "Philosophical Transactions of the Royal Society B: Biological Sciences","datePublished": "2018"}' + + ']}'; + document.getElementsByTagName('head')[0].appendChild(script); setTimeout(function () { if (window.templateID == undefined) { window.location.reload(true); @@ -1240,7 +1264,7 @@ class VFBMain extends React.Component { // Loading ids passed through the browser's url var idsList = ""; var idList = this.props.location.search; - idList = idList.replace("?","").split("&"); + idList = idList.replace("?","").replace(/:/g, '_').split("&"); for (let list in idList) { if (idList[list].indexOf("id=") > -1) { this.idFromURL = idList[list].replace("id=",""); @@ -1253,11 +1277,28 @@ class VFBMain extends React.Component { if ( window.XMLHttpRequest ) { var xhr = new XMLHttpRequest(); xhr.onload = function () { - if (this.responseXML.title.indexOf("404 Not Found") < 0) { - document.title = 'Virtual Fly Brain (' + this.responseXML.title + ')'; - document.querySelector('meta[property="og:title"]').setAttribute("content",this.responseXML.title); - document.querySelector('meta[name="description"]').setAttribute("content",this.responseXML.body.innerText); - document.querySelector('meta[property="og:description"]').setAttribute("content",this.responseXML.body.innerText); + try { + if (this.responseXML.title.indexOf("404 Not Found") < 0) { + document.title = 'Virtual Fly Brain (' + this.responseXML.title + ')'; + document.querySelector('meta[property="og:title"]').setAttribute("content",this.responseXML.title); + document.querySelector('meta[name="description"]').setAttribute("content",this.responseXML.body.innerText); + document.querySelector('meta[property="og:description"]').setAttribute("content",this.responseXML.body.innerText); + if (document.getElementById('metaDesc') != null) { + if (this.responseXML.head != undefined && this.responseXML.head.getElementsByTagName('script') != undefined && this.responseXML.head.getElementsByTagName('script') != null && this.responseXML.head.getElementsByTagName('script')[1] != undefined) { + document.getElementById('metaDesc').innerHTML = this.responseXML.head.getElementsByTagName('script')[1].innerHTML; + } + } else { + if (this.responseXML.head != undefined && this.responseXML.head.getElementsByTagName('script') != undefined && this.responseXML.head.getElementsByTagName('script') != null && this.responseXML.head.getElementsByTagName('script')[1] != undefined) { + var script = document.createElement('script'); + script.type = 'application/ld+json'; + script.id = 'metaDesc'; + script.innerHTML = this.responseXML.head.getElementsByTagName('script')[1].innerHTML; + document.getElementsByTagName('head')[0].appendChild(script); + } + } + } + } catch (err) { + console.log(err); } } xhr.open( 'GET', 'https://virtualflybrain.org/data/VFB/json/' + this.idFromURL + '.html') @@ -1325,7 +1366,7 @@ class VFBMain extends React.Component { GEPPETTO.on(GEPPETTO.Events.Instance_added, function (instance) { that.props.instanceAdded(instance); }); - + GEPPETTO.on(GEPPETTO.Events.Instance_deleted, function (instancePath) { let id = instancePath.split(".")[0]; that.props.instanceDeleted(ACTIONS.INSTANCE_DELETED, id); diff --git a/model/vfb.xmi b/model/vfb.xmi index 0f5bdb24d..4bd9fe6fb 100644 --- a/model/vfb.xmi +++ b/model/vfb.xmi @@ -470,8 +470,8 @@ id="neuron_region_connectivity_query" name="neuron_region_connectivity_query" description="neuron_region_connectivity_query" - query=""statement": "MATCH (primary) WHERE primary.short_form in [{ID}] WITH primary MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_post_synaptic_terminal_in]-(primary) WITH DISTINCT collect(properties(r)) + {} as props, target, primary WITH apoc.map.removeKeys(apoc.map.merge(props[0], props[1]),['iri', 'short_form', 'Related', 'label', 'type']) as synapse_counts, { short_form: target.short_form, label: coalesce(target.label,''), iri: target.iri, types: labels(target), symbol: coalesce(target.`symbol`[0], '')} as object, target,primary OPTIONAL MATCH (o:Class)<-[r:SUBCLASSOF|INSTANCEOF]-(target) WITH CASE WHEN o IS NULL THEN [] ELSE COLLECT ({ short_form: o.short_form, label: coalesce(o.label,''), iri: o.iri, types: labels(o), symbol: coalesce(o.`symbol`[0], '')} ) END AS parents ,primary,target,synapse_counts, object OPTIONAL MATCH (target)<-[:depicts]-(channel:Individual)-[irw:in_register_with]->(template:Individual)-[:depicts]->(template_anat:Individual) WITH template, channel, template_anat, irw, primary, target, synapse_counts, object, parents OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class) WITH CASE WHEN channel IS NULL THEN [] ELSE collect ({ channel: { short_form: channel.short_form, label: coalesce(channel.label,''), iri: channel.iri, types: labels(channel), symbol: coalesce(channel.`symbol`[0], '')} , imaging_technique: { short_form: technique.short_form, label: coalesce(technique.label,''), iri: technique.iri, types: labels(technique), symbol: coalesce(technique.`symbol`[0], '')} ,image: { template_channel : { short_form: template.short_form, label: coalesce(template.label,''), iri: template.iri, types: labels(template), symbol: coalesce(template.`symbol`[0], '')} , template_anatomy: { short_form: template_anat.short_form, label: coalesce(template_anat.label,''), iri: template_anat.iri, types: labels(template_anat), symbol: coalesce(template_anat.`symbol`[0], '')} ,image_folder: COALESCE(irw.folder[0], ''), index: coalesce(apoc.convert.toInteger(irw.index[0]), []) + [] }}) END AS channel_image,primary,target,synapse_counts, object,parents RETURN { core : { short_form: primary.short_form, label: coalesce(primary.label,''), iri: primary.iri, types: labels(primary), symbol: coalesce(primary.`symbol`[0], '')} , description : coalesce(primary.description, []), comment : coalesce(primary.comment, []) } AS term, '64731dc' AS version , synapse_counts, object, parents, channel_image, 'neuron_region_connectivity_query' as query", "parameters" : { "ID" : "$ID" }" - countQuery=""statement": "MATCH (primary) WHERE primary.short_form in [{ID}] WITH primary MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_post_synaptic_terminal_in]-(primary) RETURN count(target) as count", "parameters" : { "ID" : "$ID" }"/> + query=""statement": "MATCH (primary) WHERE primary.short_form in [{ID}] WITH primary MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_postsynaptic_terminal_in]-(primary) WITH DISTINCT collect(properties(r)) + {} as props, target, primary WITH apoc.map.removeKeys(apoc.map.merge(props[0], props[1]),['iri', 'short_form', 'Related', 'label', 'type']) as synapse_counts, { short_form: target.short_form, label: coalesce(target.label,''), iri: target.iri, types: labels(target), symbol: coalesce(target.`symbol`[0], '')} as object, target,primary OPTIONAL MATCH (o:Class)<-[r:SUBCLASSOF|INSTANCEOF]-(target) WITH CASE WHEN o IS NULL THEN [] ELSE COLLECT ({ short_form: o.short_form, label: coalesce(o.label,''), iri: o.iri, types: labels(o), symbol: coalesce(o.`symbol`[0], '')} ) END AS parents ,primary,target,synapse_counts, object OPTIONAL MATCH (target)<-[:depicts]-(channel:Individual)-[irw:in_register_with]->(template:Individual)-[:depicts]->(template_anat:Individual) WITH template, channel, template_anat, irw, primary, target, synapse_counts, object, parents OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class) WITH CASE WHEN channel IS NULL THEN [] ELSE collect ({ channel: { short_form: channel.short_form, label: coalesce(channel.label,''), iri: channel.iri, types: labels(channel), symbol: coalesce(channel.`symbol`[0], '')} , imaging_technique: { short_form: technique.short_form, label: coalesce(technique.label,''), iri: technique.iri, types: labels(technique), symbol: coalesce(technique.`symbol`[0], '')} ,image: { template_channel : { short_form: template.short_form, label: coalesce(template.label,''), iri: template.iri, types: labels(template), symbol: coalesce(template.`symbol`[0], '')} , template_anatomy: { short_form: template_anat.short_form, label: coalesce(template_anat.label,''), iri: template_anat.iri, types: labels(template_anat), symbol: coalesce(template_anat.`symbol`[0], '')} ,image_folder: COALESCE(irw.folder[0], ''), index: coalesce(apoc.convert.toInteger(irw.index[0]), []) + [] }}) END AS channel_image,primary,target,synapse_counts, object,parents RETURN { core : { short_form: primary.short_form, label: coalesce(primary.label,''), iri: primary.iri, types: labels(primary), symbol: coalesce(primary.`symbol`[0], '')} , description : coalesce(primary.description, []), comment : coalesce(primary.comment, []) } AS term, '64731dc' AS version , synapse_counts, object, parents, channel_image, 'neuron_region_connectivity_query' as query", "parameters" : { "ID" : "$ID" }" + countQuery=""statement": "MATCH (primary) WHERE primary.short_form in [{ID}] WITH primary MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_postsynaptic_terminal_in]-(primary) RETURN count(target) as count", "parameters" : { "ID" : "$ID" }"/>