Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • statistical-genetics/jass
  • statistical-genetics/jass-server
2 results
Show changes
Commits on Source (62)
Showing
with 588 additions and 129864 deletions
...@@ -8,6 +8,9 @@ test-python: ...@@ -8,6 +8,9 @@ test-python:
image: python:$PYTHON_VERSION image: python:$PYTHON_VERSION
needs: [] needs: []
stage: test stage: test
retry:
max: 2
when: runner_system_failure
script: script:
- apt-get update && apt install -y libblas-dev liblapack-dev python3-dev - apt-get update && apt install -y libblas-dev liblapack-dev python3-dev
- pip install -r requirements.txt - pip install -r requirements.txt
...@@ -16,8 +19,8 @@ test-python: ...@@ -16,8 +19,8 @@ test-python:
matrix: matrix:
- PYTHON_VERSION: [ - PYTHON_VERSION: [
'3.7', '3.7',
'3.8', # '3.8',
'3.9', # '3.9',
'3.10', '3.10',
] ]
...@@ -27,7 +30,12 @@ test-docker-compose: ...@@ -27,7 +30,12 @@ test-docker-compose:
services: services:
- registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:dind - registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:dind
needs: [] needs: []
when: delayed
start_in: 3 minutes
stage: test stage: test
retry:
max: 2
when: runner_system_failure
script: script:
- apk update && apk upgrade && apk add jq bash curl - apk update && apk upgrade && apk add jq bash curl
- ./test_docker_compose.sh - ./test_docker_compose.sh
...@@ -77,6 +85,9 @@ pages: ...@@ -77,6 +85,9 @@ pages:
.build: .build:
stage: build stage: build
image: registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:latest image: registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:latest
retry:
max: 2
when: runner_system_failure
services: services:
- registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:dind - registry-gitlab.pasteur.fr/dsi-tools/docker-images/docker:dind
script: script:
...@@ -153,6 +164,9 @@ build-client-prod: ...@@ -153,6 +164,9 @@ build-client-prod:
- test-python - test-python
# - test-backend-image # - test-backend-image
image: harbor.pasteur.fr/kube-system/helm-kubectl:3.4.0 image: harbor.pasteur.fr/kube-system/helm-kubectl:3.4.0
retry:
max: 2
when: runner_system_failure
script: script:
# create if missing the shared pvc to save data # create if missing the shared pvc to save data
- kubectl apply -n $NAMESPACE -f chart/pvc-shared-data.yaml - kubectl apply -n $NAMESPACE -f chart/pvc-shared-data.yaml
...@@ -224,6 +238,9 @@ delete-dev-deployment: ...@@ -224,6 +238,9 @@ delete-dev-deployment:
environment: environment:
name: "k8sdev-01/jass-dev/${CI_COMMIT_REF_SLUG}" name: "k8sdev-01/jass-dev/${CI_COMMIT_REF_SLUG}"
action: stop action: stop
retry:
max: 2
when: runner_system_failure
script: script:
- echo "Removing $CI_COMMIT_REF_SLUG" - echo "Removing $CI_COMMIT_REF_SLUG"
- helm delete -n ${NAMESPACE} ${CI_COMMIT_REF_SLUG} - helm delete -n ${NAMESPACE} ${CI_COMMIT_REF_SLUG}
......
apiVersion: v1
kind: Service
metadata:
name: {{ printf "%s-%s" .Release.Name "celery-worker-svc" }}
labels:
{{- include "chart.labels" . | nindent 4 }}
app: celery-worker-svc
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
{{- include "chart.labels" . | nindent 4 }}
app: celery-worker
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: StatefulSet
metadata: metadata:
name: {{ printf "%s-%s" .Release.Name "celery" }} name: {{ printf "%s-%s" .Release.Name "celery" }}
labels: labels:
...@@ -10,8 +26,12 @@ spec: ...@@ -10,8 +26,12 @@ spec:
matchLabels: matchLabels:
{{- include "chart.selectorLabels" . | nindent 6 }} {{- include "chart.selectorLabels" . | nindent 6 }}
app: celery-worker app: celery-worker
strategy: serviceName: {{ printf "%s-%s" .Release.Name "celery-worker-svc" }}
type: Recreate {{- if .Values.celery.replicas }}
replicas: {{ .Values.celery.replicas }}
{{- end }}
updateStrategy:
type: RollingUpdate
template: template:
metadata: metadata:
{{- with .Values.podAnnotations }} {{- with .Values.podAnnotations }}
...@@ -45,6 +65,10 @@ spec: ...@@ -45,6 +65,10 @@ spec:
value: {{ printf "%s-rabbitmq" .Release.Name }} value: {{ printf "%s-rabbitmq" .Release.Name }}
- name: RABBITMQ_PORT - name: RABBITMQ_PORT
value: '5672' value: '5672'
{{- if .Values.projects.minSizeToKeepFree }}
- name: MIN_SIZE_TO_KEEP_IN_PROJECTS_DIR_IN_MB
value: '{{ .Values.projects.minSizeToKeepFree }}'
{{- end }}
command: command:
- "celery" - "celery"
- "-A" - "-A"
......
projects: projects:
size: 34Gi # quota - 15Gi from pvc-shared-data.yaml - 1Gi for rabbitMQ size: 84Gi # quota - 15Gi from pvc-shared-data.yaml - 1Gi for rabbitMQ
minSizeToKeepFree: 16384 # size is in MB,
celery: celery:
resources: resources:
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'BrandonGrotesqueBld'; font-family: 'BrandonGrotesqueBld';
src: local('BrandonGrotesqueBld'), src: local('BrandonGrotesqueBld'),
...@@ -25,8 +25,8 @@ ...@@ -25,8 +25,8 @@
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: 'PlayfairDisplayBld'; font-family: 'PlayfairDisplayBld';
src: local('PlayfairDisplayBld'), src: local('PlayfairDisplayBld'),
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
} }
/* FONT DETAILS */ /* FONT DETAILS */
* { * {
font-family: 'BrandonGrotesqueReg'; font-family: 'BrandonGrotesqueReg';
font-size: 18px; font-size: 18px;
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
text-transform: capitalize; text-transform: capitalize;
font-size: 30px; font-size: 30px;
} }
h2 { h2 {
font-family: 'PlayfairDisplayBld'; font-family: 'PlayfairDisplayBld';
font-size: 24px; font-size: 24px;
...@@ -99,9 +99,10 @@ ...@@ -99,9 +99,10 @@
} }
.main_intro { .main_intro {
width: 75%; text-align: justify;
margin: 0 auto; font-family: 'BrandonGrotesqueReg';
padding: 30px 0px 30px 0px; font-size: 18px;
line-height: 1.5;
} }
.card-title { .card-title {
...@@ -109,18 +110,6 @@ ...@@ -109,18 +110,6 @@
font-size: 25px; font-size: 25px;
} }
.left-card {
text-align: left;
width: 60%;
margin: 20px auto;
}
.right-card {
text-align: right;
width: 60%;
margin: 20px auto;
}
button,input,select,textarea { button,input,select,textarea {
font-size:100%; font-size:100%;
margin:0; margin:0;
...@@ -233,4 +222,4 @@ button:active,input[type="submit"]:active,input[type="button"]:active,input[type ...@@ -233,4 +222,4 @@ button:active,input[type="submit"]:active,input[type="button"]:active,input[type
.footer-text { .footer-text {
font-size: 12px; font-size: 12px;
} }
\ No newline at end of file
<template> <template>
<v-card :elevation=0 :class="myClass"> <v-card :elevation=0 :class="myClass">
<v-container> <v-col
<v-card-title class="card-title"> {{title}} </v-card-title> cols="12"
sm="8"
offset-sm="2"
lg="6"
offset-lg="3">
<v-card-title class="card-title"> {{title}} </v-card-title>
<v-row> <v-row>
<v-col <v-col
cols="5" cols="12"
md="3"> sm="5"
md="4">
<v-img <v-img
class="mx-auto"
height="200" height="200"
width="200" width="200"
:src='myImg' :src='myImg'
></v-img> ></v-img>
</v-col> </v-col>
<v-col <v-col
cols="12"
sm="7" sm="7"
md="9"> md="8">
<v-card-text style="font-size:18px; color:black;"> <v-card-text style="font-size:18px; color:black;text-align: justify;">
{{description}} {{description}}
</v-card-text> </v-card-text>
</v-col </v-col>
> <v-col
</v-row> cols="12"
<v-card-actions style="width: 50%; margin: 0 auto; background: white; border-radius: 3px; padding: 20px; box-shadow: 1px 1px 5px #aeaeae;"> md="8"
offset-md="2">
<v-card-actions style="margin: 0 auto; background: white; border-radius: 3px; padding: 20px; box-shadow: 1px 1px 5px #aeaeae;">
<v-card-text style="font-size:18px; text-align:center;"> {{short_btn}}</v-card-text> <v-card-text style="font-size:18px; text-align:center;"> {{short_btn}}</v-card-text>
<v-btn :to ="link" :style="myStyle" style="color:#fff; text-transform:none; margin:0 auto;"> <v-btn :to ="link" :style="myStyle" style="color:#fff; text-transform:none; margin:0 auto;">
{{short_desc}} {{short_desc}}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-container> </v-col>
</v-row>
</v-col>
</v-card> </v-card>
</template> </template>
...@@ -37,3 +49,10 @@ ...@@ -37,3 +49,10 @@
props: ['title', "description", "short_btn", "short_desc","link","myStyle","myClass","myImg"] props: ['title', "description", "short_btn", "short_desc","link","myStyle","myClass","myImg"]
} }
</script> </script>
<style>
.card-title {
word-break:unset;
}
</style>
<template> <template>
<div> <v-speed-dial v-model="dialShare" absolute top right direction="left" open-on-hover>
<v-speed-dial v-model="dialShare" absolute right bottom direction="left" open-on-hover>
<template v-slot:activator> <template v-slot:activator>
<v-btn fab bottom small color="primary"> <v-btn fab bottom small color="primary">
<v-icon v-if="dialShare">mdi-close</v-icon> <v-icon v-if="dialShare">mdi-close</v-icon>
...@@ -16,12 +15,13 @@ ...@@ -16,12 +15,13 @@
<v-btn dark fab bottom color="green" small :href="`https://wa.me/?text=Checkout%20this%20page.%20${pageUrl}`" target="_blank"> <v-btn dark fab bottom color="green" small :href="`https://wa.me/?text=Checkout%20this%20page.%20${pageUrl}`" target="_blank">
<v-icon>mdi-whatsapp</v-icon> <v-icon>mdi-whatsapp</v-icon>
</v-btn> </v-btn>
<v-btn dark fab bottom color="tertiary" small :href="`mailto:?subject=Awesomeness!&amp;body=Checkout this page!<a href='${pageUrl}'>${pageUrl}</a>`" target="_blank"> <v-btn dark fab bottom color="primary" small :href="`mailto:?subject=JASS analysis&amp;body=Here is an analysis computed in JASS: ${pageUrl}`" target="_blank" title="Send by mail">
<v-icon>mdi-email</v-icon> <v-icon>mdi-email</v-icon>
</v-btn> </v-btn>
<v-btn dark fab bottom color="primary" small v-on:click="copySign()" title="Copy to clipboard">
<v-icon>mdi-clipboard</v-icon>
</v-btn>
</v-speed-dial> </v-speed-dial>
</div>
</template> </template>
<script> <script>
...@@ -31,8 +31,13 @@ ...@@ -31,8 +31,13 @@
return { return {
dialShare: false dialShare: false
} }
},
methods: {
copySign() {
navigator.clipboard.writeText(this.pageUrl);
}
} }
} }
</script> </script>
\ No newline at end of file
...@@ -20,10 +20,25 @@ ...@@ -20,10 +20,25 @@
></v-img> ></v-img>
</v-app-bar> </v-app-bar>
<v-tabs fixed-tabs dark> <v-tabs fixed-tabs dark>
<span class="hidden-xs-only" style="width:280px"></span>
<v-tab v-for="(item, i) in items" <v-tab v-for="(item, i) in items"
:key="i" :key="i"
:to="item.to" v-text="item.title"> :to="item.to" v-text="item.title">
</v-tab> </v-tab>
<v-tab to="/workload" style="max-width:280px;margin-right: inherit;margin-left: auto;">
Workload
<v-progress-linear
:value="this.activeTask/(this.activeTask+this.pendingTask)*100"
:background-color="(this.activeTask==0)&&(this.pendingTask==0)?'#fffb':undefined"
:buffer-value="this.activeTask === null ? 0 : 100"
height="20"
:stream="this.activeTask === null"
:striped="this.activeTask !== null"
color="rgb(45, 150, 250)"
:title="'Task: '+this.activeTask +' running and ' +this.pendingTask+' pending.'"
class="hidden-xs-only ml-2"
><small>{{textProgressBar}}</small></v-progress-linear>
</v-tab>
</v-tabs> </v-tabs>
<v-main> <v-main>
<v-container> <v-container>
...@@ -38,6 +53,8 @@ ...@@ -38,6 +53,8 @@
export default { export default {
data () { data () {
return { return {
activeTask: null,
pendingTask: null,
clipped: false, clipped: false,
drawer: false, drawer: false,
fixed: false, fixed: false,
...@@ -125,6 +142,36 @@ export default { ...@@ -125,6 +142,36 @@ export default {
], ],
}, },
} }
},
created(){
this.getQueueStatus();
},
methods:{
async getQueueStatus(){
if (this.isready)
return;
await this.$axios.$get('/queue_status').then((function (resultStatus) {
console.log(resultStatus);
this.activeTask = resultStatus.active;
this.pendingTask = resultStatus.reserved;
setTimeout(this.getQueueStatus, 5000);
}).bind(this)).catch((function () {
setTimeout(this.getQueueStatus, 10000);
}).bind(this));
},
},
computed:{
textProgressBar(){
if (this.activeTask === null)
return ''
if (this.pendingTask === 0){
if (this.activeTask ===0 ){
return 'no load';
}
return this.activeTask + " running";
}
return this.activeTask + " + " + this.pendingTask;
},
} }
} }
</script> </script>
......
...@@ -2,47 +2,44 @@ ...@@ -2,47 +2,44 @@
<div> <div>
<h1 class="main_title"> Welcome to JASS v2.1</h1> <h1 class="main_title"> Welcome to JASS v2.1</h1>
<h3>a web interface for the Joint Analysis of GWAS Summary Statistics</h3> <h3>a web interface for the Joint Analysis of GWAS Summary Statistics</h3>
<p class="main_intro"> <v-row><v-col cols="12" sm="10" offset-sm="1">
<v-card-text style="text-align: justify;" class="main_intro">
The JASS web interface efficiently compute multi-trait genome-wide association study (GWAS) and enable user to interactively explore results. The JASS web interface efficiently compute multi-trait genome-wide association study (GWAS) and enable user to interactively explore results.
Multi-trait GWAS can increase statistical power by leveraging pleiotropy, but also can deepen our understanding of SNPs functional effect (for a detailed explanation see the citation box below). Multi-trait GWAS can increase statistical power by leveraging pleiotropy, but also can deepen our understanding of SNPs functional effect (for a detailed explanation see the citation box below).
Currently this website host <b>{{initmeta.nb_phenotypes}}</b> traits available for analysis with the Omnibus test. Currently this website host <b>{{initmeta.nb_phenotypes}}</b> traits available for analysis with the Omnibus test.
All GWAS have been pre-processed using the <a href="https://gitlab.pasteur.fr/statistical-genetics/jass_suite_pipeline">JASS pre-processing pipeline</a> and imputation of missing statistics has been conducted using the <a href="https://gitlab.pasteur.fr/statistical-genetics/raiss">RAISS software</a>, resulting in a total of <b>{{initmeta.nb_snps}}</b> SNPs available for analysis. All GWAS have been pre-processed using the <a href="https://gitlab.pasteur.fr/statistical-genetics/jass_suite_pipeline">JASS pre-processing pipeline</a> and imputation of missing statistics has been conducted using the <a href="https://gitlab.pasteur.fr/statistical-genetics/raiss">RAISS software</a>, resulting in a total of <b>{{initmeta.nb_snps}}</b> SNPs available for analysis.
To analyze data in your own facility and/or access supplementary joint analysis tests, please download and install the <a href="https://statistical-genetics.pages.pasteur.fr/jass/">JASS python package.</a> To analyze data in your own facility and/or access supplementary joint analysis tests, please download and install the <a href="https://statistical-genetics.pages.pasteur.fr/jass/">JASS python package.</a>
</p> </v-card-text>
</v-col></v-row>
<v-container fluid>
<v-row dense> <v-row dense>
<AnalysisCard v-for="analysis in analysis" :key="analysis.title" :title="analysis.title" :description="analysis.description" :short_btn="analysis.short_btn" :short_desc="analysis.short_desc" :link="analysis.link" :myStyle="analysis.myStyle" :myClass="analysis.myClass" :myImg="analysis.myImg" /> <AnalysisCard v-for="analysis in analysis" :key="analysis.title" :title="analysis.title" :description="analysis.description" :short_btn="analysis.short_btn" :short_desc="analysis.short_desc" :link="analysis.link" :myStyle="analysis.myStyle" :myClass="analysis.myClass" :myImg="analysis.myImg" />
</v-row> </v-row>
</v-container>
<div style="text-align: center; background-color: #fbfbfb; padding: 20px; width: 80%; margin: 30px auto;"> <v-row><v-col cols="10" offset="1" md="8" offset-md="2" style="text-align: center; background-color: #f8f8f8;margin:1em auto" id="citations">
<h2> Citations </h2> <h2> Citations </h2>
<p style="text-align:center; padding:20px;"> <p style="text-align:center; padding:20px;">
<h4>When referring to theoretical basis of JASS tests, cite :</h4> <h4>When referring to theoretical basis of JASS tests, cite :</h4>
<b><i class="fas fa-quote-left"> Julienne, H., Laville, V., McCaw, Z.R., He, Z., Guillemot, V., Lasry, C., Ziyatdinov, A., Nerin, C., Vaysse, A., Lechat, P., Ménager, H and Aschard, H. </i>Multitrait GWAS to connect disease variants and biological mechanisms</b> <i>PLOS Genetics</i> Aug 30;17(8):e1009713. https://doi.org/10.1371/journal.pgen.1009713<a href="https://academic.oup.com/nargab/article/2/1/lqaa003/5715214" target="_blank">https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1009713</a><br> <b><i class="fas fa-quote-left"> Julienne, H., Laville, V., McCaw, Z.R., He, Z., Guillemot, V., Lasry, C., Ziyatdinov, A., Nerin, C., Vaysse, A., Lechat, P., Ménager, H and Aschard, H. </i>Multitrait GWAS to connect disease variants and biological mechanisms</b> <i>PLOS Genetics</i> Aug 30;17(8):e1009713. doi: 10.1371/journal.pgen.1009713 <br/><a href="https://academic.oup.com/nargab/article/2/1/lqaa003/5715214" target="_blank">https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1009713</a><br>
<br> <br>
<h4>When using JASS software in publication, cite :</h4> <h4>When using JASS software in publication, cite :</h4>
<b><i class="fas fa-quote-left">Julienne, H., Lechat, P., Guillemot, V., Lasry, C., Yao, C., Araud, R., Laville, V., Vilhjalmsson, B., Ménager, H. and Aschard, H. </i>JASS: command line and web interface for the joint analysis of GWAS results.</b> <i>NAR Genom Bioinform</i> 2020 Mar;2(1):lqaa003. <b><i class="fas fa-quote-left">Julienne, H., Lechat, P., Guillemot, V., Lasry, C., Yao, C., Araud, R., Laville, V., Vilhjalmsson, B., Ménager, H. and Aschard, H. </i>JASS: command line and web interface for the joint analysis of GWAS results.</b> <i>NAR Genom Bioinform</i> 2020 Mar;2(1):lqaa003.
doi: 10.1093/nargab/lqaa003. Epub 2020 Jan 24. <a href="https://academic.oup.com/nargab/article/2/1/lqaa003/5715214" target="_blank">https://academic.oup.com/nargab/article/2/1/lqaa003/5715214</a><br> doi: 10.1093/nargab/lqaa003. Epub 2020 Jan 24. <br/><a href="https://academic.oup.com/nargab/article/2/1/lqaa003/5715214" target="_blank">https://academic.oup.com/nargab/article/2/1/lqaa003/5715214</a><br>
<br> <br>
<h4>When refereeing to the imputation of summary statistics, cite :</h4> <h4>When refereeing to the imputation of summary statistics, cite :</h4>
<b><i class="fas fa-quote-left">Julienne, H., Shi, H., Pasaniuc, B. and Aschard, H. </i>RAISS: robust and accurate imputation from summary statistics.</b> <i>Bioinformatics</i> 2019 Nov 1;35(22):4837-4839. <b><i class="fas fa-quote-left">Julienne, H., Shi, H., Pasaniuc, B. and Aschard, H. </i>RAISS: robust and accurate imputation from summary statistics.</b> <i>Bioinformatics</i> 2019 Nov 1;35(22):4837-4839.
doi: 10.1093/bioinformatics/btz466. <a href="https://academic.oup.com/bioinformatics/article/35/22/4837/5512360" target="_blank">https://academic.oup.com/bioinformatics/article/35/22/4837/5512360</a><br> doi: 10.1093/bioinformatics/btz466. <br/><a href="https://academic.oup.com/bioinformatics/article/35/22/4837/5512360" target="_blank">https://academic.oup.com/bioinformatics/article/35/22/4837/5512360</a><br>
<br> <br>
</p> </p>
</div> </v-col>
<v-row> <v-col cols="10" offset="1" md="8" offset-md="2" style="text-align: center; background-color: #f8f8f8;margin:1em auto">
<v-col cols="12">
<div style="text-align: center; background-color: #fbfbfb; padding: 20px; width: 80%; margin: 30px auto;">
<h2> Team </h2> <h2> Team </h2>
<p> <p>
Jass is developed at Institut Pasteur by the <a href="https://research.pasteur.fr/en/team/statistical-genetics/">Statistical Genetics group</a> and the <a href="https://research.pasteur.fr/en/team/bioinformatics-and-biostatistics-hub/">Biostatistic and Bioinformatic HUB</a> Jass is developed at Institut Pasteur by the <a href="https://research.pasteur.fr/en/team/statistical-genetics/">Statistical Genetics group</a> and the <a href="https://research.pasteur.fr/en/team/bioinformatics-and-biostatistics-hub/">Biostatistic and Bioinformatic HUB</a>
<p> <p>
<a href="https://research.pasteur.fr/en/team/statistical-genetics/"> The Statistical Genetics group</a>, lead by Hugues Aschard, is interested in developping new methods to analyze GWAS data. In particular, in JASS, we leverage publicly available GWAS results to discover new signals and understand pleiotropy. While our team focus mainly on developping the theory behind the tools, we also aim to make our software widely available and to follow high quality software development standards. Hence, our close collaboration with the hub and more specifically with its WINTER group dedicated to web developement. <a href="https://research.pasteur.fr/en/team/hub-winter/">The WINTER group</a> is a software development team focusing mainly on Web technologies for publishing and sharing scientific tools, analysis, data and workflows. We work hand in hand to develop JASS website. <a href="https://research.pasteur.fr/en/team/statistical-genetics/"> The Statistical Genetics unit</a>, lead by Hugues Aschard, is interested in developping new methods to analyze GWAS data. In particular, in JASS, we leverage publicly available GWAS results to discover new signals and understand pleiotropy. While our team focus mainly on developping the theory behind the tools, we also aim to make our software widely available and to follow high quality software development standards. Hence, our close collaboration with the hub and more specifically with its WINTER group dedicated to web developement. <a href="https://research.pasteur.fr/en/team/hub-winter/">The WINTER group</a> is a software development team focusing mainly on Web technologies for publishing and sharing scientific tools, analysis, data and workflows. We work hand in hand to develop JASS website.
</p> </p>
</div>
</v-col> </v-col>
</v-row> </v-row>
...@@ -85,7 +82,6 @@ export default { ...@@ -85,7 +82,6 @@ export default {
myStyle:{ myStyle:{
backgroundColor:'#2D96FA', backgroundColor:'#2D96FA',
}, },
myClass:'left-card',
myImg:'/jass19Avrilsmall.png', myImg:'/jass19Avrilsmall.png',
}, },
...@@ -107,3 +103,9 @@ export default { ...@@ -107,3 +103,9 @@ export default {
} }
} }
</script> </script>
<style>
#citations a[target="_blank"] {
word-break:break-all;
}
</style>
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
Please select the traits you want to analyze jointly with the Omnibus test. A research box bellow allows you to query our current database. The maximum number of trait that can be analyzed jointly is 64 traits. Computation time for a large number of trait can take up to half an hour. Please select the traits you want to analyze jointly with the Omnibus test. A research box bellow allows you to query our current database. The maximum number of trait that can be analyzed jointly is 64 traits. Computation time for a large number of trait can take up to half an hour.
</p> </p>
</div> </div>
<h2>Select the GWAS to be analyzed jointly:</h2> <h2>Select at least two GWASs to be analyzed jointly:</h2>
<div id="app"> <div id="app">
<v-app> <v-app>
<v-data-table <v-data-table
...@@ -33,7 +33,7 @@ Please select the traits you want to analyze jointly with the Omnibus test. A re ...@@ -33,7 +33,7 @@ Please select the traits you want to analyze jointly with the Omnibus test. A re
</template> </template>
</v-data-table> </v-data-table>
<div align="left" justify="start"> <div align="left" justify="start">
<v-btn depressed color="primary" v-on:click="runAnalysis()"> <v-btn depressed color="primary" v-on:click="runAnalysis()" :disabled="selectedRows.length <=1">
Launch analysis Launch analysis
</v-btn> </v-btn>
</div> </div>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
class="text-subtitle-1 text-center" class="text-subtitle-1 text-center"
cols="12" cols="12"
> >
<h2>Creating the worktable ! It might take a few minutes</h2> <h2>Creating the worktable ! It might take a few minutes </h2>
</v-col> </v-col>
<v-col <v-col
class="text-subtitle-1 text-center" class="text-subtitle-1 text-center"
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
> >
<PhenotypeCard v-for="pheno in phenotypesDict" :key="pheno.id" :phe="pheno"/> <PhenotypeCard v-for="pheno in phenotypesDict" :key="pheno.id" :phe="pheno"/>
</v-col> </v-col>
<v-col cols="6"> <v-col cols="12" md="8" lg="6">
<v-progress-linear <v-progress-linear
color="#298e49" color="#298e49"
striped striped
rounded rounded
height="20" height="20"
v-model="progress" :value="progress"
> >
<strong style="color:white; text-transform:capitalize;">{{progress}} %</strong> <strong style="color:white; text-transform:capitalize;">{{progress}} %</strong>
</v-progress-linear> </v-progress-linear>
...@@ -34,14 +34,13 @@ ...@@ -34,14 +34,13 @@
<v-col cols="12"> <v-col cols="12">
<v-card > <v-card>
<ShareLink :pageUrl="sharedUrl"/> <ShareLink :pageUrl="sharedUrl" style="top:-1em"/>
</v-card>
<div class="row" style="text-align:left;padding:12px;margin:0;"> <div class="row" style="text-align:left;padding:12px;margin:0;">
<h1 style="text-transform:none;">Results for :</h1> <h1 style="text-transform:none;">Results for :</h1>
<PhenotypeCard v-for="pheno in phenotypesDict" :key="pheno.id" :phe="pheno"/> <PhenotypeCard v-for="pheno in phenotypesDict" :key="pheno.id" :phe="pheno"/>
</div> </div>
</v-card>
<v-card style="margin-top:10px; margin-bottom:10px;" outlined tile no-gutter> <v-card style="margin-top:10px; margin-bottom:10px;" outlined tile no-gutter>
<v-card-title class="text-h5"> <v-card-title class="text-h5">
<v-row justify="start" no-gutters> <v-row justify="start" no-gutters>
...@@ -57,10 +56,14 @@ ...@@ -57,10 +56,14 @@
<b>Test performed:</b>Omnibus <b>Test performed:</b>Omnibus
</v-col> </v-col>
<v-col > <v-col >
<v-card> <v-card style="padding:16px">
Genetic covariance derived with <a href="https://www.nature.com/articles/ng.3406">the LDScore</a> Genetic covariance derived with <a href="https://www.nature.com/articles/ng.3406">the LDScore</a>
The value on the diagonal correspond to the heritability of the trait. The value on the diagonal correspond to the heritability of the trait.
<div id="divCorrelationHeat" ref="divCorrelationHeat"><!-- Plotly chart will be drawn inside this DIV --></div> <div v-if="this.gencov && 'error' in this.gencov">
<v-icon style="margin-top: -8px;" color="error">mdi-alert-octagon</v-icon>
Computation failure: <v-card-text style="white-space: pre;">{{gencov.error}}</v-card-text>
</div>
<div v-else id="divCorrelationHeat" ref="divCorrelationHeat"><!-- Plotly chart will be drawn inside this DIV --></div>
</v-card> </v-card>
</v-col> </v-col>
</v-row> </v-row>
...@@ -81,7 +84,7 @@ ...@@ -81,7 +84,7 @@
</v-col> </v-col>
<v-col > <v-col >
<vue-json-to-csv :json-data="Regions"> <vue-json-to-csv :json-data="Regions" :csv-title="'LD-independent lead SNPs - '+ this.project.id">
<v-btn small color="#298e49" style="color:#fff; text-transform:capitalize;" v-on:click="processExportAllJASSPVAL()" > <v-btn small color="#298e49" style="color:#fff; text-transform:capitalize;" v-on:click="processExportAllJASSPVAL()" >
Export LD-independent lead SNPs Export LD-independent lead SNPs
</v-btn> </v-btn>
...@@ -129,6 +132,19 @@ ...@@ -129,6 +132,19 @@
indeterminate indeterminate
></v-progress-circular> ></v-progress-circular>
</v-btn> </v-btn>
<v-btn
:disabled="status.qq_plot != 'READY'"
small color="#298e49" style="color:#fff;text-transform:capitalize;"
v-on:click="qqPlot()">
QQ plot
<v-progress-circular
v-if="status.qq_plot != 'READY'"
:size="15"
:width="2"
style="margin-left:5px"
indeterminate
></v-progress-circular>
</v-btn>
<p>Tips : You can share the link of this page with your collaborators. They will have access to the same results.</p> <p>Tips : You can share the link of this page with your collaborators. They will have access to the same results.</p>
</v-col> </v-col>
</v-row> </v-row>
...@@ -162,7 +178,7 @@ ...@@ -162,7 +178,7 @@
:items-per-page="5" :items-per-page="5"
class="elevation-1" class="elevation-1"
></v-data-table> ></v-data-table>
<vue-json-to-csv :json-data="significantsRegions"> <vue-json-to-csv :json-data="significantsRegions" :csv-title="'Significants regions - '+ this.project.id">
<v-btn small color="#298e49" style="color:#fff;">export csv</v-btn> <v-btn small color="#298e49" style="color:#fff;">export csv</v-btn>
<!-- <button> <!-- <button>
<b>export csv</b> <b>export csv</b>
...@@ -493,6 +509,11 @@ methods:{ ...@@ -493,6 +509,11 @@ methods:{
console.log("quadrant "+desiredLink); console.log("quadrant "+desiredLink);
window.open(desiredLink,'_blank'); window.open(desiredLink,'_blank');
}, },
qqPlot(){
const desiredLink = process.env.API_URL+"/projects/"+this.project.id+"/qqplot";
console.log("qq "+desiredLink);
window.open(desiredLink,'_blank');
},
manhattanPlot(){ manhattanPlot(){
const desiredLink = process.env.API_URL+"/projects/"+this.project.id+"/globalmanhattan"; const desiredLink = process.env.API_URL+"/projects/"+this.project.id+"/globalmanhattan";
console.log("globalmanhattan "+desiredLink); console.log("globalmanhattan "+desiredLink);
...@@ -553,7 +574,7 @@ methods:{ ...@@ -553,7 +574,7 @@ methods:{
this.sharedUrl = "http://"+window.location.host + "/projects/" + this.project.id; this.sharedUrl = "http://"+window.location.host + "/projects/" + this.project.id;
console.log(this.project.id); console.log(this.project.id);
this.status=result.status; this.status=result.status;
if(result.status.worktable ==="READY"){ if(result.status.metadata ==="READY" && ! this.isready){
await this.$axios.$get('/projects/'+this.project.id+"/gencov").then((async function (result2) { await this.$axios.$get('/projects/'+this.project.id+"/gencov").then((async function (result2) {
this.gencov = result2; this.gencov = result2;
...@@ -574,16 +595,26 @@ methods:{ ...@@ -574,16 +595,26 @@ methods:{
await this.$axios.$get('/projects/'+this.project.id+"/metadata").then((function (result4) { await this.$axios.$get('/projects/'+this.project.id+"/metadata").then((function (result4) {
this.metadata = result4; this.metadata = result4;
}).bind(this)); }).bind(this));
}).bind(this)); }).bind(this)).catch(error => {
console.error(error.response.data.detail);
this.gencov = {'error': error.response.data.detail}
this.isready=true;
});
} }
let keepFetching=true;
for (const step in result.status){ for (const step in result.status){
if (result.status[step] !=="READY"){ if (result.status[step] !=="READY"){
setTimeout(this.getStatus, 10000); keepFetching=true
break; break;
}else{
keepFetching=false;
} }
} }
if (keepFetching){
setTimeout(this.getStatus, 5000);
}
}).bind(this)).catch((function () {
setTimeout(this.getStatus, 10000);
}).bind(this)); }).bind(this));
......
<template>
<v-row>
<v-col cols=12 style="text-align:center">
<h1>Server workload</h1>
</v-col>
<v-col cols=12 >
<v-row>
<v-col cols=6 style="text-align:right">
<h4>Compute node:</h4>
</v-col>
<v-col cols=6 >
<h4>{{this.worker}}</h4>
</v-col>
</v-row>
</v-col>
<v-col cols=12 >
<v-row>
<v-col cols=6 style="text-align:right">
<h4>Tasks being processed:</h4>
</v-col>
<v-col cols=6 >
<h4>{{this.activeTask}}</h4>
</v-col>
</v-row>
</v-col>
<v-col cols=12 >
<v-row>
<v-col cols=6 style="text-align:right">
<h4>Pending tasks to be computed</h4>
</v-col>
<v-col cols=6 >
<h4>{{this.pendingTask}}</h4>
</v-col>
</v-row>
</v-col>
<v-col
v-if="activeTask===0"
style="text-align:center"
cols=12>
Your analysis would start right away.
</v-col>
<v-col
v-else-if="activeTask>0"
style="text-align:center"
cols=12>
Your analysis would be queued, and processed as soon as possible.
</v-col>
<v-col
v-else
cols=12>
&nbsp;
</v-col>
</v-row>
</template>
<script>
export default {
data () {
return {
activeTask: '_',
pendingTask: '_',
worker: '_',
}
},
created(){
this.getQueueStatus();
},
methods:{
async getQueueStatus(){
await this.$axios.$get('/queue_status').then((function (resultStatus) {
console.log(resultStatus);
this.activeTask = resultStatus.active;
this.worker = resultStatus.worker;
this.pendingTask = resultStatus.reserved;
setTimeout(this.getQueueStatus, 5000);
}).bind(this)).catch((function () {
setTimeout(this.getQueueStatus, 10000);
}).bind(this));
},
}
}
</script>
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
This diff is collapsed.
File deleted
Installation Installation
============ ============
You can use JASS locally either using the command line interface in a terminal, or by running a web server. Deployment in a public server is also later discussed in this document. You can use JASS locally either using the command line interface in a terminal, or by running a web server. Deployment in a public server is also later discussed in this document.
You need **python3** to install and use JASS. As of April 2022, JASS runs on python from 3.6 to 3.10. You need **python3** to install and use JASS. As of April 2022, JASS runs on python from 3.6 to 3.10.
Installation with pip (recommended) Installation with pip (recommended)
----------------------------------- -----------------------------------
We advise users to install jass in a virtual environment We advise users to install jass in a virtual environment
.. code-block:: shell .. code-block:: shell
python3 -m venv $PATH_NEW_ENVIRONMENT python3 -m venv $PATH_NEW_ENVIRONMENT
source $PATH_NEW_ENVIRONMENT/bin/activate source $PATH_NEW_ENVIRONMENT/bin/activate
pip3 install git+https://gitlab.pasteur.fr/statistical-genetics/jass.git pip3 install git+https://gitlab.pasteur.fr/statistical-genetics/jass.git
pip3 install -r https://gitlab.pasteur.fr/statistical-genetics/jass/-/raw/master/requirements.txt pip3 install -r https://gitlab.pasteur.fr/statistical-genetics/jass/-/raw/master/requirements.txt
Installation with conda
----------------------- Installation with conda
-----------------------
Installation with pip (recommended)
----------------------------------- A procedure for the correct installation of JASS in Anaconda environment for development purpose is developed in detail below.
We advise users to install jass in a virtual environment
**1. Create a specific directory and load the code on your laptop (only the first time)**
.. code-block:: shell
For example, the directory $HOME/DEVELOP_JASS has been created.
python3 -m venv $PATH_NEW_ENVIRONMENT In a TERMINAL window, type the following instructions:
source $PATH_NEW_ENVIRONMENT/bin/activate
pip3 install git+https://gitlab.pasteur.fr/statistical-genetics/jass.git .. code-block:: shell
pip3 install -r https://gitlab.pasteur.fr/statistical-genetics/jass/-/raw/master/requirements.txt
cd $HOME/DEVELOP_JASS
Installation with conda git clone https://gitlab.pasteur.fr/statistical-genetics/jass
-----------------------
**2. Change directory to the JASS main directory**
A procedure for the correct installation of JASS in Anaconda environment for development purpose is developed in detail below.
.. code-block:: shell
**1. Create a specific directory and load the code on your laptop (only the first time)**
cd $HOME/DEVELOP_JASS/jass
For example, the directory $HOME/DEVELOP_JASS has been created.
In a TERMINAL window, type the following instructions: **3. Create a specific virtual ANACONDA environment for JASS:**
.. code-block:: shell .. note::
cd $HOME/DEVELOP_JASS You have to check that you are in the **base** environment of Anaconda before creating the new virtual environment:
git clone https://gitlab.pasteur.fr/statistical-genetics/jass (for example, Dev_Jass_Pyt36 with python 3.6)
**2. Change directory to the JASS main directory** .. code-block:: shell
.. code-block:: shell conda create --name Dev_Jass_Pyt36 python=3.6
cd $HOME/DEVELOP_JASS/jass **4. Activation of the new environment**
**3. Create a specific virtual ANACONDA environment for JASS:** .. code-block:: shell
.. note:: conda activate Dev_Jass_Pyt36
You have to check that you are in the **base** environment of Anaconda before creating the new virtual environment: **5. Installing JASS dependencies**
(for example, Dev_Jass_Pyt36 with python 3.6)
.. code-block:: shell
.. code-block:: shell
pip install -r requirements.txt
conda create --name Dev_Jass_Pyt36 python=3.6
**6. Installing JASS for development purpose**
**4. Activation of the new environment**
.. code-block:: shell
.. code-block:: shell
pip install -e .
conda activate Dev_Jass_Pyt36
**7. Installation verification**
**5. Installing JASS dependencies**
.. code-block:: shell
.. code-block:: shell
pip freeze
pip install -r requirements.txt
Installing it this way will automatically import and setup all of the dependencies required to run JASS.
**6. Installing JASS for development purpose**
This is pretty much all you need to do to use JASS on the command line, or to run a local personal web server. To deploy JASS on a public web server, please refer to the "Public server deployment" section.
.. code-block:: shell
Additional software installation on Linux
pip install -e . ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**7. Installation verification** Some python packages require additional non-python software that you might need to install, e.g. on Ubuntu, with:
.. code-block:: shell .. code-block:: shell
pip freeze sudo apt install libfreetype6-dev #(required by matplotlib)
sudo apt install libhdf5-dev #(required by tables)
Installing it this way will automatically import and setup all of the dependencies required to run JASS. sudo apt install rabbitmq-server #(required by celery)
This is pretty much all you need to do to use JASS on the command line, or to run a local personal web server. To deploy JASS on a public web server, please refer to the "Public server deployment" section. Additional software installation on Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Additional software installation on Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. tip:: For Windows, an easy option is to install the free software package manager called chocolatey.
Some python packages require additional non-python software that you might need to install, e.g. on Ubuntu, with: In order to have a correct installation of RabbitMQ on windows, we recommend to install chocolatey by running the following command as Administrator in a powershell:
.. code-block:: shell .. code-block:: shell
sudo apt install libfreetype6-dev #(required by matplotlib) Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
sudo apt install libhdf5-dev #(required by tables)
sudo apt install rabbitmq-server #(required by celery) Then, you can install RabbitMQ with the "choco install" command:
Additional software installation on Windows .. code-block:: shell
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Choco install rabbitmq
.. tip:: For Windows, an easy option is to install the free software package manager called chocolatey.
Run JASS as a web application (optional)
In order to have a correct installation of RabbitMQ on windows, we recommend to install chocolatey by running the following command as Administrator in a powershell: ----------------------------------------
.. code-block:: shell To run locally JASS as a web application, it is recommended to use the docker-compose as it handles all dependencies in one command.
If using docker-compose is not an option, you need to launch two servers in two different processes, the `celery` task management server and the web server. The web server handles the HTTP requests, and sends all computation requests to the task management server.
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Launching with docker compose (recommended)
Then, you can install RabbitMQ with the "choco install" command: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once docker and docker-compose installed, just run
.. code-block:: shell
.. code-block:: shell
Choco install rabbitmq
# build the application
Run JASS as a web application (optional) docker-compose build
---------------------------------------- # launch the whole application
docker-compose up
To run locally JASS as a web application, it is recommended to use the docker-compose as it handles all dependencies in one command. # then visit http://0.0.0.0:3000/
If using docker-compose is not an option, you need to launch two servers in two different processes, the `celery` task management server and the web server. The web server handles the HTTP requests, and sends all computation requests to the task management server.
Launching with docker compose (recommended) launching the two servers on Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once docker and docker-compose installed, just run The command lines below show how to launch the two servers. Please note that you should of course not use this for any use beyond tests and personnal use, we provide further instructions below to deploy JASS on shared/public servers.
.. code-block:: shell .. code-block:: shell
# build the application # launch celery to process tasks
docker-compose build celery -A jass worker
# launch the whole application ## and in ANOTHER TERMINAL
docker-compose up # launch the web server
# then visit http://0.0.0.0:3000/ jass serve
By default, the Jass server will listen to requests on the port 8080 of your machine. You can control the host and port that the JASS standalone webserver listens to through two environment variables, ``JASS_HOST`` and ``JASS_PORT``, that you just have to set before to launch the web server.
launching the two servers on Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Launching the celery server on Windows
The command lines below show how to launch the two servers. Please note that you should of course not use this for any use beyond tests and personnal use, we provide further instructions below to deploy JASS on shared/public servers. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: shell In order to launch the celery server on windows, it is necessary to use the following command in a terminal:
# launch celery to process tasks .. code-block:: shell
celery -A jass worker
## and in ANOTHER TERMINAL celery -A jass worker --pool=solo
# launch the web server
jass serve .. warning::
The command recommended for Linux crashes when it is used on windows due to incorrect recognition of the prefork option on windows by the billiard library.
By default, the Jass server will listen to requests on the port 8080 of your machine. You can control the host and port that the JASS standalone webserver listens to through two environment variables, ``JASS_HOST`` and ``JASS_PORT``, that you just have to set before to launch the web server. The part “--pool=solo” is necessary on windows because this is the only option of celery that works on windows.
Launching the celery server on Windows Launching the web server on Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to launch the celery server on windows, it is necessary to use the following command in a terminal: The web server is launched in another terminal (the same as on Linux):
.. code-block:: shell .. code-block:: shell
celery -A jass worker --pool=solo jass serve
.. warning:: Public server deployment (optional)
The command recommended for Linux crashes when it is used on windows due to incorrect recognition of the prefork option on windows by the billiard library. -----------------------------------
The part “--pool=solo” is necessary on windows because this is the only option of celery that works on windows.
Helm charts are available in the `chart folder of the source repository <https://gitlab.pasteur.fr/statistical-genetics/jass/tree/master/chart>`_. These charts automate the installation and update of the application in a kubernetes cluster.
Launching the web server on Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this specific deployment, the JASS web application is hosted by an NGINX server, served by the uvicorn library. It communicates with a celery container that handles the user-launched tasks. Many other deployment options are of course possible, use whichever suits your infrastructure!
The web server is launched in another terminal (the same as on Linux):
.. code-block:: shell
jass serve
Public server deployment (optional)
-----------------------------------
Helm charts are available in the `chart folder of the source repository <https://gitlab.pasteur.fr/statistical-genetics/jass/tree/master/chart>`_. These charts automate the installation and update of the application in a kubernetes cluster.
In this specific deployment, the JASS web application is hosted by an NGINX server, served by the uvicorn library. It communicates with a celery container that handles the user-launched tasks. Many other deployment options are of course possible, use whichever suits your infrastructure!
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import shutil
import sys import sys
import argparse import argparse
from datetime import timedelta, datetime from datetime import timedelta, datetime
from json import JSONDecodeError
import uvicorn import uvicorn
...@@ -171,10 +173,14 @@ def w_clean_project_data(args): ...@@ -171,10 +173,14 @@ def w_clean_project_data(args):
for proj in get_projects_last_access(): for proj in get_projects_last_access():
print(f"Project {proj['project_id']} was last accessed on {proj['last_access']}, ", end='') print(f"Project {proj['project_id']} was last accessed on {proj['last_access']}, ", end='')
if proj['last_access'] + shift < datetime.now(): if proj['last_access'] + shift < datetime.now():
if load_project(project_id=proj['project_id'], flag_as_visited=False).delete_large_files(): try:
print("removing its large files") if load_project(project_id=proj['project_id'], flag_as_visited=False).delete_large_files():
else: print("removing its large files")
print("large files already removed") else:
print("large files already removed")
except JSONDecodeError as e:
print("removing as meta.json is corrupted (" + str(e) + ")")
shutil.rmtree(proj['path'])
else: else:
print("keeping it") print("keeping it")
......
...@@ -15,7 +15,7 @@ matplotlib.use("AGG") ...@@ -15,7 +15,7 @@ matplotlib.use("AGG")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib import colors from matplotlib import colors
import matplotlib.patches as mpatches import matplotlib.patches as mpatches
from scipy.stats import norm from scipy.stats import norm, chi2
import os import os
from pandas import DataFrame, read_hdf from pandas import DataFrame, read_hdf
...@@ -82,7 +82,6 @@ def create_global_plot(work_file_path: str, global_plot_path: str): ...@@ -82,7 +82,6 @@ def create_global_plot(work_file_path: str, global_plot_path: str):
ax.set_xticks(lab_pos) ax.set_xticks(lab_pos)
ax.set_xticklabels(label, rotation =90) ax.set_xticklabels(label, rotation =90)
ax.set_xlabel("", fontsize=16) ax.set_xlabel("", fontsize=16)
ax.set_ylabel("-log10(p-value)", fontsize=16) ax.set_ylabel("-log10(p-value)", fontsize=16)
# add line for significant SNPs # add line for significant SNPs
...@@ -106,23 +105,14 @@ def create_local_plot(work_file_path: str, local_plot_path: str): ...@@ -106,23 +105,14 @@ def create_local_plot(work_file_path: str, local_plot_path: str):
colors = ['blue', 'red'] colors = ['blue', 'red']
df = read_hdf(work_file_path, "SumStatTab") df = read_hdf(work_file_path, "SumStatTab")
chr = int(df["CHR"].iloc[0]) chr = int(df["CHR"].iloc[0])
Num_color = chr % 2 Num_color = chr % 2
df['JASS_PVAL'] = replaceZeroes(df['JASS_PVAL']) df['JASS_PVAL'] = replaceZeroes(df['JASS_PVAL'])
df["-log10(Joint p-value)"] = -np.log10(df.JASS_PVAL) df["-log10(Joint p-value)"] = -np.log10(df.JASS_PVAL)
Columns_to_keep = ["position", "-log10(Joint p-value)"] Columns_to_keep = ["position", "-log10(Joint p-value)"]
df = df[Columns_to_keep]
Columns_to_delete = []
for col in df.columns:
if col not in Columns_to_keep:
Columns_to_delete.append(col)
df.drop(Columns_to_delete, axis=1, inplace=True)
df.sort_values(by="position", inplace=True) df.sort_values(by="position", inplace=True)
Nb_values = df.shape[0] Nb_values = df.shape[0]
...@@ -261,28 +251,31 @@ def create_quadrant_plot(work_file_path: str, ...@@ -261,28 +251,31 @@ def create_quadrant_plot(work_file_path: str,
def create_qq_plot(work_file_path: str, qq_plot_path: str): def create_qq_plot(work_file_path: str, qq_plot_path: str):
df = read_hdf(work_file_path, "SumStatTab") df = read_hdf(work_file_path, "SumStatTab", columns=['JASS_PVAL', 'UNIVARIATE_MIN_PVAL'])
df[['JASS_PVAL', 'UNIVARIATE_MIN_PVAL']] = replaceZeroes( df[['JASS_PVAL', 'UNIVARIATE_MIN_PVAL']] = replaceZeroes(
df[['JASS_PVAL', 'UNIVARIATE_MIN_PVAL']]) df[['JASS_PVAL', 'UNIVARIATE_MIN_PVAL']])
pvalue = -np.log10(df.JASS_PVAL) pvalue = -np.log10(df.JASS_PVAL)
# Cast values between 0 and 1, 0 and 1 excluded pvalue_univ = -np.log10(df.UNIVARIATE_MIN_PVAL)
x = -np.log10(np.arange(1, pvalue.shape[0] + 1) / (pvalue.shape[0] + 2)) # compute_expected pvalue
y = pvalue.sort_values()
plt.scatter(x[::-1], y, s=5) QQ_pval = pd.DataFrame(index =-np.log10(np.arange(1, pvalue.shape[0] + 1) / (pvalue.shape[0] + 2)),
pval_median = np.nanmedian(y) {"JASS pvalue" : pvalue.sort_values(),
lambda_value = pval_median / np.median(x) "UNIV pvalue" : pvalue_univ.sort_values()})
x_1 = np.linspace(0, 6)
y_1 = lambda_value * x_1 pval_median = df.JASS_PVAL.median()
x_2 = np.linspace(0, 6) pval_median_univ = df.UNIVARIATE_MIN_PVAL.median()
plt.plot(x_1, y_1, c="red") print("median pval")
plt.plot(x_2, x_2) print(pval_median)
plt.title("median p-val = {:.3f}".format(pval_median)) lambda_value_jass = chi.sf(pval_median) / chi.sf(0.5)
plt.xlabel("expected quantile of -log10(P)") lambda_value_univ = chi.sf(pval_median_univ) / chi.sf(0.5)
plt.ylabel("observed quantile of -log10(P)")
p = sns.lineplot(data=QQ_pval)
plt.savefig(qq_plot_path, dpi=600) p.set("QQ plot")
p.set_xlabel("Expected p-values", fontsize = 20)
p.set_ylabel("Observed p-values", fontsize = 20)
plt.savefig(qq_plot_path)
print("------ QQ plot -----") print("------ QQ plot -----")
def create_qq_plot_by_GWAS(init_file_path: str, qq_plot_folder: str): def create_qq_plot_by_GWAS(init_file_path: str, qq_plot_folder: str):
......
...@@ -6,6 +6,7 @@ from __future__ import absolute_import ...@@ -6,6 +6,7 @@ from __future__ import absolute_import
import abc import abc
import shutil import shutil
from datetime import datetime from datetime import datetime
from json import JSONDecodeError
from pathlib import Path from pathlib import Path
from celery import states from celery import states
import json import json
...@@ -123,8 +124,13 @@ class Project(BaseModel, abc.ABC): ...@@ -123,8 +124,13 @@ class Project(BaseModel, abc.ABC):
if fail_if_exists: if fail_if_exists:
raise e raise e
if os.path.exists(self.get_meta_file_path()): if os.path.exists(self.get_meta_file_path()):
return False try:
# The directory exists, but not meta.json, we clean up the directory, in case of. load_project(self.id)
return False
except JSONDecodeError as e:
print(f"Project {self.id} corrupted ({str(e)}). Removing it, before trying to re-create it.")
pass
# The directory exists, but meta.json don't or is corrupted, we clean up the directory, in case of.
shutil.rmtree(self.get_folder_path()) shutil.rmtree(self.get_folder_path())
return self.create(fail_if_exists=fail_if_exists) return self.create(fail_if_exists=fail_if_exists)
...@@ -159,7 +165,7 @@ class Project(BaseModel, abc.ABC): ...@@ -159,7 +165,7 @@ class Project(BaseModel, abc.ABC):
def update_progress(self, progress): def update_progress(self, progress):
self.progress = progress self.progress = progress
self.save() self.save()
print("PROGRESS {0}".format(self.progress)) print(f"PROGRESS for {self.id}: {self.progress}")
def get_folder_path(self): def get_folder_path(self):
""" """
...@@ -212,6 +218,12 @@ class Project(BaseModel, abc.ABC): ...@@ -212,6 +218,12 @@ class Project(BaseModel, abc.ABC):
def get_project_summary_statistics(self): def get_project_summary_statistics(self):
return get_worktable_summary(self.get_worktable_path()) return get_worktable_summary(self.get_worktable_path())
def get_metadata_file_path(self):
"""
Get the path of the metadata json file
"""
return os.path.join(self.get_folder_path(), "metadata.json")
def get_project_nsnps(self): def get_project_nsnps(self):
return get_inittable_meta(self.get_worktable_path()) return get_inittable_meta(self.get_worktable_path())
...@@ -250,11 +262,7 @@ class Project(BaseModel, abc.ABC): ...@@ -250,11 +262,7 @@ class Project(BaseModel, abc.ABC):
os.remove(project_hdf_path) os.remove(project_hdf_path)
# as a consequence, its creation will have to be redone, so set progress to 0 and remove is status. # as a consequence, its creation will have to be redone, so set progress to 0 and remove is status.
self.progress = 0 self.progress = 0
try: self.status.clear()
del self.status['worktable']
except KeyError:
# worktable not in status
pass
self.save() self.save()
if self.delayed_gen_csv_file: if self.delayed_gen_csv_file:
...@@ -264,6 +272,15 @@ class Project(BaseModel, abc.ABC): ...@@ -264,6 +272,15 @@ class Project(BaseModel, abc.ABC):
if os.path.exists(csv_path): if os.path.exists(csv_path):
have_removed_file = True have_removed_file = True
os.remove(csv_path) os.remove(csv_path)
for filename in os.listdir(self.get_folder_path()):
if filename.endswith(".png"):
os.remove(os.path.join(self.get_folder_path(), filename))
have_removed_file = True
try:
os.remove(self.get_metadata_file_path())
have_removed_file = True
except FileNotFoundError:
pass
return have_removed_file return have_removed_file
@call_with_tb('global_manhattan') @call_with_tb('global_manhattan')
...@@ -290,6 +307,12 @@ class Project(BaseModel, abc.ABC): ...@@ -290,6 +307,12 @@ class Project(BaseModel, abc.ABC):
def create_csv_file(self): def create_csv_file(self):
return create_genome_full_csv(self.get_worktable_path(), self.get_csv_path()) return create_genome_full_csv(self.get_worktable_path(), self.get_csv_path())
@call_with_tb('metadata')
def create_project_metadata_file(self):
with open(self.get_metadata_file_path(), 'w') as f:
f.write(json.dumps(self.get_project_nsnps()))
print("------ metadata -----")
class GlobalProject(Project): class GlobalProject(Project):
@call_with_tb('worktable') @call_with_tb('worktable')
...@@ -408,5 +431,10 @@ def ensure_space_in_project_dir(*, except_project_id=None): ...@@ -408,5 +431,10 @@ def ensure_space_in_project_dir(*, except_project_id=None):
f'({free} MB<{config["MIN_SIZE_TO_KEEP_IN_PROJECTS_DIR_IN_MB"]} MB), ' f'({free} MB<{config["MIN_SIZE_TO_KEEP_IN_PROJECTS_DIR_IN_MB"]} MB), '
f'removing data from old projects') f'removing data from old projects')
show_space_message = False show_space_message = False
if load_project(proj['project_id'], flag_as_visited=False).delete_large_files(): try:
print(f"Project {proj['project_id']} was last accessed on {proj['last_access']}, data removed.") if load_project(proj['project_id'], flag_as_visited=False).delete_large_files():
print(f"Project {proj['project_id']} was last accessed on {proj['last_access']}, data removed.")
except JSONDecodeError as e:
print(f"Project {proj['project_id']} has a corrupted meta.json ({str(e)}), removing it.")
shutil.rmtree(proj['path'])
...@@ -865,11 +865,18 @@ def get_worktable_gencov(project_hdf_path: str): ...@@ -865,11 +865,18 @@ def get_worktable_gencov(project_hdf_path: str):
for i in range(gencov.shape[0]): for i in range(gencov.shape[0]):
for j in range(i, gencov.shape[0]): for j in range(i, gencov.shape[0]):
lines.append({ if np.isnan(gencov.loc[sorted_trait.index[i], sorted_trait.index[j]]):
"phenotypeID_A": sorted_trait.iloc[i], lines.append({
"phenotypeID_B": sorted_trait.iloc[j], "phenotypeID_A": sorted_trait.iloc[i],
"gencov":gencov.loc[sorted_trait.index[i], sorted_trait.index[j]] "phenotypeID_B": sorted_trait.iloc[j],
}) "gencov":"null"
})
else:
lines.append({
"phenotypeID_A": sorted_trait.iloc[i],
"phenotypeID_B": sorted_trait.iloc[j],
"gencov":gencov.loc[sorted_trait.index[i], sorted_trait.index[j]]
})
print(lines) print(lines)
return lines return lines
...@@ -1062,4 +1069,4 @@ def create_genome_full_csv_lock_file(project_hdf_path): ...@@ -1062,4 +1069,4 @@ def create_genome_full_csv_lock_file(project_hdf_path):
the_lock = "The lock is set on : workTable.csv is not yet available" the_lock = "The lock is set on : workTable.csv is not yet available"
file_lock = open(the_lock_path, "w") file_lock = open(the_lock_path, "w")
file_lock.write(the_lock) file_lock.write(the_lock)
file_lock.close() file_lock.close()
\ No newline at end of file
#!/usr/bin/env python3 #!/usr/bin/env python3
import logging import logging
import os import os
import shutil
from json import JSONDecodeError
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from starlette.responses import RedirectResponse from starlette.responses import RedirectResponse, JSONResponse
from tables import HDF5ExtError
from jass import util from jass import util
from jass.config import config from jass.config import config
from jass.models.phenotype import Phenotype, get_available_phenotypes, PhenotypeIdList from jass.models.phenotype import Phenotype, get_available_phenotypes, PhenotypeIdList
from jass.models.inittable import get_inittable_meta from jass.models.inittable import get_inittable_meta
from jass.models.project import GlobalProject, load_project as project__load_project from jass.models.project import GlobalProject, load_project as project__load_project
from jass.tasks import create_project, run_project_analysis_if_needed from jass.tasks import create_project, run_project_analysis_if_needed, get_queue_status
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from fastapi.responses import Response, FileResponse from fastapi.responses import Response, FileResponse
...@@ -32,11 +35,15 @@ else: ...@@ -32,11 +35,15 @@ else:
logging.info("/webui cannot be served as client/dist is missing, please build the client.") logging.info("/webui cannot be served as client/dist is missing, please build the client.")
def load_project(project_id): def load_project(project_id, *args, **kargs):
try: try:
return project__load_project(project_id=project_id) return project__load_project(project_id=project_id, *args, **kargs)
except FileNotFoundError: except FileNotFoundError:
raise HTTPException(status_code=404, detail="Project not found") raise HTTPException(status_code=404, detail="Project not found")
except JSONDecodeError as e:
print(f"Project {project_id} has a corrupted meta.json ({str(e)}), removing it.")
shutil.rmtree(GlobalProject.get_folder_path_from_id(project_id))
raise HTTPException(status_code=404, detail="Project invalid, and removed")
@app.get("/") @app.get("/")
...@@ -75,6 +82,11 @@ def project_detail(project_id: str): ...@@ -75,6 +82,11 @@ def project_detail(project_id: str):
return load_project(project_id=project_id).progress return load_project(project_id=project_id).progress
@app.get("/api/projects/{project_id}/unload", response_model=bool)
def project_detail(project_id: str):
return load_project(project_id=project_id, flag_as_visited=False).delete_large_files()
@app.get("/api/projects/{project_id}/summary") @app.get("/api/projects/{project_id}/summary")
def project_detail(project_id: str): def project_detail(project_id: str):
return load_project(project_id=project_id).get_project_summary_statistics() return load_project(project_id=project_id).get_project_summary_statistics()
...@@ -82,12 +94,19 @@ def project_detail(project_id: str): ...@@ -82,12 +94,19 @@ def project_detail(project_id: str):
@app.get("/api/projects/{project_id}/gencov") @app.get("/api/projects/{project_id}/gencov")
def project_gencov_view(project_id: str): def project_gencov_view(project_id: str):
return load_project(project_id=project_id).get_project_gencov() try:
return JSONResponse(load_project(project_id=project_id).get_project_gencov())
except ValueError as e: # NaN in values
raise HTTPException(status_code=500, detail=str(e))
except HDF5ExtError as e: # file corrupted
raise HTTPException(status_code=500, detail=str(e))
except Exception as e: # anything else
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/projects/{project_id}/metadata") @app.get("/api/projects/{project_id}/metadata")
def project_metadata(project_id: str): def project_metadata(project_id: str):
return load_project(project_id=project_id).get_project_nsnps() return FileResponse(load_project(project_id=project_id).get_metadata_file_path())
@app.get("/api/projects/{project_id}/globalmanhattan") @app.get("/api/projects/{project_id}/globalmanhattan")
...@@ -106,6 +125,14 @@ def project_get_quadrant(project_id: str): ...@@ -106,6 +125,14 @@ def project_get_quadrant(project_id: str):
) )
@app.get("/api/projects/{project_id}/qqplot")
def project_get_qq_plot(project_id: str):
return FileResponse(
load_project(project_id=project_id).get_qq_plot_path(),
media_type="image/png",
)
@app.get("/api/projects/{project_id}/genome_full") @app.get("/api/projects/{project_id}/genome_full")
def get_full_sumstat(project_id: str): def get_full_sumstat(project_id: str):
project = load_project(project_id=project_id) project = load_project(project_id=project_id)
...@@ -115,6 +142,7 @@ def get_full_sumstat(project_id: str): ...@@ -115,6 +142,7 @@ def get_full_sumstat(project_id: str):
print("CREATED CSV FILE") print("CREATED CSV FILE")
return FileResponse( return FileResponse(
project.get_csv_path(), project.get_csv_path(),
filename=f"genome_full_{project_id}.csv",
media_type="text/csv", media_type="text/csv",
) )
...@@ -124,6 +152,7 @@ def get_data_manhattan(project_id: str): ...@@ -124,6 +152,7 @@ def get_data_manhattan(project_id: str):
return Response( return Response(
content=load_project(project_id=project_id).get_project_genomedata(), content=load_project(project_id=project_id).get_project_genomedata(),
media_type="text/csv", media_type="text/csv",
headers={"content-disposition": f"attachment; filename=genome_{project_id}.csv"},
) )
...@@ -153,6 +182,11 @@ def get_manhattan(project_id: str, selected_chr: str, selected_region: str): ...@@ -153,6 +182,11 @@ def get_manhattan(project_id: str, selected_chr: str, selected_region: str):
) )
@app.get("/api/queue_status")
def queue_status():
return JSONResponse(get_queue_status())
# @blp_inittable.route("") # @blp_inittable.route("")
......