├── .babelrc ├── .coveragerc ├── .coveralls.yml ├── .dockerignore ├── .eslintrc.yml ├── .github └── workflows │ ├── github-publish-celery-flaskomics.yml │ ├── github-publish-flaskomics.yml │ └── lint_test.yml ├── .gitignore ├── .readthedocs.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── Pipfile ├── Pipfile.lock ├── README.md ├── app.py ├── askomics.png ├── askomics ├── __init__.py ├── api │ ├── __init__.py │ ├── admin.py │ ├── auth.py │ ├── catch_url.py │ ├── data.py │ ├── datasets.py │ ├── file.py │ ├── galaxy.py │ ├── ontology.py │ ├── query.py │ ├── results.py │ ├── sparql.py │ ├── start.py │ └── view.py ├── app.py ├── libaskomics │ ├── BedFile.py │ ├── CsvFile.py │ ├── Database.py │ ├── Dataset.py │ ├── DatasetsHandler.py │ ├── File.py │ ├── FilesHandler.py │ ├── FilesUtils.py │ ├── Galaxy.py │ ├── GffFile.py │ ├── LdapAuth.py │ ├── LocalAuth.py │ ├── Mailer.py │ ├── OntologyManager.py │ ├── Params.py │ ├── PrefixManager.py │ ├── RdfFile.py │ ├── RdfGraph.py │ ├── Result.py │ ├── ResultsHandler.py │ ├── SparqlQuery.py │ ├── SparqlQueryLauncher.py │ ├── Start.py │ ├── TriplestoreExplorer.py │ ├── Utils.py │ ├── __init__.py │ └── prefix.cc.json ├── middleware.py ├── react │ └── src │ │ ├── classes │ │ └── utils.jsx │ │ ├── components │ │ ├── autocomplete.jsx │ │ ├── template.jsx │ │ └── waiting.jsx │ │ ├── contact.jsx │ │ ├── footer.jsx │ │ ├── index.jsx │ │ ├── navbar.jsx │ │ ├── routes.jsx │ │ └── routes │ │ ├── about │ │ └── about.jsx │ │ ├── account │ │ ├── account.jsx │ │ ├── delete_account.jsx │ │ ├── update_apikey.jsx │ │ ├── update_galaxy.jsx │ │ ├── update_password.jsx │ │ └── update_profile.jsx │ │ ├── admin │ │ ├── admin.jsx │ │ ├── datasetstable.jsx │ │ ├── filestable.jsx │ │ ├── ontologies.jsx │ │ ├── prefixes.jsx │ │ ├── queriestable.jsx │ │ └── userstable.jsx │ │ ├── ask │ │ └── ask.jsx │ │ ├── data │ │ └── data.jsx │ │ ├── datasets │ │ ├── datasets.jsx │ │ └── datasetstable.jsx │ │ ├── error │ │ └── error.jsx │ │ ├── form │ │ ├── attribute.jsx │ │ ├── entity.jsx │ │ └── query.jsx │ │ ├── form_edit │ │ ├── attribute.jsx │ │ ├── entity.jsx │ │ └── query.jsx │ │ ├── integration │ │ ├── advancedoptions.jsx │ │ ├── bedpreview.jsx │ │ ├── csvtable.jsx │ │ ├── gffpreview.jsx │ │ ├── integration.jsx │ │ └── rdfpreview.jsx │ │ ├── login │ │ ├── login.jsx │ │ ├── logout.jsx │ │ ├── passwordreset.jsx │ │ └── signup.jsx │ │ ├── overview │ │ └── overview.jsx │ │ ├── query │ │ ├── attribute.jsx │ │ ├── graphfilters.js │ │ ├── linkview.jsx │ │ ├── ontolinkview.jsx │ │ ├── overviewModal.jsx │ │ ├── query.jsx │ │ └── visualization.jsx │ │ ├── results │ │ ├── results.jsx │ │ └── resultsfilestable.jsx │ │ ├── sparql │ │ ├── advancedsparql.jsx │ │ ├── resultstable.jsx │ │ └── sparql.jsx │ │ └── upload │ │ ├── filestable.jsx │ │ ├── upload.jsx │ │ ├── uploadform.jsx │ │ ├── uploadgalaxyform.jsx │ │ ├── uploadmodal.jsx │ │ └── uploadurlform.jsx ├── static │ ├── about.html │ ├── css │ │ └── askomics.css │ ├── favicon.png │ └── welcome.html ├── tasks.py └── templates │ └── index.html ├── cli ├── add_user.py ├── clear_cache.py ├── clear_cache.sh ├── config_updater.py ├── set_config.sh ├── set_user.sh ├── update_base_url.py ├── update_base_url.sh └── upload_files.py ├── config ├── askomics.ini.template └── askomics.test.ini ├── docker ├── Dockerfile ├── DockerfileAll ├── DockerfileCelery └── start_all.sh ├── docs ├── abstraction-overview.md ├── abstraction.md ├── ci.md ├── cli.md ├── configure.md ├── console.md ├── contribute.md ├── data.md ├── dev-deployment.md ├── docs.md ├── federation.md ├── galaxy.md ├── img │ ├── abstraction_2d.png │ ├── abstraction_3d.png │ ├── account_button.png │ ├── askogalaxy.png │ ├── askograph.png │ ├── attribute_box.png │ ├── attributes.png │ ├── complex_query.png │ ├── csv_convert.png │ ├── custom_nodes.png │ ├── datasets.png │ ├── de_results_preview.png │ ├── external_startpoint.png │ ├── faldo.png │ ├── files_table.png │ ├── filters.png │ ├── form.png │ ├── form_edit.png │ ├── form_example.png │ ├── galaxy_askomics_files.png │ ├── galaxy_execute_it.png │ ├── galaxy_history_result.png │ ├── galaxy_import_from_galaxy.png │ ├── galaxy_input_data.png │ ├── galaxy_search_tool.png │ ├── gff.png │ ├── gff_preview.png │ ├── integrate_external.png │ ├── linked_query.png │ ├── login_button.png │ ├── minus.png │ ├── name_attr.png │ ├── navbar.png │ ├── navbar_files.png │ ├── num_attr.png │ ├── ontology_autocomplete.png │ ├── ontology_graph.png │ ├── ontology_integration.png │ ├── ontology_link.png │ ├── preview.png │ ├── preview_results.png │ ├── qtl_preview.png │ ├── query.png │ ├── query_builder.png │ ├── results_table.png │ ├── sparql.png │ ├── startpoint.png │ ├── symbol_to_ensembl_preview.png │ ├── template.png │ ├── tsv.png │ ├── union.png │ ├── union_duplicated.png │ └── uri_label_attr.png ├── index.md ├── manage.md ├── ontologies.md ├── prefixes.md ├── production-deployment.md ├── query.md ├── requirements.txt ├── results.md ├── style.css ├── template.md └── tutorial.md ├── mkdocs.yml ├── package-lock.json ├── package.json ├── requirements.txt ├── setup.py ├── test-data ├── abstraction.nt ├── abstraction.ttl ├── abstraction.xml ├── agro_min.ttl ├── de.tsv ├── gene.bed ├── gene.gff3 ├── gene.tsv ├── linked_uris.csv ├── malformed.tsv ├── qtl.tsv ├── transcripts.tsv ├── transcripts_chunk1.tsv ├── transcripts_chunk2.tsv ├── transcripts_chunk3.tsv └── uris.csv ├── tests ├── __init__.py ├── conftest.py ├── data │ ├── graphState_filtered_query.json │ ├── graphState_simple_query.json │ ├── graphState_simple_query_form.json │ ├── graphState_simple_query_form_modified.json │ ├── graphstate.json │ ├── linked_uri_query.json │ ├── query.json │ ├── startpoints.json │ └── uri_query.json ├── results │ ├── abstraction.json │ ├── data_full.json │ ├── data_public.json │ ├── graphstate.json │ ├── init.json │ ├── preview.json │ ├── preview_files.json │ ├── preview_malformed_files.json │ ├── query.sparql │ ├── result.csv │ ├── results.json │ ├── results_admin.json │ ├── results_form.json │ ├── results_linked_uri.json │ ├── results_uri.json │ ├── sparql_and_graph.json │ ├── sparql_preview.json │ ├── sparql_query.json │ ├── startpoints.json │ └── test_query.json ├── test_api.py ├── test_api_admin.py ├── test_api_auth.py ├── test_api_data.py ├── test_api_datasets.py ├── test_api_file.py ├── test_api_galaxy.py ├── test_api_ontology.py ├── test_api_query.py ├── test_api_results.py ├── test_api_sparql.py ├── test_cleanup.py └── test_uri.py └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = venv/* 3 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: 5OBT9Dq2n5pQfhOV9gJNu8THW8O1qUxdg -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | askomics/static/js/askomics.js 2 | askomics.egg-info 3 | askomics.png 4 | docs 5 | LICENSE 6 | mkdocs.yml 7 | node_modules 8 | __pycache__ 9 | README.md 10 | test-data 11 | tests 12 | venv 13 | .coveralls.yml 14 | .dockerignore 15 | .eslintrc.yml 16 | .git 17 | .gitignore 18 | .pytest_cache 19 | .readthedocs.yml 20 | .travis.yml 21 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true 3 | es6: true 4 | extends: 'plugin:react/recommended' 5 | globals: 6 | Atomics: readonly 7 | SharedArrayBuffer: readonly 8 | parserOptions: 9 | ecmaFeatures: 10 | jsx: true 11 | ecmaVersion: 2018 12 | sourceType: module 13 | plugins: 14 | - react 15 | rules: {} 16 | settings: 17 | react: 18 | version: detect 19 | parser: babel-eslint -------------------------------------------------------------------------------- /.github/workflows/github-publish-celery-flaskomics.yml: -------------------------------------------------------------------------------- 1 | name: Publish Flaskomics Docker image to github 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | - 'dev' 8 | tags: 9 | - '*.*.*' 10 | 11 | env: 12 | REGISTRY: ghcr.io 13 | IMAGE_NAME: celery-flaskomics 14 | 15 | jobs: 16 | build-and-push-image: 17 | if: github.repository == 'askomics/flaskomics' 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | - 25 | name: Checkout repository 26 | uses: actions/checkout@v3 27 | 28 | - 29 | name: Login to registry 30 | uses: docker/login-action@v1 31 | with: 32 | registry: ${{ env.REGISTRY }} 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - 37 | name: Docker meta 38 | id: meta 39 | uses: docker/metadata-action@v3 40 | with: 41 | # list of Docker images to use as base name for tags 42 | images: ${{ env.REGISTRY }}/askomics/${{ env.IMAGE_NAME }} 43 | # generate Docker tags based on the following events/attributes 44 | tags: | 45 | type=ref,event=branch 46 | type=ref,event=pr 47 | type=semver,pattern={{version}} 48 | type=semver,pattern={{major}}.{{minor}} 49 | type=semver,pattern={{major}} 50 | 51 | - 52 | name: Set up QEMU 53 | uses: docker/setup-qemu-action@v1 54 | 55 | - 56 | name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@v1 58 | 59 | - 60 | name: Build and push 61 | uses: docker/build-push-action@v2 62 | with: 63 | context: . 64 | file: ./docker/DockerfileCelery 65 | push: true 66 | tags: ${{ steps.meta.outputs.tags }} 67 | labels: ${{ steps.meta.outputs.labels }} 68 | -------------------------------------------------------------------------------- /.github/workflows/github-publish-flaskomics.yml: -------------------------------------------------------------------------------- 1 | name: Publish Flaskomics Docker image to github 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | - 'dev' 8 | tags: 9 | - '*.*.*' 10 | 11 | env: 12 | REGISTRY: ghcr.io 13 | IMAGE_NAME: flaskomics 14 | 15 | jobs: 16 | build-and-push-image: 17 | if: github.repository == 'askomics/flaskomics' 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | - 25 | name: Checkout repository 26 | uses: actions/checkout@v3 27 | 28 | - 29 | name: Login to registry 30 | uses: docker/login-action@v1 31 | with: 32 | registry: ${{ env.REGISTRY }} 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - 37 | name: Docker meta 38 | id: meta 39 | uses: docker/metadata-action@v3 40 | with: 41 | # list of Docker images to use as base name for tags 42 | images: ${{ env.REGISTRY }}/askomics/${{ env.IMAGE_NAME }} 43 | # generate Docker tags based on the following events/attributes 44 | tags: | 45 | type=ref,event=branch 46 | type=ref,event=pr 47 | type=semver,pattern={{version}} 48 | type=semver,pattern={{major}}.{{minor}} 49 | type=semver,pattern={{major}} 50 | 51 | - 52 | name: Set up QEMU 53 | uses: docker/setup-qemu-action@v1 54 | 55 | - 56 | name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@v1 58 | 59 | - 60 | name: Build and push 61 | uses: docker/build-push-action@v2 62 | with: 63 | context: . 64 | file: ./docker/Dockerfile 65 | push: true 66 | tags: ${{ steps.meta.outputs.tags }} 67 | labels: ${{ steps.meta.outputs.labels }} 68 | -------------------------------------------------------------------------------- /.github/workflows/lint_test.yml: -------------------------------------------------------------------------------- 1 | name: Lint and test 2 | on: ["push", "pull_request"] 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout 8 | uses: actions/checkout@v2 9 | - uses: actions/setup-python@v1 10 | with: 11 | python-version: 3.11 12 | - name: Install flake8 13 | run: pip install flake8 14 | - name: Flake8 15 | run: flake8 askomics tests cli --ignore=E501,W504 16 | 17 | node_test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions/setup-node@v2-beta 22 | with: 23 | node-version: '10' 24 | - name: Install modules 25 | run: make install-js MODE=dev 26 | - name: Run ESLint 27 | run: make eslint MODE=dev TRAVIS=true 28 | 29 | py_test: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v2 34 | - uses: actions/setup-python@v1 35 | with: 36 | python-version: 3.11 37 | - name: Update apt cache 38 | run: sudo apt-get update 39 | - name: Install python-ldap deps 40 | run: sudo apt-get install libldap2-dev libsasl2-dev 41 | - name: Install python dev deps 42 | run: pip install ephemeris coveralls 43 | - name: Install deps 44 | run: make install-python MODE=dev 45 | - name: Launch containers 46 | run: | 47 | docker pull redis:4.0 48 | docker pull askomics/virtuoso:7.2.5.1 49 | docker pull bgruening/galaxy-stable:20.05 50 | docker pull xgaia/corese:latest 51 | docker pull xgaia/isql-api:2.1.1 52 | docker pull xgaia/simple-ldap:latest 53 | docker run -d --name virtuoso -p 8891:8890 -p 1112:1111 -e DBA_PASSWORD=dba -e DEFAULT_GRAPH=http://localhost:8891/DAV -t askomics/virtuoso:7.2.5.1 /bin/sh -c "netstat -nr | grep '^0\.0\.0\.0' | grep -oE '((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])' | grep -v '^0\.0\.0\.0' | sed 's/$/ askomics-host/' >> /etc/hosts && /virtuoso/virtuoso.sh" 54 | sleep 1m 55 | docker run -d --name redis -p 6380:6379 -t redis:4.0 56 | docker run -d --name galaxy -p 8081:80 -t bgruening/galaxy-stable:20.05 57 | docker run -d --name corese -p 8082:8080 -t xgaia/corese:latest /bin/sh -c "netstat -nr | grep '^0\.0\.0\.0' | grep -oE '((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])' | grep -v '^0\.0\.0\.0' | sed 's/$/ askomics-host/' >> /etc/hosts && /corese/start.sh" 58 | docker run -d --name isql-api -p 5051:5050 -e VIRTUOSO_HOST=askomics-host -e VIRTUOSO_ISQL_PORT=1112 -t xgaia/isql-api:2.1.1 /bin/sh -c "netstat -nr | grep '^0\.0\.0\.0' | grep -oE '((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])' | grep -v '^0\.0\.0\.0' | sed 's/$/ askomics-host/' >> /etc/hosts && sh /isqlapi/docker-run.sh" 59 | docker run -d --name ldap -p 8389:389 -e ORGANISATION_NAME=AskOmics -e SUFFIX='dc=askomics,dc=org' -e ROOT_USER=admin -e ROOT_PW_CLEAR=admin -e FIRST_USER=true -e USER_UID=jwick -e USER_GIVEN_NAME=John -e USER_SURNAME=Wick -e USER_EMAIL=john.wick@askomics.org -e USER_PW_CLEAR=jwick -t xgaia/simple-ldap:latest 60 | galaxy-wait -g http://localhost:8081 --timeout 900 61 | echo "Galaxy is online, waiting a bit more for admin user creation" 62 | sleep 1m 63 | - name: Run tests 64 | run: | 65 | make pytest MODE=dev TRAVIS=true 66 | - name: Coveralls 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | run: | 70 | coveralls 71 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | 13 | # Build documentation with MkDocs 14 | mkdocs: 15 | configuration: mkdocs.yml 16 | 17 | # Optionally build your docs in additional formats such as PDF and ePub 18 | formats: all 19 | 20 | # Optionally set the version of Python and requirements required to build your docs 21 | python: 22 | install: 23 | - requirements: docs/requirements.txt 24 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | werkzeug = "*" 8 | flask = "<3" 9 | validate-email = "*" 10 | gunicorn = "*" 11 | python-magic = "*" 12 | rdflib = "*" 13 | sparqlwrapper = "*" 14 | requests = "*" 15 | celery = "*" 16 | redis = "*" 17 | watchdog = "*" 18 | gitpython = "*" 19 | biopython = "==1.81" 20 | bcbio-gff = "==0.7.0" 21 | bioblend = "*" 22 | pysam = "*" 23 | pybedtools = "*" 24 | deepdiff = "*" 25 | sentry-sdk = {extras = ["flask"], version = "*"} 26 | configparser = "*" 27 | tld = "*" 28 | argh = "*" 29 | python-ldap = "*" 30 | python-dateutil = "*" 31 | markupsafe = "*" 32 | 33 | [dev-packages] 34 | pytest = "*" 35 | pytest-cov ="*" 36 | coveralls = "*" 37 | flake8 = "*" 38 | mkdocs = "*" 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AskOmics 2 | 3 | [![Lint and test](https://github.com/askomics/flaskomics/workflows/Lint%20and%20test/badge.svg)](https://github.com/askomics/flaskomics/actions?query=workflow%3A%22Lint+and+test%22) 4 | [![Coverage Status](https://coveralls.io/repos/github/askomics/flaskomics/badge.svg?branch=master)](https://coveralls.io/github/askomics/flaskomics?branch=master) 5 | [![Docker Build](https://img.shields.io/docker/pulls/askomics/flaskomics.svg)](https://hub.docker.com/r/askomics/flaskomics/) 6 | [![Documentation Status](https://readthedocs.org/projects/flaskomics/badge/?version=latest)](https://flaskomics.readthedocs.io/en/latest/?badge=latest) 7 | 8 | Rebuild of [AskOmics](https://github.com/askomics/askomics) 9 | 10 | ![AskOmics logo](askomics.png) 11 | 12 | AskOmics is a visual SPARQL query interface supporting both intuitive data integration and querying while shielding the user from most of the technical difficulties underlying RDF and SPARQL. 13 | 14 | 15 | ## Documentation 16 | 17 | All documentation, included installation instruction is [here](https://flaskomics.readthedocs.io/en/latest/) 18 | A Galaxy Training tutorial is available [here](https://training.galaxyproject.org/training-material/topics/transcriptomics/tutorials/rna-seq-analysis-with-askomics-it/tutorial.html) 19 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from askomics.app import create_app, create_celery 2 | 3 | application = create_app(config='config/askomics.ini') 4 | celery = create_celery(application) 5 | 6 | if __name__ == '__main__': 7 | application.run() 8 | -------------------------------------------------------------------------------- /askomics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askomics/flaskomics/2c10eef9e34305844b6afdd37a4e508d6ae5a294/askomics.png -------------------------------------------------------------------------------- /askomics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askomics/flaskomics/2c10eef9e34305844b6afdd37a4e508d6ae5a294/askomics/__init__.py -------------------------------------------------------------------------------- /askomics/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askomics/flaskomics/2c10eef9e34305844b6afdd37a4e508d6ae5a294/askomics/api/__init__.py -------------------------------------------------------------------------------- /askomics/api/catch_url.py: -------------------------------------------------------------------------------- 1 | """Catch_all route 2 | """ 3 | from flask import Blueprint, redirect 4 | 5 | catch_url_bp = Blueprint('catch_url', __name__, url_prefix='/') 6 | 7 | 8 | @catch_url_bp.route('/') 9 | def catch_all(path): 10 | """Return all routes to home 11 | 12 | Parameters 13 | ---------- 14 | path : str 15 | Original path 16 | 17 | Returns 18 | ------- 19 | redirect 20 | Redirect to route / 21 | """ 22 | 23 | return redirect('/?path={}'.format(path)) 24 | -------------------------------------------------------------------------------- /askomics/api/data.py: -------------------------------------------------------------------------------- 1 | """Api routes""" 2 | import urllib.parse 3 | import sys 4 | import traceback 5 | 6 | from askomics.api.auth import api_auth 7 | from askomics.libaskomics.SparqlQuery import SparqlQuery 8 | 9 | from flask import (Blueprint, current_app, jsonify, session) 10 | 11 | 12 | data_bp = Blueprint('data', __name__, url_prefix='/') 13 | 14 | 15 | @data_bp.route('/api/data/', methods=['GET']) 16 | @api_auth 17 | def get_data(uri): 18 | """Get information about uri 19 | 20 | Returns 21 | ------- 22 | json 23 | error: True if error, else False 24 | errorMessage: the error message of error, else an empty string 25 | """ 26 | 27 | try: 28 | query = SparqlQuery(current_app, session, get_graphs=True) 29 | graphs, endpoints = query.get_graphs_and_endpoints(all_selected=True) 30 | 31 | endpoints = [val['uri'] for val in endpoints.values()] 32 | 33 | data = [] 34 | 35 | # If the user do not have access to any endpoint (no viewable graph), skip 36 | if endpoints: 37 | 38 | uri = urllib.parse.quote(uri) 39 | base_uri = current_app.iniconfig.get('triplestore', 'namespace_data') 40 | full_uri = "<%s%s>" % (base_uri, uri) 41 | 42 | data = query.get_uri_parameters(full_uri, endpoints) 43 | 44 | except Exception as e: 45 | current_app.logger.error(str(e).replace('\\n', '\n')) 46 | traceback.print_exc(file=sys.stdout) 47 | return jsonify({ 48 | 'error': True, 49 | 'errorMessage': str(e).replace('\\n', '\n'), 50 | 'data': [] 51 | }), 500 52 | 53 | return jsonify({ 54 | 'data': data, 55 | 'error': False, 56 | 'errorMessage': "" 57 | }) 58 | -------------------------------------------------------------------------------- /askomics/api/ontology.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | import sys 3 | 4 | from askomics.api.auth import api_auth 5 | from askomics.libaskomics.OntologyManager import OntologyManager 6 | 7 | from flask import (Blueprint, current_app, jsonify, request, session) 8 | 9 | onto_bp = Blueprint('ontology', __name__, url_prefix='/') 10 | 11 | 12 | @onto_bp.route("/api/ontology//autocomplete", methods=["GET"]) 13 | @api_auth 14 | def autocomplete(short_ontology): 15 | """Get the default sparql query 16 | 17 | Returns 18 | ------- 19 | json 20 | """ 21 | 22 | if "user" not in session and current_app.iniconfig.getboolean("askomics", "protect_public"): 23 | return jsonify({ 24 | "error": True, 25 | "errorMessage": "Ontology {} not found".format(short_ontology), 26 | "results": [] 27 | }), 401 28 | try: 29 | om = OntologyManager(current_app, session) 30 | ontology = om.get_ontology(short_name=short_ontology) 31 | if not ontology: 32 | return jsonify({ 33 | "error": True, 34 | "errorMessage": "Ontology {} not found".format(short_ontology), 35 | "results": [] 36 | }), 404 37 | 38 | if ontology['type'] == "none": 39 | return jsonify({ 40 | "error": True, 41 | "errorMessage": "Ontology {} does not have autocompletion".format(short_ontology), 42 | "results": [] 43 | }), 404 44 | 45 | results = om.autocomplete(ontology["uri"], ontology["type"], request.args.get("q"), short_ontology, ontology["graph"], ontology["endpoint"], ontology['label_uri'], ontology['remote_graph']) 46 | 47 | except Exception as e: 48 | traceback.print_exc(file=sys.stdout) 49 | return jsonify({ 50 | "error": True, 51 | "errorMessage": str(e), 52 | "results": [] 53 | }), 500 54 | 55 | return jsonify({ 56 | "error": False, 57 | "errorMessage": "", 58 | "results": results 59 | }), 200 60 | -------------------------------------------------------------------------------- /askomics/api/view.py: -------------------------------------------------------------------------------- 1 | """Render route""" 2 | from flask import (Blueprint, render_template, current_app) 3 | 4 | 5 | view_bp = Blueprint('view', __name__, url_prefix='/') 6 | 7 | 8 | @view_bp.route('/', defaults={'path': ''}) 9 | @view_bp.route('/') 10 | def home(path): 11 | """Render the html of AskOmics 12 | 13 | Returns 14 | ------- 15 | html 16 | Html code of AskOmics 17 | """ 18 | proxy_path = "/" 19 | try: 20 | proxy_path = current_app.iniconfig.get('askomics', 'reverse_proxy_path') 21 | except Exception: 22 | pass 23 | 24 | # get sentry frontend dsn 25 | sentry_dsn = "" 26 | try: 27 | sentry_dsn = current_app.iniconfig.get("sentry", "frontend_dsn") 28 | except Exception: 29 | pass 30 | 31 | title = "AskOmics" 32 | try: 33 | subtitle = current_app.iniconfig.get('askomics', 'subtitle') 34 | title = "AskOmics | {}".format(subtitle) 35 | except Exception: 36 | pass 37 | 38 | return render_template('index.html', title=title, proxy_path=proxy_path, sentry=sentry_dsn) 39 | -------------------------------------------------------------------------------- /askomics/libaskomics/FilesUtils.py: -------------------------------------------------------------------------------- 1 | from askomics.libaskomics.Database import Database 2 | from askomics.libaskomics.Params import Params 3 | 4 | 5 | class FilesUtils(Params): 6 | """Contain methods usefull in FilesHandler and ResultsdHandler""" 7 | 8 | def __init__(self, app, session): 9 | """init 10 | 11 | Parameters 12 | ---------- 13 | app : Flask 14 | flask app 15 | session : 16 | AskOmics session, contain the user 17 | """ 18 | Params.__init__(self, app, session) 19 | 20 | def get_size_occupied_by_user(self): 21 | """Get disk size occuped by file user (uploaded files and results) 22 | 23 | Returns 24 | ------- 25 | int 26 | size un bytes 27 | """ 28 | database = Database(self.app, self.session) 29 | 30 | query = ''' 31 | SELECT SUM(size) 32 | FROM ( 33 | SELECT size 34 | FROM results 35 | WHERE user_id = ? 36 | UNION ALL 37 | SELECT size 38 | FROM files 39 | WHERE user_id = ? 40 | ) 41 | ''' 42 | 43 | result = database.execute_sql_query(query, (self.session["user"]["id"], self.session["user"]["id"])) 44 | 45 | return 0 if result[0][0] is None else result[0][0] 46 | -------------------------------------------------------------------------------- /askomics/libaskomics/Mailer.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.mime.multipart import MIMEMultipart 3 | from email.mime.text import MIMEText 4 | 5 | from askomics.libaskomics.Params import Params 6 | 7 | 8 | class Mailer(Params): 9 | """Send mail 10 | 11 | Attributes 12 | ---------- 13 | host : str 14 | SMTP host 15 | password : str 16 | SMTP password 17 | port : int 18 | SMTP port 19 | user : str 20 | SMTP user 21 | """ 22 | 23 | def __init__(self, app, session): 24 | """init 25 | 26 | Parameters 27 | ---------- 28 | app : Flask 29 | flask app 30 | session 31 | AskOmics session, contain the user 32 | """ 33 | Params.__init__(self, app, session) 34 | try: 35 | self.host = self.settings.get('askomics', 'smtp_host') 36 | self.port = self.settings.get('askomics', 'smtp_port') 37 | except Exception: 38 | self.host = None 39 | self.port = None 40 | 41 | try: 42 | self.user = self.settings.get('askomics', 'smtp_user') 43 | self.password = self.settings.get('askomics', 'smtp_password') 44 | except Exception: 45 | self.user = None 46 | self.password = None 47 | 48 | try: 49 | self.sender = self.settings.get('askomics', 'smtp_sender') 50 | except Exception: 51 | self.sender = None 52 | 53 | try: 54 | self.connection = self.settings.get('askomics', 'smtp_connection') 55 | except Exception: 56 | self.connection = None 57 | 58 | def check_mailer(self): 59 | """Check if a smtp server is set 60 | 61 | Returns 62 | ------- 63 | bool 64 | True if SMTP is set 65 | """ 66 | if not self.host or not self.port or not self.sender: 67 | return False 68 | return True 69 | 70 | def send_mail(self, receiver, subject, body): 71 | """Send a mail 72 | 73 | Parameters 74 | ---------- 75 | receiver : str 76 | receiver email adress 77 | subject : str 78 | email subject 79 | body : str 80 | Mail content 81 | """ 82 | if self.check_mailer: 83 | pass 84 | message = MIMEMultipart('alternative') 85 | message.set_charset("utf-8") 86 | message["FROM"] = self.sender 87 | message["To"] = receiver 88 | message["Subject"] = subject 89 | 90 | _attach = MIMEText(body.encode('utf-8'), 'plain', 'UTF-8') 91 | message.attach(_attach) 92 | 93 | smtp = smtplib.SMTP(host=self.host, port=self.port) 94 | smtp.connect(self.host, self.port) 95 | 96 | if self.connection == "starttls": 97 | smtp.starttls() 98 | 99 | if self.user and self.password: 100 | smtp.login(self.user, self.password) 101 | 102 | smtp.sendmail(self.sender, receiver, message.as_string()) 103 | -------------------------------------------------------------------------------- /askomics/libaskomics/Params.py: -------------------------------------------------------------------------------- 1 | """Contain the Params class 2 | """ 3 | 4 | 5 | class Params(object): 6 | 7 | """Mother of all libaskomics classes 8 | 9 | Attributes 10 | ---------- 11 | app : 12 | flask app 13 | log : 14 | flask logger 15 | session : 16 | flask session 17 | settings : 18 | askomics settings (from ini) 19 | """ 20 | 21 | def __init__(self, app, session): 22 | """Store the logger, settings, session and app 23 | 24 | Parameters 25 | ---------- 26 | app : 27 | flask app 28 | session : 29 | flask session 30 | """ 31 | self.log = app.logger 32 | self.settings = app.iniconfig 33 | self.session = session 34 | self.app = app 35 | 36 | self.error = False 37 | self.error_message = [] 38 | 39 | def get_error(self): 40 | 41 | return self.error 42 | 43 | def get_error_message(self): 44 | 45 | return self.error_message 46 | 47 | def str_to_bool(self, bool_str): 48 | """Convert a true/false string to a boolan value 49 | 50 | Parameters 51 | ---------- 52 | bool_str : str 53 | boolean string 54 | 55 | Returns 56 | ------- 57 | bool 58 | True or False 59 | """ 60 | if bool_str.lower() == 'true': 61 | return True 62 | return False 63 | 64 | def logged_user(self): 65 | """Check if a user is logged 66 | 67 | Returns 68 | ------- 69 | bool 70 | True if a user is logged 71 | """ 72 | if "user" in self.session: 73 | return True 74 | return False 75 | -------------------------------------------------------------------------------- /askomics/libaskomics/RdfGraph.py: -------------------------------------------------------------------------------- 1 | from askomics.libaskomics.Params import Params 2 | 3 | import rdflib 4 | from rdflib.namespace import Namespace 5 | 6 | 7 | class RdfGraph(Params): 8 | """rdflib.graph wrapper 9 | 10 | Attributes 11 | ---------- 12 | namespace_internal : Namespace 13 | AskOmics napespace 14 | namespace_data : Namespace 15 | AskOmics prefix 16 | graph : Graph 17 | rdflib graph 18 | ntriple : int 19 | Number of triple in the graph 20 | """ 21 | 22 | def __init__(self, app, session): 23 | """init 24 | 25 | Parameters 26 | ---------- 27 | app : Flask 28 | Flask app 29 | session 30 | AskOmics session 31 | """ 32 | Params.__init__(self, app, session) 33 | 34 | self.namespace_data = Namespace(self.settings.get('triplestore', 'namespace_data')) 35 | self.namespace_internal = Namespace(self.settings.get('triplestore', 'namespace_internal')) 36 | 37 | self.graph = rdflib.Graph() 38 | self.graph.bind('', self.namespace_data) 39 | self.graph.bind('askomics', self.namespace_internal) 40 | self.graph.bind('faldo', "http://biohackathon.org/resource/faldo/") 41 | self.graph.bind('dc', 'http://purl.org/dc/elements/1.1/') 42 | self.graph.bind('prov', 'http://www.w3.org/ns/prov#') 43 | self.graph.bind('dcat', 'http://www.w3.org/ns/dcat#') 44 | self.ntriple = 0 45 | self.percent = None 46 | 47 | def parse(self, source=None, publicID=None, format=None, location=None, file=None, data=None, **args): 48 | """Parse a RDF file""" 49 | self.graph.parse(source=source, publicID=publicID, format=format, location=location, file=file, data=data, **args) 50 | 51 | def add(self, triple): 52 | """Add a triple into the rdf graph 53 | 54 | Parameters 55 | ---------- 56 | triple : tuple 57 | triple to add 58 | """ 59 | self.graph.add(triple) 60 | self.ntriple += 1 61 | 62 | def remove(self, triple): 63 | """Remove a triple into the rdf graph 64 | 65 | Parameters 66 | ---------- 67 | triple : tuple 68 | triple to remove 69 | """ 70 | self.graph.remove(triple) 71 | self.ntriple -= 1 72 | 73 | def bind(self, a, b): 74 | """Bind a namespace 75 | 76 | Parameters 77 | ---------- 78 | a : string 79 | prefix 80 | b : string 81 | namespace 82 | """ 83 | self.graph.bind(a, b) 84 | 85 | def get_triple(self): 86 | """Get all triple""" 87 | for s, p, o in self.graph: 88 | yield s, p, o 89 | 90 | def serialize(self, destination=None, format='xml', base=None, encoding=None, **args): 91 | """Serialize the graph into a file 92 | 93 | Parameters 94 | ---------- 95 | format : string 96 | rdf syntaxe 97 | encoding : string 98 | Encoding 99 | destination : string 100 | File destination 101 | """ 102 | result = self.graph.serialize(destination=destination, format=format, base=base, encoding=encoding, **args) 103 | 104 | if destination is None: 105 | return result 106 | -------------------------------------------------------------------------------- /askomics/libaskomics/Start.py: -------------------------------------------------------------------------------- 1 | """Contain the Start classe 2 | """ 3 | import os 4 | 5 | from askomics.libaskomics.Database import Database 6 | from askomics.libaskomics.Params import Params 7 | 8 | 9 | class Start(Params): 10 | 11 | """Initialize the data directory and the database 12 | 13 | Attributes 14 | ---------- 15 | data_directory : str 16 | Path to the data directory 17 | database_path : str 18 | Path to the database file 19 | """ 20 | 21 | def __init__(self, app, session): 22 | """Get data directory and database paths from the askomics settings 23 | 24 | Parameters 25 | ---------- 26 | app : 27 | flask app 28 | session : 29 | flask session 30 | """ 31 | Params.__init__(self, app, session) 32 | 33 | self.data_directory = self.settings.get('askomics', 'data_directory') 34 | self.database_path = self.settings.get('askomics', 'database_path') 35 | 36 | def start(self): 37 | """Create the data diretory and initialize the database file 38 | """ 39 | self.create_data_directory() 40 | self.create_database() 41 | self.create_anonymous() 42 | 43 | def create_data_directory(self): 44 | """Create the data directory if it not exists 45 | """ 46 | if not os.path.isdir(self.data_directory): 47 | os.makedirs(self.data_directory) 48 | 49 | def create_database(self): 50 | """Initialize the database file 51 | """ 52 | database = Database(self.app, self.session) 53 | database.init_database() 54 | 55 | def create_anonymous(self): 56 | """Create anonymous data folder if required 57 | """ 58 | if self.settings.get('askomics', 'anonymous_query', fallback=False): 59 | data_path = "{}/{}_{}/results".format( 60 | self.data_directory, 61 | "0", 62 | "anonymous" 63 | ) 64 | if not os.path.isdir(data_path): 65 | os.makedirs(data_path) 66 | -------------------------------------------------------------------------------- /askomics/libaskomics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askomics/flaskomics/2c10eef9e34305844b6afdd37a4e508d6ae5a294/askomics/libaskomics/__init__.py -------------------------------------------------------------------------------- /askomics/middleware.py: -------------------------------------------------------------------------------- 1 | class PrefixMiddleware(object): 2 | 3 | def __init__(self, app, prefix=''): 4 | self.app = app 5 | self.prefix = prefix 6 | 7 | def __call__(self, environ, start_response): 8 | if self.prefix is not None: 9 | environ['SCRIPT_NAME'] = self.prefix 10 | path_info = environ['PATH_INFO'] 11 | if path_info.startswith(self.prefix): 12 | environ['PATH_INFO'] = path_info[len(self.prefix):] 13 | return self.app(environ, start_response) 14 | -------------------------------------------------------------------------------- /askomics/react/src/classes/utils.jsx: -------------------------------------------------------------------------------- 1 | export default class Utils { 2 | 3 | isUrl(s) { 4 | var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ 5 | return regexp.test(s); 6 | } 7 | 8 | truncate(string, n) { 9 | if (string.length > n) { 10 | return string.substring(0 ,n) + "..." 11 | } else { 12 | return string 13 | } 14 | } 15 | 16 | splitUrl(url) { 17 | let splitList = url.split('/') 18 | // take last elem 19 | let last = splitList[splitList.length - 1] 20 | let splitList2 = last.split('#') 21 | return decodeURI(splitList2[splitList2.length - 1]) 22 | } 23 | 24 | humanFileSize (bytes, si) { 25 | let thresh = si ? 1000 : 1024 26 | if (Math.abs(bytes) < thresh) { 27 | return bytes + ' B' 28 | } 29 | let units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] 30 | let u = -1 31 | do { 32 | bytes /= thresh 33 | ++u 34 | } while (Math.abs(bytes) >= thresh && u < units.length - 1) 35 | return bytes.toFixed(1) + ' ' + units[u] 36 | } 37 | 38 | humanDate (date) { 39 | let event = new Date(date * 1000) 40 | return event.toUTCString() 41 | } 42 | 43 | objectHaveKeys(obj, level, ...rest) { 44 | if (obj === undefined) return false 45 | if (rest.length == 0 && obj.hasOwnProperty(level)) return true 46 | return this.objectHaveKeys(obj[level], ...rest) 47 | } 48 | 49 | isFunction(functionToCheck) { 50 | return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]' 51 | } 52 | 53 | stringToHexColor (str) { 54 | // first, hash the string into an int 55 | let hash = 0 56 | for (var i = 0; i < str.length; i++) { 57 | hash = str.charCodeAt(i) + ((hash << 5) - hash) 58 | } 59 | // Then convert this int into a rgb color code 60 | let c = (hash & 0x00FFFFFF).toString(16).toUpperCase() 61 | let hex = '#' + '00000'.substring(0, 6 - c.length) + c 62 | return hex 63 | } 64 | 65 | isDarkColor(hex) { 66 | let c = hex.substring(1) // strip # 67 | let rgb = parseInt(c, 16) // convert rrggbb to decimal 68 | let r = (rgb >> 16) & 0xff // extract red 69 | let g = (rgb >> 8) & 0xff // extract green 70 | let b = (rgb >> 0) & 0xff // extract blue 71 | 72 | let luma = 0.2126 * r + 0.7152 * g + 0.0722 * b // per ITU-R BT.709 73 | 74 | if (luma < 128) { 75 | return true 76 | } 77 | return false 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /askomics/react/src/components/template.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default class Template extends Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | } 9 | 10 | render() { 11 | return
12 | } 13 | } 14 | 15 | Template.propTypes = { 16 | template: PropTypes.string.isRequired 17 | } 18 | -------------------------------------------------------------------------------- /askomics/react/src/components/waiting.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Spinner } from 'reactstrap' 3 | import PropTypes from 'prop-types' 4 | 5 | export default class WaitingDiv extends Component { 6 | constructor (props) { 7 | super(props) 8 | this.state = { 9 | center: this.props.center ? 'center' : '', 10 | size: this.props.size ? this.props.size : '' 11 | } 12 | } 13 | 14 | render () { 15 | if (this.props.waiting) { 16 | return ( 17 |
18 | 19 |
20 | ) 21 | } 22 | return null 23 | } 24 | } 25 | 26 | WaitingDiv.propTypes = { 27 | center: PropTypes.bool, 28 | waiting: PropTypes.bool, 29 | size: PropTypes.string 30 | } 31 | -------------------------------------------------------------------------------- /askomics/react/src/contact.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Link } from 'react-router-dom' 3 | import { Collapse, Navbar, NavbarBrand, Nav, NavItem } from 'reactstrap' 4 | import PropTypes from 'prop-types' 5 | import Template from './components/template' 6 | 7 | export default class Contact extends Component { 8 | constructor (props) { 9 | super(props) 10 | console.log("test") 11 | } 12 | 13 | render () { 14 | return ( 15 |
16 |

Contact

17 |
18 |
19 |