├── .flake8 ├── .gitignore ├── .gitmodules ├── .travis-install ├── .travis.yml ├── LICENSE ├── README.md ├── abacus_server ├── __init__.py ├── __main__.py ├── abacus_server │ ├── __init__.py │ ├── celery.py │ ├── file_handlers.py │ ├── prod_settings.py │ ├── settings.py │ ├── tasks.py │ ├── urls.py │ ├── views.py │ └── wsgi.py ├── config.json.example └── manage.py ├── biohub-cli.py ├── biohub ├── __init__.py ├── abacus │ ├── __init__.py │ ├── apps.py │ ├── conf.py │ ├── config.json.example │ ├── consts.py │ ├── handlers.py │ ├── remote.py │ ├── result.py │ ├── security.py │ ├── tasks.py │ ├── urls.py │ ├── views.py │ └── ws_handlers.py ├── accounts │ ├── __init__.py │ ├── apps.py │ ├── mail.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── serializers.py │ ├── urls.py │ ├── validators.py │ └── views.py ├── biobrick │ ├── __init__.py │ ├── apps.py │ ├── bin │ │ └── updateparts.py │ ├── cache.py │ ├── exceptions.py │ ├── highlighter.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── installjob.py │ │ │ └── refreshweight.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20170918_2041.py │ │ └── __init__.py │ ├── models.py │ ├── search_indexes.py │ ├── serializers.py │ ├── spiders.py │ ├── sql │ │ ├── __init__.py │ │ ├── creation │ │ │ ├── __init__.py │ │ │ ├── views_creation.py │ │ │ └── views_creation.sql │ │ ├── igem │ │ │ └── preprocess.sql │ │ └── weight │ │ │ └── fetch.sql │ ├── urls.py │ └── views.py ├── biocircuit │ ├── __init__.py │ ├── apps.py │ ├── biocircuit.py │ ├── biogate_man.py │ ├── compile-espresso.py │ ├── espresso │ │ ├── backup │ │ │ ├── black_white.c │ │ │ ├── canonical.c │ │ │ ├── equiv.c │ │ │ ├── essentiality.c │ │ │ ├── map.c │ │ │ ├── sigma.c │ │ │ ├── signature.c │ │ │ ├── signature.h │ │ │ └── signature_exact.c │ │ ├── cofactor.c │ │ ├── cols.c │ │ ├── compl.c │ │ ├── contain.c │ │ ├── cubestr.c │ │ ├── cvrin.c │ │ ├── cvrm.c │ │ ├── cvrmisc.c │ │ ├── cvrout.c │ │ ├── dominate.c │ │ ├── espresso.c │ │ ├── espresso.h │ │ ├── espressomodule.c │ │ ├── essen.c │ │ ├── exact.c │ │ ├── example.txt │ │ ├── expand.c │ │ ├── gasp.c │ │ ├── gimpel.c │ │ ├── globals.c │ │ ├── hack.c │ │ ├── indep.c │ │ ├── irred.c │ │ ├── matrix.c │ │ ├── mincov.c │ │ ├── mincov_int.h │ │ ├── opo.c │ │ ├── pair.c │ │ ├── part.c │ │ ├── primes.c │ │ ├── reduce.c │ │ ├── rows.c │ │ ├── set.c │ │ ├── set.h │ │ ├── setc.c │ │ ├── sharp.c │ │ ├── sminterf.c │ │ ├── solution.c │ │ ├── sparse.c │ │ ├── sparse.h │ │ ├── sparse_int.h │ │ ├── unate.c │ │ ├── utility.h │ │ └── verify.c │ ├── gates_lizhi.json │ ├── migrations │ │ └── __init__.py │ ├── urls.py │ └── views.py ├── biomap │ ├── __init__.py │ ├── analyzer.py │ ├── apps.py │ ├── builder.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── installgraph.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── sql │ │ ├── __init__.py │ │ └── fetch_bricks.sql │ ├── storage.py │ ├── urls.py │ ├── views.py │ └── ws_handlers.py ├── compat │ ├── __init__.py │ ├── channel_worker.py │ ├── db.py │ ├── redis.py │ ├── rest.py │ └── warnings.py ├── core │ ├── __init__.py │ ├── app_template │ │ ├── __init__.py-tpl │ │ ├── apps.py-tpl │ │ ├── migrations │ │ │ └── __init__.py-tpl │ │ ├── models.py-tpl │ │ ├── serializers.py-tpl │ │ ├── urls.py-tpl │ │ └── views.py-tpl │ ├── apps.py │ ├── channel_routing.py │ ├── conf │ │ ├── __init__.py │ │ └── signal.py │ ├── files │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── clean_unused.py │ │ ├── handlers.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── views.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── codestyle.py │ │ │ ├── startapp.py │ │ │ └── test.py │ ├── models.py │ ├── plugins │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── config.py │ │ ├── exceptions.py │ │ ├── ipc_slave.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ ├── __init__.py │ │ │ │ ├── _base.py │ │ │ │ ├── installplugin.py │ │ │ │ ├── migrateplugin.py │ │ │ │ ├── mkpluginmigrations.py │ │ │ │ ├── newplugin.py │ │ │ │ └── removeplugin.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── plugin_template │ │ │ ├── __init__.py-tpl │ │ │ ├── apps.py-tpl │ │ │ ├── migrations │ │ │ │ └── __init__.py-tpl │ │ │ ├── models.py-tpl │ │ │ ├── serializers.py-tpl │ │ │ ├── urls.py-tpl │ │ │ ├── views.py-tpl │ │ │ └── ws_handlers.py-tpl │ │ ├── registry.py │ │ ├── serializers.py │ │ ├── urls.py │ │ ├── user_plugin_manager.py │ │ └── views.py │ ├── routes.py │ ├── tasks │ │ ├── __init__.py │ │ ├── base.py │ │ ├── broker.py │ │ ├── consumers.py │ │ ├── data_structures.py │ │ ├── exceptions.py │ │ ├── executors.py │ │ ├── payload.py │ │ ├── registry.py │ │ ├── result.py │ │ ├── status.py │ │ └── storage.py │ └── websocket │ │ ├── __init__.py │ │ ├── consumers.py │ │ ├── message.py │ │ ├── parsers.py │ │ ├── registry.py │ │ ├── test.py │ │ └── tool.py ├── forum │ ├── __init__.py │ ├── apps.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── makedigest.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20170919_1517.py │ │ ├── 0003_auto_20170919_1520.py │ │ ├── 0004_activity_brick_name.py │ │ ├── 0005_auto_20171001_2105.py │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── activity_model.py │ │ ├── bio_models.py │ │ └── forum_models.py │ ├── serializers │ │ ├── __init__.py │ │ ├── activity_serializers.py │ │ ├── article_serializers.py │ │ ├── experience_serializers.py │ │ └── post_serializers.py │ ├── signals │ │ ├── __init__.py │ │ ├── bio_signals.py │ │ └── forum_signals.py │ ├── urls.py │ ├── user_defined_signals.py │ └── views │ │ ├── __init__.py │ │ ├── activity_views.py │ │ ├── article_views.py │ │ ├── experience_views.py │ │ └── post_views.py ├── main │ ├── __init__.py │ ├── asgi.py │ ├── settings │ │ ├── __init__.py │ │ ├── base.py │ │ ├── dev.py │ │ └── prod.py │ ├── urls.py │ ├── views.py │ └── wsgi.py ├── manage.py ├── notices │ ├── __init__.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20171001_2105.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── template │ │ ├── __init__.py │ │ └── filters.py │ ├── tool.py │ ├── urls.py │ ├── views.py │ └── ws_handlers.py └── utils │ ├── __init__.py │ ├── collections.py │ ├── db.py │ ├── detect.py │ ├── download.py │ ├── http.py │ ├── module.py │ ├── path.py │ ├── redis.py │ ├── registry │ ├── __init__.py │ └── base.py │ ├── rest │ ├── __init__.py │ ├── authentication.py │ ├── fields.py │ ├── pagination.py │ ├── permissions.py │ └── serializers.py │ ├── test.py │ └── url.py ├── config.json.example ├── docs └── index.md ├── ipc_master.py ├── mkdocs.yml ├── requirements ├── abacus-server.txt ├── base.txt ├── biobrick-base.txt ├── biocircuit-base.txt ├── codestyle-base.txt ├── dev.txt ├── doc-base.txt ├── prod.txt └── test-base.txt └── tests ├── __init__.py ├── abacus ├── FakeAbacus │ ├── bin │ │ └── FakeAbacus.jar │ ├── compile │ └── src │ │ ├── FakeAbacus.java │ │ └── cmd │ │ ├── ArgsTemplement.java │ │ └── Option.java ├── __init__.py ├── _base.py ├── example.pdb ├── test_local.py ├── test_permission.py └── test_remote.py ├── accounts ├── __init__.py ├── test_change_password.py ├── test_crud.py ├── test_imgs │ ├── a.png │ └── b.png ├── test_login.py ├── test_logout.py ├── test_password_validation.py ├── test_register.py ├── test_reset_password.py └── test_username_validation.py ├── biobrick ├── __init__.py ├── _base.py ├── test_cache.py ├── test_restful.py ├── test_search.py ├── test_spiders.py └── test_wierd_spiders.py ├── biocircuit ├── __init__.py ├── test_biocircuit.py ├── test_gates.py └── test_score.py ├── biomap ├── __init__.py └── test_ana.py ├── core ├── __init__.py ├── files │ ├── __init__.py │ ├── _utils.py │ ├── cleanunused_tests │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ ├── views.py │ │ └── ws_handlers.py │ ├── samples │ │ ├── 2 │ │ ├── 1.txt │ │ ├── 2.txt │ │ └── 2.txt.txt │ ├── test_cleanunused.py │ ├── test_handlers.py │ ├── test_models.py │ ├── test_utils.py │ └── test_views.py ├── plugins │ ├── __init__.py │ ├── _base.py │ ├── another_plugin │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── bad_plugin │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── expected │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ ├── views.py │ │ └── ws_handlers.py │ ├── my_plugin │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── consts.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── test_api.py │ ├── test_command.py │ ├── test_install.py │ ├── test_protect.py │ ├── test_refresh.py │ ├── test_reload.py │ ├── test_remove.py │ └── test_user_plugin.py ├── tasks │ ├── __init__.py │ ├── _base.py │ ├── myplugin │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── serializers.py │ │ ├── tasks.py │ │ ├── urls.py │ │ ├── views.py │ │ └── ws_handlers.py │ ├── test_payload.py │ ├── test_run.py │ └── test_task_register.py ├── test_route.py ├── urls.py └── websocket │ ├── __init__.py │ ├── _base.py │ ├── my_plugin │ ├── __init__.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── urls.py │ ├── views.py │ └── ws_handlers.py │ ├── test_broadcast.py │ ├── test_connect.py │ └── test_signal.py ├── forum ├── __init__.py ├── test_activity_and_notices.py ├── test_restful_articles.py ├── test_restful_experiences.py ├── test_restful_posts.py └── test_signals.py ├── notices ├── __init__.py ├── test_logic.py ├── test_send.py └── test_ws.py ├── test_send_real_mail.py └── utils ├── __init__.py └── rest ├── __init__.py ├── models.py ├── test_bind_model.py └── test_dynamic_serializer.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore=E501 3 | exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,biogate.py 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # Jupyter Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # SageMath parsed files 79 | *.sage.py 80 | 81 | # Environments 82 | .env 83 | .venv 84 | env/ 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | .spyproject 91 | 92 | # Rope project settings 93 | .ropeproject 94 | 95 | # mkdocs documentation 96 | /site 97 | 98 | # mypy 99 | .mypy_cache/ 100 | 101 | /config.json 102 | config.json 103 | /.idea/ 104 | 105 | # biocircuit 106 | biogate.py 107 | 108 | # editor configs 109 | .vscode/ 110 | 111 | /abacus_server/config.json 112 | /experiences_list.txt 113 | /_download 114 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frontend/main"] 2 | path = frontend/main 3 | url = https://github.com/USTC-Software2017-frontend/Biohub-frontend 4 | [submodule "frontend/biocircuit"] 5 | path = frontend/biocircuit 6 | url = https://github.com/USTC-Software2017-frontend/biohub-plugin-BioBLESS 7 | [submodule "frontend/abacus"] 8 | path = frontend/abacus 9 | url = https://github.com/USTC-Software2017-frontend/biohub-plugin-ABACUS 10 | [submodule "frontend/biomap"] 11 | path = frontend/biomap 12 | url = https://github.com/USTC-Software2017-frontend/biohub-plugin-BioMap 13 | [submodule "frontend/scaffold"] 14 | path = frontend/scaffold 15 | url = https://github.com/USTC-Software2017-frontend/biohub-plugin-scaffold.git 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: required 3 | python: 4 | - "3.6" 5 | services: 6 | - mysql 7 | - redis-server 8 | notifications: 9 | email: false 10 | cache: 11 | pip: true 12 | directories: 13 | - _download 14 | install: "bash .travis-install" 15 | script: "biohub/manage.py codestyle && biohub/manage.py test" 16 | -------------------------------------------------------------------------------- /abacus_server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/abacus_server/__init__.py -------------------------------------------------------------------------------- /abacus_server/abacus_server/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | from . import tasks # noqa 3 | 4 | __all__ = ['celery_app'] 5 | -------------------------------------------------------------------------------- /abacus_server/abacus_server/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | from celery import Celery 5 | 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'abacus_server.settings') 7 | 8 | app = Celery('abacus_server') 9 | app.config_from_object('django.conf:settings', namespace='CELERY') 10 | -------------------------------------------------------------------------------- /abacus_server/abacus_server/file_handlers.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | from django.core.files.uploadhandler import TemporaryFileUploadHandler 4 | from django.core.files.storage import default_storage 5 | 6 | FakeFile = namedtuple('FakeFile', 'name') 7 | 8 | 9 | class StraightHandler(TemporaryFileUploadHandler): 10 | """ 11 | A file handler to directly store uploaded files. 12 | """ 13 | 14 | def file_complete(self, file_size): 15 | super(StraightHandler, self).file_complete(file_size) 16 | 17 | self.file_name = default_storage.save(self.file_name, self.file) 18 | 19 | # Hacky approach to avoid annoying error. 20 | if hasattr(self.file.file, '_closer'): 21 | self.file.file._closer.close_called = True 22 | 23 | self.file = FakeFile(name=self.file_name) 24 | return self.file 25 | -------------------------------------------------------------------------------- /abacus_server/abacus_server/prod_settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | from .settings import * # noqa 4 | 5 | DEBUG = False 6 | ALLOWED_HOSTS = ['*'] 7 | 8 | LOGGING_DIR = os.getenv('BIOHUB_LOGGING_DIR', tempfile.gettempdir()) 9 | LOGGING_REQUEST_DIR = os.path.join(LOGGING_DIR, 'biohub_log', 'request') 10 | LOGGING_ERROR_DIR = os.path.join(LOGGING_DIR, 'biohub_log', 'error') 11 | 12 | os.makedirs(LOGGING_REQUEST_DIR, exist_ok=True) 13 | os.makedirs(LOGGING_ERROR_DIR, exist_ok=True) 14 | 15 | 16 | LOGGING = { 17 | "version": 1, 18 | "disable_existing_handlers": True, 19 | 'formatters': { 20 | 'verbose': { 21 | 'format': '%(levelname)s %(asctime)s %(name)s:%(funcName)s:%(lineno)d %(process)d %(thread)x %(message)s', 22 | 'level': 'WARNING' 23 | }, 24 | }, 25 | "handlers": { 26 | "error_log": { 27 | 'class': 'logging.handlers.TimedRotatingFileHandler', 28 | 'filename': os.path.join(LOGGING_ERROR_DIR, 'log'), 29 | 'formatter': 'verbose', 30 | 'when': 'D' 31 | }, 32 | 'ignore': { 33 | 'class': 'logging.NullHandler' 34 | } 35 | }, 36 | "loggers": { 37 | 'django': { 38 | 'handlers': ['error_log'], 39 | 'propagate': False, 40 | 'level': 'WARNING', 41 | }, 42 | 'abacus_server': { 43 | 'handlers': ['error_log'], 44 | 'propagate': False, 45 | 'level': 'WARNING', 46 | }, 47 | '': { 48 | 'handlers': ['error_log'], 49 | 'propagate': False, 50 | 'level': 'ERROR', 51 | }, 52 | 'daphne': { 53 | 'handlers': ['ignore'], 54 | 'propagate': False, 55 | 'level': 'DEBUG' 56 | } 57 | } 58 | } 59 | 60 | del os 61 | -------------------------------------------------------------------------------- /abacus_server/abacus_server/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.conf import settings 3 | from django.views.static import serve 4 | 5 | from .views import MainView, QueryView 6 | 7 | reg_hex = '[0-9a-f-]' 8 | reg_guid = r'%s{8}-%s{4}-%s{4}-%s{4}-%s{12}' % ((reg_hex,) * 5) 9 | 10 | urlpatterns = [ 11 | url(r'^$', MainView.as_view(), name='main'), 12 | url(r'^(%s)/$' % reg_guid, QueryView.as_view(), name='query') 13 | ] 14 | 15 | if settings.DEBUG: 16 | urlpatterns += [ 17 | url(r'^media/(?P.*)$', serve, dict(document_root=settings.MEDIA_ROOT)) 18 | ] 19 | -------------------------------------------------------------------------------- /abacus_server/abacus_server/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for abacus_server project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "abacus_server.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /abacus_server/config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "ABACUS_JAR_PATH": "", 3 | "REDIS_URI": "", 4 | "ABACUS_DATABASE_PATH": "", 5 | "STORAGE_ROOT": "" 6 | } 7 | -------------------------------------------------------------------------------- /abacus_server/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import os.path as path 4 | import sys 5 | 6 | CURRENT_DIR = path.join(path.dirname(__file__)) 7 | 8 | sys.path.insert(0, CURRENT_DIR) 9 | 10 | sys.path.insert(0, '.') 11 | 12 | if __name__ == "__main__": 13 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "abacus_server.settings") 14 | try: 15 | from django.core.management import execute_from_command_line 16 | except ImportError: 17 | # The above import may fail for some other reason. Ensure that the 18 | # issue is really that Django is missing to avoid masking other 19 | # exceptions on Python 2. 20 | try: 21 | import django # noqa 22 | except ImportError: 23 | raise ImportError( 24 | "Couldn't import Django. Are you sure it's installed and " 25 | "available on your PYTHONPATH environment variable? Did you " 26 | "forget to activate a virtual environment?" 27 | ) 28 | raise 29 | execute_from_command_line(sys.argv) 30 | -------------------------------------------------------------------------------- /biohub/__init__.py: -------------------------------------------------------------------------------- 1 | from django.utils.version import get_version 2 | 3 | VERSION = (2, 0, 0, 'final', 0) 4 | 5 | __version__ = get_version(VERSION) 6 | -------------------------------------------------------------------------------- /biohub/abacus/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.abacus.apps.AbacusConfig' 2 | -------------------------------------------------------------------------------- /biohub/abacus/apps.py: -------------------------------------------------------------------------------- 1 | from biohub.core.plugins import PluginConfig 2 | 3 | 4 | class AbacusConfig(PluginConfig): 5 | 6 | name = 'biohub.abacus' 7 | title = 'ABACUS' 8 | author = 'Jay Lan, Jiansi Li, hsfzxjy' 9 | description = 'ABACUS is a tool to design amino acid sequence from a given protein. Proteins are specified in Protein Data Bank format. The uploaded .pdb will be evaludated by simulated annealing algorithm, and another .pdb file representing the result will be returned.' 10 | js_url = 'https://ustc-software2017-frontend.github.io/biohub-plugin-ABACUS/build/plugin.js' 11 | -------------------------------------------------------------------------------- /biohub/abacus/config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "ABACUS_JAR_PATH": "", 3 | "ABACUS_DATABASE_PATH": "", 4 | "ABACUS_REMOTE_SERVERS": [] 5 | } 6 | -------------------------------------------------------------------------------- /biohub/abacus/consts.py: -------------------------------------------------------------------------------- 1 | LOCAL = 'local' 2 | REMOTE = 'remote' 3 | -------------------------------------------------------------------------------- /biohub/abacus/remote.py: -------------------------------------------------------------------------------- 1 | from random import choice 2 | 3 | import requests 4 | from django.urls import reverse 5 | 6 | from biohub.utils.url import add_params 7 | from .conf import settings 8 | from . import security 9 | 10 | __all__ = ['start', 'query'] 11 | 12 | 13 | def _ensure_success(response): 14 | """ 15 | To ensure the request succeeded. 16 | """ 17 | assert response.status_code == 200, \ 18 | "Remote call failed with status code {}\nContent: {}".format(response.status_code, response.text) 19 | 20 | 21 | def choose_server(): 22 | """ 23 | Choose and return the most idled server. 24 | """ 25 | return choice(settings.ABACUS_REMOTE_SERVERS) 26 | 27 | 28 | def start(request): 29 | """ 30 | Uploads the file to remote server. 31 | 32 | Returns a tuple (task_id, server, signature). 33 | """ 34 | server = choose_server() 35 | signature = security.signature() 36 | response = requests.post( 37 | server, 38 | params={ 39 | 'callback': add_params( 40 | request.build_absolute_uri(reverse('api:abacus:remote-callback')), 41 | s=signature 42 | ), 43 | }, 44 | files={ 45 | 'file': request.FILES['file'] 46 | } 47 | ) 48 | _ensure_success(response) 49 | return response.json()['task_id'], server, signature 50 | 51 | 52 | def query(task_id): 53 | """ 54 | Queries the task specified by task_id and returns the raw response. 55 | """ 56 | from .result import AbacusAsyncResult 57 | 58 | response = requests.get( 59 | '{}{}/'.format(AbacusAsyncResult(task_id).server, task_id) 60 | ) 61 | _ensure_success(response) 62 | return response.json() 63 | -------------------------------------------------------------------------------- /biohub/abacus/security.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from django.core.signing import dumps 4 | from django.utils.crypto import get_random_string 5 | 6 | sign = partial(dumps, salt='Salt for abacus.', compress=True) 7 | 8 | 9 | def signature(): 10 | """ 11 | Generates a random signature. 12 | """ 13 | return sign(get_random_string()) 14 | 15 | 16 | def validate_signature(async_result, signature): 17 | """ 18 | Validates if the signature is correct. 19 | """ 20 | return async_result.signature == signature 21 | -------------------------------------------------------------------------------- /biohub/abacus/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_api, url 2 | from . import views 3 | 4 | register_api(r'^abacus/', [ 5 | url(r'^start/$', views.StartView.as_view(), name='abacus-start'), 6 | url(r'^query/(?P[0-9a-f-]+)/$', views.QueryView.as_view(), name='abacus-query'), 7 | url(r'^callback/$', views.CallbackView.as_view(), name='remote-callback') 8 | ], 'abacus') 9 | -------------------------------------------------------------------------------- /biohub/abacus/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import views, permissions, parsers 2 | from rest_framework.exceptions import ValidationError 3 | from rest_framework.response import Response 4 | from .handlers import get_handler, query 5 | from .result import AbacusAsyncResult 6 | from .security import validate_signature 7 | 8 | 9 | class StartView(views.APIView): 10 | 11 | permission_classes = (permissions.IsAuthenticated,) 12 | parser_classes = (parsers.MultiPartParser, parsers.FormParser,) 13 | 14 | def post(self, request): 15 | handler = get_handler(request) 16 | return Response(handler.start_task(request.user)) 17 | 18 | 19 | class QueryView(views.APIView): 20 | 21 | permission_classes = (permissions.IsAuthenticated,) 22 | 23 | def get(self, request, task_id): 24 | return Response(query(task_id)) 25 | 26 | 27 | class CallbackView(views.APIView): 28 | 29 | def fail(self, detail): 30 | raise ValidationError(detail) 31 | 32 | def get(self, request): 33 | async_result = AbacusAsyncResult(request.GET.get('task_id', '')) 34 | 35 | if not validate_signature(async_result, request.GET.get('s', '')): 36 | self.fail('Bad signature.') 37 | 38 | if async_result._get_field('status') is None: 39 | self.fail('Task not exists.') 40 | 41 | if 'error' in request.GET: 42 | async_result.error(None) 43 | elif 'output' in request.GET: 44 | async_result.resolve(request.GET['output']) 45 | else: 46 | self.fail('Should specify either error or output.') 47 | return Response('') 48 | -------------------------------------------------------------------------------- /biohub/abacus/ws_handlers.py: -------------------------------------------------------------------------------- 1 | from biohub.core.websocket import register_handler # noqa 2 | 3 | # Place your websocket handlers here. 4 | -------------------------------------------------------------------------------- /biohub/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.accounts.apps.AccountsConfig' 2 | -------------------------------------------------------------------------------- /biohub/accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | name = 'biohub.accounts' 6 | label = 'accounts' 7 | -------------------------------------------------------------------------------- /biohub/accounts/mail.py: -------------------------------------------------------------------------------- 1 | from django.core import mail 2 | 3 | 4 | def get_password_reset_email(user, callback, connection=None): 5 | email = mail.EmailMessage( 6 | 'Biohub Password Reset', 7 | ( 8 | 'Click here to reset: {callback}.' 9 | ).format(callback=callback), 10 | to=[user.email], 11 | connection=connection 12 | ) 13 | email.content_subtype = 'html' 14 | return email 15 | -------------------------------------------------------------------------------- /biohub/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/accounts/mixins.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404 2 | 3 | from rest_framework.response import Response 4 | from rest_framework import viewsets 5 | from rest_framework.exceptions import NotFound 6 | 7 | from .serializers import UserSerializer 8 | from .models import User 9 | 10 | 11 | class UserPaginationMixin(object): 12 | 13 | def paginate_user_queryset(self, queryset): 14 | page = self.paginate_queryset(queryset.order_by('id')) 15 | if page is not None: 16 | serializer = UserSerializer(page, many=True) 17 | return self.get_paginated_response(serializer.data) 18 | 19 | serializer = UserSerializer(page, many=True) 20 | return Response(serializer.data) 21 | 22 | 23 | re_user_lookup_value = r'\d+|me|n:[\da-zA-Z_]{4,15}' 24 | 25 | 26 | class BaseUserViewSetMixin(viewsets.GenericViewSet): 27 | 28 | user_lookup_value_regex = re_user_lookup_value 29 | 30 | def get_user_queryset(self): 31 | return User.objects.all() 32 | 33 | def get_user_object(self): 34 | lookup = self.kwargs['user_pk'] 35 | 36 | if lookup == 'me': 37 | 38 | if not self.request.user.is_authenticated(): 39 | raise NotFound 40 | 41 | if self.action != 'retrieve': 42 | return self.request.user 43 | 44 | lookup = str(self.request.user.id) 45 | 46 | if lookup.startswith('n:'): 47 | 48 | return get_object_or_404(self.get_user_queryset(), username=lookup[2:]) 49 | else: 50 | return get_object_or_404(self.get_user_queryset(), pk=lookup) 51 | 52 | @classmethod 53 | def add_to_router(cls, router): 54 | router.register(r'users/(?P%s)' % cls.user_lookup_value_regex, cls) 55 | return router 56 | -------------------------------------------------------------------------------- /biohub/accounts/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from biohub.core.routes import register_api, url 4 | 5 | from . import views 6 | 7 | router = DefaultRouter() 8 | router.register(r'users', views.UserViewSet) 9 | 10 | register_api(r'^', [ 11 | url(r'^users/register/$', views.register, name='register'), 12 | url(r'^users/login/$', views.login, name='login'), 13 | url(r'^users/logout/$', views.logout, name='logout'), 14 | url(r'^users/upload_avatar/$', views.upload_avatar, name='upload-avatar'), 15 | url(r'^users/change_password/$', 16 | views.change_password, name='change-password'), 17 | url(r'^users/reset_password/$', 18 | views.PasswordResetView.as_view(), name='reset-password') 19 | ] + router.urls, 'accounts') 20 | 21 | extra_router = DefaultRouter() 22 | views.UserRelationViewSet.add_to_router(extra_router) 23 | register_api(r'^', extra_router.urls) 24 | -------------------------------------------------------------------------------- /biohub/accounts/validators.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.core import validators 4 | from django.core.exceptions import ValidationError 5 | from django.utils.deconstruct import deconstructible 6 | 7 | 8 | @deconstructible 9 | class UsernameValidator(validators.RegexValidator): 10 | 11 | regex = r'^[\da-zA-Z_]{4,15}$' 12 | message = ( 13 | 'Enter a valid username. This value may contain only English letters, ' 14 | 'numbers and underscores, with the length no less than 4 and no less ' 15 | 'than 15.') 16 | flags = re.ASCII 17 | 18 | 19 | class PasswordValidator(object): 20 | 21 | def validate(self, password, user=None): 22 | is_valid = re.search(r'^\w{6,20}$', password) \ 23 | and re.search(r'\d', password) and re.search(r'[a-zA-Z]', password) 24 | 25 | if not is_valid: 26 | raise ValidationError( 27 | "Password should contain both numbers and English letters, " 28 | "with the length no less than 6 and no more than 20.") 29 | -------------------------------------------------------------------------------- /biohub/biobrick/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.biobrick.apps.BiobrickConfig' 2 | -------------------------------------------------------------------------------- /biohub/biobrick/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BiobrickConfig(AppConfig): 5 | name = 'biohub.biobrick' 6 | -------------------------------------------------------------------------------- /biohub/biobrick/exceptions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status, exceptions 2 | 3 | 4 | class SpiderError(Exception): 5 | 6 | def __init__(self, *args, **kwargs): 7 | self.details = self.message(*args, **kwargs) 8 | 9 | def message(self, *args, **kwargs): 10 | return self.error_message_template.format(*args, **kwargs) 11 | 12 | @property 13 | def api_exception(self): 14 | return self.exception_class(self.details) 15 | 16 | 17 | class ResourceNotFoundError(SpiderError): 18 | 19 | error_message_template = 'Resource {} cannot be found on igem offical pages.' 20 | status_code = status.HTTP_404_NOT_FOUND 21 | exception_class = exceptions.NotFound 22 | 23 | 24 | class NetworkError(SpiderError): 25 | 26 | error_message_template = 'Resource {} cannot be fetched due to network problem: {}.' 27 | status_code = status.HTTP_500_INTERNAL_SERVER_ERROR 28 | exception_class = exceptions.APIException 29 | -------------------------------------------------------------------------------- /biohub/biobrick/highlighter.py: -------------------------------------------------------------------------------- 1 | from haystack.utils.highlighting import Highlighter 2 | 3 | 4 | class SimpleHighlighter(Highlighter): 5 | 6 | def render_html(self, highlight_locations=None, start_offset=None, end_offset=None): 7 | # Start by chopping the block down to the proper window. 8 | text = self.text_block 9 | 10 | # Invert highlight_locations to a location -> term list 11 | term_list = [] 12 | 13 | for term, locations in highlight_locations.items(): 14 | term_list += [(loc, term) for loc in locations] 15 | 16 | loc_to_term = sorted(term_list) 17 | 18 | # Prepare the highlight template 19 | if self.css_class: 20 | hl_start = '<%s class="%s">' % (self.html_tag, self.css_class) 21 | else: 22 | hl_start = '<%s>' % (self.html_tag) 23 | 24 | hl_end = '' % self.html_tag 25 | 26 | # Copy the part from the start of the string to the first match, 27 | # and there replace the match with a highlighted version. 28 | highlighted_chunk = text[:start_offset] 29 | matched_so_far = 0 30 | prev = start_offset 31 | prev_str = "" 32 | 33 | for cur, cur_str in loc_to_term: 34 | # This can be in a different case than cur_str 35 | actual_term = text[cur:cur + len(cur_str)] 36 | 37 | # Handle incorrect highlight_locations by first checking for the term 38 | if actual_term.lower() == cur_str: 39 | if cur < prev + len(prev_str): 40 | continue 41 | 42 | highlighted_chunk += text[prev + len(prev_str):cur] + hl_start + actual_term + hl_end 43 | prev = cur 44 | prev_str = cur_str 45 | 46 | # Keep track of how far we've copied so far, for the last step 47 | matched_so_far = cur + len(actual_term) 48 | 49 | # Don't forget the chunk after the last term 50 | highlighted_chunk += text[matched_so_far:] 51 | 52 | return highlighted_chunk 53 | -------------------------------------------------------------------------------- /biohub/biobrick/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/management/__init__.py -------------------------------------------------------------------------------- /biohub/biobrick/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/management/commands/__init__.py -------------------------------------------------------------------------------- /biohub/biobrick/migrations/0002_auto_20170918_2041.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-18 12:41 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | from ..sql.creation.views_creation import migration_sql 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | initial = True 15 | 16 | dependencies = [ 17 | ('forum', '0001_initial'), 18 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 19 | ('biobrick', '0001_initial'), 20 | ] 21 | 22 | operations = [ 23 | migrations.AddField( 24 | model_name='biobrickmeta', 25 | name='document', 26 | field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='forum.Article'), 27 | ), 28 | migrations.AddField( 29 | model_name='biobrickmeta', 30 | name='users_rated', 31 | field=models.ManyToManyField(related_name='bricks_rated', through='biobrick.RatedUser', to=settings.AUTH_USER_MODEL), 32 | ), 33 | migrations.AddField( 34 | model_name='biobrickmeta', 35 | name='users_starred', 36 | field=models.ManyToManyField(related_name='bricks_starred', through='biobrick.StarredUser', to=settings.AUTH_USER_MODEL), 37 | ), 38 | migrations.AddField( 39 | model_name='biobrickmeta', 40 | name='users_watching', 41 | field=models.ManyToManyField(related_name='bricks_watching', through='biobrick.WatchingUser', to=settings.AUTH_USER_MODEL), 42 | ), 43 | migration_sql 44 | ] 45 | -------------------------------------------------------------------------------- /biohub/biobrick/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/biobrick/search_indexes.py: -------------------------------------------------------------------------------- 1 | from haystack import indexes 2 | 3 | from .models import Biobrick 4 | 5 | 6 | class BiobrickIndex(indexes.SearchIndex, indexes.Indexable): 7 | 8 | text = indexes.CharField( 9 | model_attr='index_description', null=True, 10 | document=True, indexed=True 11 | ) 12 | part_name = indexes.CharField( 13 | model_attr='part_name', null=True, 14 | indexed=True, boost=1.25 15 | ) 16 | part_type = indexes.CharField( 17 | model_attr='part_type', null=True, 18 | indexed=True, boost=1.25 19 | ) 20 | author = indexes.CharField( 21 | model_attr='author', null=True, 22 | indexed=True, boost=1.25 23 | ) 24 | creation_date = indexes.DateField(model_attr='creation_date', null=True) 25 | weight = indexes.FloatField(model_attr='weight', null=True) 26 | stars = indexes.IntegerField(model_attr='stars', null=True) 27 | watches = indexes.IntegerField(model_attr='watches', null=True) 28 | rate_score = indexes.IntegerField(model_attr='rate_score', null=True) 29 | uses = indexes.IntegerField(model_attr='uses') 30 | 31 | def get_model(self): 32 | return Biobrick 33 | 34 | def get_updated_field(self): 35 | return 'weight_updated_time' 36 | 37 | def index_queryset(self, using=None): 38 | return self.get_model().objects.only('short_desc', 'part_name', 'part_type', 'creation_date', 'weight', 'author', 'uses', 'stars', 'watches', 'rate_score') 39 | 40 | def __str__(self): 41 | return '' 42 | -------------------------------------------------------------------------------- /biohub/biobrick/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/sql/__init__.py -------------------------------------------------------------------------------- /biohub/biobrick/sql/creation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/sql/creation/__init__.py -------------------------------------------------------------------------------- /biohub/biobrick/sql/creation/views_creation.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from django.db import migrations 3 | 4 | with open(path.join(path.dirname(__file__), 'views_creation.sql'), 'r') as f: 5 | migration_sql = migrations.RunSQL(f.read()) 6 | -------------------------------------------------------------------------------- /biohub/biobrick/sql/creation/views_creation.sql: -------------------------------------------------------------------------------- 1 | DROP VIEW IF EXISTS parts; 2 | CREATE VIEW parts AS SELECT * from igem.parts_filtered; 3 | DROP VIEW IF EXISTS biobricks; 4 | CREATE VIEW biobricks AS 5 | SELECT 6 | w.weight as weight, 7 | w.weight_updated_time as weight_updated_time, 8 | m.group_name as group_name, 9 | m.experience_status as experience_status, 10 | m.twin_num as twin_num, 11 | m.parameters as parameters, 12 | m.rates as rates, 13 | m.rate_score as rate_score, 14 | m.stars as stars, 15 | m.document_id as document_id, 16 | m.watches as watches, 17 | m.last_fetched as last_fetched, 18 | p.part_id as part_id, 19 | p.ok as ok, 20 | p.part_name as part_name, 21 | p.short_desc as short_desc, 22 | p.description as description, 23 | p.part_type as part_type, 24 | p.author as author, 25 | p.status as status, 26 | p.dominant as dominant, 27 | p.part_status as part_status, 28 | p.sample_status as sample_status, 29 | p.creation_date as creation_date, 30 | p.uses as uses, 31 | p.works as works, 32 | p.favorite as favorite, 33 | p.has_barcode as has_barcode, 34 | p.nickname as nickname, 35 | p.categories as categories, 36 | p.sequence as sequence, 37 | p.sequence_length as sequence_length, 38 | p.review_count as review_count, 39 | p.review_total as review_total, 40 | p.ac as ac, 41 | p.ruler as ruler 42 | FROM 43 | igem.parts_filtered as p 44 | LEFT JOIN biobrick_biobrickmeta as m ON m.part_name = p.part_name 45 | LEFT JOIN biobrick_biobrickweight as w ON w.part_name = p.part_name; 46 | -------------------------------------------------------------------------------- /biohub/biobrick/sql/weight/fetch.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | p.part_name as part_name, 3 | p.favorite as favorite, 4 | p.has_barcode as has_barcode, 5 | p.status_w as status_w, 6 | p.sample_status_w as sample_status_w, 7 | p.works_w as works_w, 8 | p.uses_w as uses_w, 9 | p.review_total_w as review_total_w, 10 | p.review_count_w as review_count_w, 11 | p.has_subpart as has_subpart, 12 | p.deep_count_w as deep_count_w, 13 | p.ac_w as ac_w, 14 | m.rates as rates, 15 | m.rate_score as rate_score, 16 | m.stars as stars, 17 | m.watches as watches, 18 | w.weight as old_weight 19 | FROM 20 | igem.parts_filtered as p 21 | LEFT JOIN biobrick_biobrickmeta as m ON p.part_name = m.part_name 22 | LEFT JOIN biobrick_biobrickweight as w ON w.part_name = p.part_name; 23 | -------------------------------------------------------------------------------- /biohub/biobrick/urls.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biobrick/urls.py -------------------------------------------------------------------------------- /biohub/biocircuit/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'ctyi & zml' 2 | 3 | default_app_config = 'biohub.biocircuit.apps.BiocircuitConfig' 4 | -------------------------------------------------------------------------------- /biohub/biocircuit/apps.py: -------------------------------------------------------------------------------- 1 | from biohub.core.plugins import PluginConfig 2 | 3 | 4 | class BiocircuitConfig(PluginConfig): 5 | 6 | name = 'biohub.biocircuit' 7 | title = 'Biocircuit' 8 | author = 'zml, wxq' 9 | description = 'The design of robust and sensitive biological circuit are always bothering. To rationally design synthetic gene circuits with specific functions, we write BioCircuit. The plugin is an adpatation of the project BioBLESS developed by USTC-Software 2015, with the ability to design digital circuit, biological circuit as well as analyze the performance of them.' 10 | js_url = 'https://ustc-software2017-frontend.github.io/biohub-plugin-bioBLESS/build/plugin.js' 11 | -------------------------------------------------------------------------------- /biohub/biocircuit/compile-espresso.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import platform 4 | 5 | sysstr = sys.platform 6 | dirname = os.path.dirname(os.path.abspath(__file__)) 7 | a, b, *c = platform.python_version().split('.') 8 | 9 | 10 | def make_path(*args): 11 | return os.path.join(dirname, *args) 12 | 13 | 14 | if sysstr == "linux" or sysstr == "linux2" or sysstr == "darwin": 15 | commands = ['gcc', '-w', make_path('espresso', '*.c'), 16 | '-fPIC', '-shared', 17 | '-o', make_path('espresso.so'), 18 | '-lpython{a}.{b}m', 19 | '-I/usr/local/include/python{a}.{b}m/', 20 | '-I/usr/include/python{a}.{b}m/'] 21 | elif sysstr in ["win32", "win64"]: 22 | commands = ['gcc', '-w', make_path('espresso', '*.c'), 23 | '-mdll', 24 | '-D', 'MS_{}'.format(sysstr.upper()), 25 | '-o', make_path('espresso.pyd'), 26 | '-lpython{a}{b}', 27 | r'-IC:\Python{a}{b}\include', 28 | r'-LC:\Python{a}{b}\libs'] 29 | else: 30 | print('Unsupported OS!') 31 | sys.exit(1) 32 | 33 | if __name__ == '__main__': 34 | sys.exit( 35 | os.system(' '.join(part.format(**globals()) for part in commands)) 36 | ) 37 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/backup/canonical.c: -------------------------------------------------------------------------------- 1 | // Filename: canonical.c 2 | // 3 | // Routines for finding the canonical cover of the incompletely specified 4 | // logic function. 5 | // 6 | // Routine: 7 | // set_family_t *find_canonical_cover(): 8 | // Finds canonical cover of the incompletely specified logic function 9 | // by iteratively calling ess_test_and_reduction for each cube in the 10 | // ON-set. 11 | // 12 | 13 | #include "espresso.h" 14 | #include "signature.h" 15 | 16 | // 17 | // find_canonical_cover 18 | // 19 | // Objective: find canonical cover of the essential signature cube 20 | // Input: 21 | // F: ONSET cover; 22 | // D: DC cover; 23 | // R: OFFSET cover; 24 | // Output: 25 | // Return canonical cover of the essential signature cube 26 | // 27 | 28 | set_family_t * 29 | find_canonical_cover(set_family_t *F1, set_family_t *D, set_family_t *R) 30 | { 31 | set_family_t *F; 32 | set_family_t *E, *ESC; 33 | set_family_t *COVER; 34 | set *last, *p, *s; 35 | set *c; 36 | int count = 0; 37 | int last_fcount = F1->count; 38 | set *d, **extended_dc; 39 | set *sigma_c; 40 | 41 | F = sf_save(F1); 42 | E = sf_new(D->count, cube.size); 43 | E->count = D->count; 44 | sf_copy(E, D); 45 | 46 | ESC = sf_new(F->count, cube.size); 47 | 48 | while (F->count) { 49 | c = GETSET(F, --F->count); 50 | RESET(c,NONESSEN); 51 | extended_dc = cube2list(E, F); 52 | d = reduce_cube(extended_dc, c); 53 | free_cubelist(extended_dc); 54 | if (setp_empty(d)) { 55 | set_free(d); 56 | continue; 57 | } 58 | c = get_sigma(R, d); 59 | COVER = etr_order(F, E, R, c, d); 60 | set_free(d); 61 | if (TESTP(c, NONESSEN)) { 62 | sf_append(F, COVER); 63 | } else { 64 | sf_free(COVER); 65 | sf_addset(E, c); 66 | sf_addset(ESC, c); 67 | } 68 | set_free(c); 69 | } 70 | sf_free(F); 71 | sf_free(E); 72 | 73 | return ESC; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/backup/signature.h: -------------------------------------------------------------------------------- 1 | // Filename: signature.h 2 | 3 | /* black_white.c */ 4 | void setup_bw(); 5 | void free_bw(); 6 | int black_white(); 7 | void split_list(); 8 | void merge_list(); 9 | void print_bw(); 10 | void variable_list_alloc(); 11 | void variable_list_delete(); 12 | void variable_list_insert(); 13 | int variable_list_empty(); 14 | void get_next_variable(); 15 | void print_variable_list(); 16 | 17 | /* canonical.c */ 18 | int is_minterm(); 19 | set_family_t *find_canonical_cover(); 20 | 21 | /* essentiality.c */ 22 | set_family_t *etr_order(); 23 | void aux_etr_order(); 24 | set_family_t *get_mins(); 25 | int ascending(); 26 | 27 | /* sigma.c */ 28 | set *get_sigma(); 29 | void set_not(); 30 | 31 | /* signature.c */ 32 | void cleanup(); 33 | set_family_t *signature(); 34 | set_family_t *generate_primes(); 35 | 36 | /* signature_exact.c */ 37 | set_family_t *signature_minimize_exact(); 38 | sm_matrix *signature_form_table(); 39 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/backup/signature_exact.c: -------------------------------------------------------------------------------- 1 | #include "espresso.h" 2 | #include "signature.h" 3 | 4 | 5 | /* 6 | * signature_minimize_exact: 7 | * What does it do: forms and solves the covering table whose rows are 8 | * essential signature cubes (ESCubes) and whose columns are 9 | * union of essential signature sets (ESSet) 10 | * Input: 11 | * ESCubes: essential signature cubes 12 | * ESSet: union of essential signature sets 13 | * Output: 14 | * COVER: exact cover 15 | */ 16 | 17 | set_family_t * 18 | signature_minimize_exact(ESCubes,ESSet) 19 | set_family_t *ESCubes, *ESSet; 20 | { 21 | set *p; 22 | sm_matrix *table; 23 | sm_row *cover; 24 | sm_element *pe; 25 | set_family_t *COVER; 26 | int index; 27 | int *weights,heur,level; 28 | 29 | /* number ESCubes, ESSet */ 30 | foreachi_set(ESCubes,index,p){ 31 | PUTSIZE(p,index); 32 | } 33 | foreachi_set(ESSet,index,p){ 34 | PUTSIZE(p,index); 35 | } 36 | 37 | /* form the covering table */ 38 | table = signature_form_table(ESCubes, ESSet); 39 | 40 | /* solve the covering problem */ 41 | weights = NIL(int); heur = FALSE; level = 0; 42 | cover = sm_minimum_cover(table,weights,heur,level); 43 | 44 | /* form the cover */ 45 | COVER = sf_new(100, cube.size); 46 | sm_foreach_row_element(cover, pe) { 47 | COVER = sf_addset(COVER, GETSET(ESSet, pe->col_num)); 48 | } 49 | 50 | sm_free(table); 51 | sm_row_free(cover); 52 | 53 | return COVER; 54 | } 55 | 56 | sm_matrix * 57 | signature_form_table(ESCubes, ESSet) 58 | set_family_t *ESCubes, *ESSet; 59 | { 60 | sm_matrix *table; 61 | int row,column; 62 | set *c, *p; 63 | int col_deleted; 64 | 65 | table = sm_alloc(); 66 | 67 | col_deleted = 0; 68 | foreachi_set(ESSet,column,p){ 69 | if(column%1000 == 0){ 70 | col_deleted += sm_col_dominance(table,NULL); 71 | } 72 | foreachi_set(ESCubes,row,c){ 73 | if(setp_implies(c,p)){ 74 | sm_insert(table,row,column); 75 | } 76 | } 77 | } 78 | col_deleted += sm_col_dominance(table,NULL); 79 | 80 | return table; 81 | } 82 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/example.txt: -------------------------------------------------------------------------------- 1 | .i 3 2 | .o 1 3 | 000 1 4 | 001 0 5 | 010 0 6 | 011 1 7 | 100 0 8 | 101 1 9 | 110 1 10 | 111 0 11 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/mincov_int.h: -------------------------------------------------------------------------------- 1 | // Filename: mincov_int.h 2 | 3 | #include "utility.h" 4 | #include "sparse.h" 5 | 6 | typedef struct stats_struct stats_t; 7 | struct stats_struct { 8 | int debug; /* 1 if debugging is enabled */ 9 | int max_print_depth; /* dump stats for levels up to this level */ 10 | int max_depth; /* deepest the recursion has gone */ 11 | int nodes; /* total nodes visited */ 12 | int component; /* currently solving a component */ 13 | int comp_count; /* number of components detected */ 14 | int gimpel_count; /* number of times Gimpel reduction applied */ 15 | int gimpel; /* currently inside Gimpel reduction */ 16 | long start_time; /* cpu time when the covering started */ 17 | int no_branching; 18 | int lower_bound; 19 | }; 20 | 21 | typedef struct solution_struct solution_t; 22 | struct solution_struct { 23 | sm_row *row; 24 | int cost; 25 | }; 26 | 27 | solution_t *solution_alloc(void); 28 | void solution_free(solution_t *sol); 29 | solution_t *solution_dup(solution_t *sol); 30 | void solution_accept(solution_t *sol, sm_matrix *A, int *weight, int col); 31 | void solution_reject(solution_t *sol, sm_matrix *A, int *weight, int col); 32 | void solution_add(solution_t *sol, int *weight, int col); 33 | solution_t *solution_choose_best(solution_t *best1, solution_t *best2); 34 | 35 | solution_t *sm_maximal_independent_set(sm_matrix *A, int *weight); 36 | solution_t *sm_mincov(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats); 37 | int gimpel_reduce(sm_matrix *A, solution_t *select, int *weight, int lb, int bound, int depth, stats_t *stats, solution_t **best); 38 | 39 | #define WEIGHT(weight, col) (weight == NIL(int) ? 1 : weight[col]) 40 | 41 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/sminterf.c: -------------------------------------------------------------------------------- 1 | // Filename: sminterf.c 2 | 3 | #include "espresso.h" 4 | 5 | set * 6 | do_sm_minimum_cover(set_family_t *A) 7 | { 8 | sm_matrix *M; 9 | sm_row *sparse_cover; 10 | sm_element *pe; 11 | set *cover; 12 | int i, base, rownum; 13 | unsigned val; 14 | set *last, *p; 15 | 16 | M = sm_alloc(); 17 | rownum = 0; 18 | foreach_set(A, last, p) { 19 | foreach_set_element(p, i, val, base) { 20 | sm_insert(M, rownum, base); 21 | } 22 | rownum++; 23 | } 24 | 25 | sparse_cover = sm_minimum_cover(M, NIL(int), 1, 0); 26 | sm_free(M); 27 | 28 | cover = set_new(A->sf_size); 29 | sm_foreach_row_element(sparse_cover, pe) { 30 | set_insert(cover, pe->col_num); 31 | } 32 | sm_row_free(sparse_cover); 33 | 34 | return cover; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /biohub/biocircuit/espresso/utility.h: -------------------------------------------------------------------------------- 1 | // Filename: utility.h 2 | 3 | #ifndef UTILITY_H 4 | #define UTILITY_H 5 | 6 | #include 7 | 8 | #define NIL(type) ((type *) 0) 9 | 10 | #define ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) 11 | 12 | #define REALLOC(type, obj, num) \ 13 | (obj) ? ((type *) realloc((char *) obj, sizeof(type) * (num))) \ 14 | : ((type *) malloc(sizeof(type) * (num))) 15 | 16 | #define FREE(obj) if ((obj)) { free((char *) (obj)); (obj) = 0; } 17 | 18 | #ifndef MAX 19 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) 20 | #endif 21 | 22 | #ifndef MIN 23 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 24 | #endif 25 | 26 | #ifndef ABS 27 | #define ABS(a) ((a) > 0 ? (a) : -(a)) 28 | #endif 29 | 30 | #endif // UTILITY_H 31 | 32 | -------------------------------------------------------------------------------- /biohub/biocircuit/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biocircuit/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/biocircuit/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from biohub.core.routes import register_api 4 | 5 | # Place your route definition here. 6 | 7 | from . import views 8 | 9 | register_api(r'^biocircuit/', [ 10 | url(r'^biocircuit/(?P.+)/$', views.BiocircuitView.as_view(), name='biocircuit-build'), # the name seems wierd. 11 | url(r'^gates/$', views.GatesView.as_view(), name='biocircuit-gates'), 12 | url(r'^score/$', views.ScoreView.as_view(), name='biocircuit-score'), 13 | ], 'biocircuit') 14 | -------------------------------------------------------------------------------- /biohub/biomap/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.biomap.apps.BiomapConfig' 2 | -------------------------------------------------------------------------------- /biohub/biomap/apps.py: -------------------------------------------------------------------------------- 1 | from biohub.core.plugins import PluginConfig 2 | 3 | 4 | class BiomapConfig(PluginConfig): 5 | 6 | name = 'biohub.biomap' 7 | title = 'BioMap' 8 | author = 'hsfzxjy' 9 | description = 'A tool to analyze relationships between bricks. Just input the full name of a specific part (e.g. BBa_B0015) and click the corresponding button, the plugin will show you the relationship between it and other parts.' 10 | js_url = 'https://ustc-software2017-frontend.github.io/biohub-plugin-BioMap/build/plugin.js' 11 | -------------------------------------------------------------------------------- /biohub/biomap/builder.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class RelationshipBuilder: 4 | 5 | def __init__(self): 6 | from biohub.biomap.storage import storage 7 | 8 | self._storage = storage 9 | 10 | def build(self, part_name, subparts=None, force=False): 11 | 12 | if not force and self._storage.exists(part_name) and self._storage.exists('rev_' + part_name): 13 | return 14 | 15 | if subparts is None: 16 | from biohub.biobrick.models import Part 17 | 18 | try: 19 | part = Part.objects.get(part_name=part_name) 20 | except Part.DoesNotExist: 21 | return False 22 | 23 | subparts = part.ruler['sub_parts'] 24 | 25 | return self._build(part_name, subparts) 26 | 27 | def _build(self, part_name, subparts): 28 | 29 | self._storage.sadd(part_name, 0) 30 | self._storage.sadd('rev_' + part_name, 0) 31 | 32 | for part in subparts: 33 | 34 | name = 'BBa_' + part['short_name'] 35 | self._storage.sadd(part_name, name) 36 | self._storage.sadd('rev_' + name, part_name) 37 | 38 | 39 | builder = RelationshipBuilder() 40 | -------------------------------------------------------------------------------- /biohub/biomap/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biomap/management/__init__.py -------------------------------------------------------------------------------- /biohub/biomap/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biomap/management/commands/__init__.py -------------------------------------------------------------------------------- /biohub/biomap/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biomap/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/biomap/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /biohub/biomap/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers # noqa 2 | 3 | # Create your serializers here. 4 | -------------------------------------------------------------------------------- /biohub/biomap/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/biomap/sql/__init__.py -------------------------------------------------------------------------------- /biohub/biomap/sql/fetch_bricks.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | part_name, 3 | ruler 4 | FROM 5 | igem.parts_filtered; 6 | -------------------------------------------------------------------------------- /biohub/biomap/storage.py: -------------------------------------------------------------------------------- 1 | from biohub.utils.redis import Storage 2 | 3 | storage = Storage('__biohub_biomap__') 4 | -------------------------------------------------------------------------------- /biohub/biomap/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_api, register_default, url # noqa 2 | 3 | from .views import analyze_reverse, analyze 4 | 5 | register_api(r'^biomap/', [ 6 | url(r'^(?PBBa_\w+)/analyze/$', analyze), 7 | url(r'^(?PBBa_\w+)/analyze_reverse/$', analyze_reverse), 8 | ], 'biomap') 9 | -------------------------------------------------------------------------------- /biohub/biomap/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.decorators import api_view 2 | from rest_framework.response import Response 3 | from .analyzer import analyzer 4 | 5 | 6 | @api_view(['GET']) 7 | def analyze(request, part_name): 8 | result = analyzer.analyze(part_name) 9 | 10 | return Response({ 11 | 'nodes': [dict(id=name, group=group) for name, group in result.nodes.items()], 12 | 'edges': result.edges, 13 | 'too_large': result.too_large 14 | }) 15 | 16 | 17 | @api_view(['GET']) 18 | def analyze_reverse(request, part_name): 19 | 20 | result = analyzer.analyze_reverse(part_name) 21 | 22 | return Response({ 23 | 'nodes': [dict(id=name, group=group) for name, group in result.nodes.items()], 24 | 'edges': result.edges, 25 | 'too_large': result.too_large 26 | }) 27 | -------------------------------------------------------------------------------- /biohub/biomap/ws_handlers.py: -------------------------------------------------------------------------------- 1 | from biohub.core.websocket import register_handler # noqa 2 | 3 | # Place your websocket handlers here. 4 | -------------------------------------------------------------------------------- /biohub/compat/__init__.py: -------------------------------------------------------------------------------- 1 | from . import db, warnings, redis, rest, channel_worker # noqa 2 | -------------------------------------------------------------------------------- /biohub/compat/channel_worker.py: -------------------------------------------------------------------------------- 1 | import signal 2 | from channels import worker 3 | 4 | 5 | def _safe_register(signum, handler): 6 | 7 | old_handler = signal.getsignal(signum) 8 | 9 | if callable(old_handler): 10 | 11 | def new_handler(*args, **kwargs): 12 | old_handler(*args, **kwargs) 13 | handler(*args, **kwargs) 14 | 15 | signal.signal(signum, new_handler) 16 | else: 17 | signal.signal(signum, handler) 18 | 19 | 20 | def patched_install_signal_handler(self): 21 | _safe_register(signal.SIGTERM, self.sigterm_handler) 22 | _safe_register(signal.SIGINT, self.sigterm_handler) 23 | 24 | 25 | worker.Worker.install_signal_handler = worker.WorkerGroup.install_signal_handler = patched_install_signal_handler 26 | -------------------------------------------------------------------------------- /biohub/compat/db.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module aliases 'pymysql' to 'MySQLdb', since django only uses 'MySQLdb' 3 | for database connection. 4 | """ 5 | 6 | import sys 7 | import pymysql 8 | 9 | sys.modules['MySQLdb'] = pymysql 10 | -------------------------------------------------------------------------------- /biohub/compat/redis.py: -------------------------------------------------------------------------------- 1 | try: 2 | import django_redis # noqa 3 | except ModuleNotFoundError: 4 | import warnings 5 | import sys 6 | 7 | warnings.warn( 8 | 'Module `django_redis` is not available, ' 9 | '**Tasks utility is disabled.**', 10 | RuntimeWarning) 11 | 12 | class Mock(object): 13 | 14 | def __getattr__(self, name): 15 | 16 | if name == '__file__': 17 | raise AttributeError 18 | 19 | return Mock() 20 | 21 | def __call__(self, *args, **kwargs): 22 | return Mock() 23 | 24 | sys.modules['django_redis'] = Mock() 25 | -------------------------------------------------------------------------------- /biohub/compat/rest.py: -------------------------------------------------------------------------------- 1 | from urllib import parse as urlparse 2 | from rest_framework.utils import urls 3 | 4 | 5 | def replace_query_param(url, key, val): 6 | """ 7 | Hacked this function to drop scheme and netloc. 8 | """ 9 | (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) 10 | query_dict = urlparse.parse_qs(query, keep_blank_values=True) 11 | query_dict[key] = [val] 12 | query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) 13 | return urlparse.urlunsplit(('', '', path, query, fragment)) 14 | 15 | 16 | def remove_query_param(url, key): 17 | """ 18 | Hacked this function to drop scheme and netloc. 19 | """ 20 | (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) 21 | query_dict = urlparse.parse_qs(query, keep_blank_values=True) 22 | query_dict.pop(key, None) 23 | query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) 24 | return urlparse.urlunsplit(('', '', path, query, fragment)) 25 | 26 | 27 | urls.replace_query_param = replace_query_param 28 | urls.remove_query_param = remove_query_param 29 | -------------------------------------------------------------------------------- /biohub/compat/warnings.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is to better print out warning message. It: 3 | 4 | + Switches on warnings capture in logging system. 5 | + Monkey patches `warnings._formatwarnmsg_impl` to override default warning 6 | message format. 7 | + Sets up logging handler `py.warnings` to output warning messages. 8 | """ 9 | 10 | import warnings 11 | import logging 12 | import logging.config 13 | 14 | logging.captureWarnings(True) 15 | 16 | RESET_SEQ = "\033[0m" 17 | RED_SEQ = "\033[;31m" 18 | BOLD_SEQ = "\033[1m" 19 | 20 | 21 | def _formatwarnmsg_impl(msg): 22 | 23 | return '%s%s%s: %s%s' % ( 24 | RED_SEQ, BOLD_SEQ, 25 | msg.category.__name__, msg.message, 26 | RESET_SEQ) 27 | 28 | 29 | warnings._formatwarnmsg_impl = _formatwarnmsg_impl 30 | 31 | logging.config.dictConfig({ 32 | 'version': 1, 33 | 'handlers': { 34 | 'console': { 35 | 'class': 'logging.StreamHandler' 36 | } 37 | }, 38 | 'loggers': { 39 | 'py.warnings': { 40 | 'handlers': ['console'], 41 | 'level': 'WARNING' 42 | } 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /biohub/core/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.core.apps.CoreConfig' 2 | -------------------------------------------------------------------------------- /biohub/core/app_template/__init__.py-tpl: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.{{ app_name }}.apps.{{ camel_case_app_name }}Config' 2 | -------------------------------------------------------------------------------- /biohub/core/app_template/apps.py-tpl: -------------------------------------------------------------------------------- 1 | {{ unicode_literals }}from django.apps import AppConfig 2 | 3 | 4 | class {{ camel_case_app_name }}Config(AppConfig): 5 | name = 'biohub.{{ app_name }}' 6 | -------------------------------------------------------------------------------- /biohub/core/app_template/migrations/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/app_template/migrations/__init__.py-tpl -------------------------------------------------------------------------------- /biohub/core/app_template/models.py-tpl: -------------------------------------------------------------------------------- 1 | {{ unicode_literals }}from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /biohub/core/app_template/serializers.py-tpl: -------------------------------------------------------------------------------- 1 | {{ unicode_literals }}from rest_framework import serializers 2 | 3 | # Create your serializers here. 4 | -------------------------------------------------------------------------------- /biohub/core/app_template/urls.py-tpl: -------------------------------------------------------------------------------- 1 | {{ unicode_literals }}from biohub.core.routes import register_api, register_default, url 2 | 3 | # Place your route definition here. 4 | -------------------------------------------------------------------------------- /biohub/core/app_template/views.py-tpl: -------------------------------------------------------------------------------- 1 | {{ unicode_literals }}from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /biohub/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | 6 | name = 'biohub.core' 7 | label = 'biohub_core' 8 | 9 | def ready(self): 10 | from biohub.core.conf.signal import register 11 | from biohub.core.plugins.ipc_slave import bridge 12 | 13 | bridge.register() 14 | register() 15 | -------------------------------------------------------------------------------- /biohub/core/channel_routing.py: -------------------------------------------------------------------------------- 1 | from channels.routing import route 2 | 3 | from biohub.core.websocket.consumers import MainConsumer 4 | from biohub.core.tasks.consumers import task_consumer 5 | 6 | channels_routing = [ 7 | MainConsumer.as_route(path=r'^/ws/'), 8 | route('task', task_consumer) 9 | ] 10 | -------------------------------------------------------------------------------- /biohub/core/conf/signal.py: -------------------------------------------------------------------------------- 1 | import signal 2 | import warnings 3 | 4 | from biohub.core.conf import load_config 5 | from biohub.core.plugins import plugins 6 | 7 | 8 | def reload_handler(signum, frame): 9 | """ 10 | To reload config files and corresponding components when received SIGUSR1 11 | signal. 12 | """ 13 | load_config() 14 | plugins.reload_plugins() 15 | 16 | 17 | def register(): 18 | if not hasattr(signal, 'SIGUSR1'): 19 | warnings.warn( 20 | "Your system doesn't support SIGUSR1, and thus biohub cannot " 21 | "reload itself at runtime.", RuntimeWarning) 22 | return 23 | 24 | signal.signal(signal.SIGUSR1, reload_handler) 25 | -------------------------------------------------------------------------------- /biohub/core/files/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.core.files.apps.FilesConfig' 2 | -------------------------------------------------------------------------------- /biohub/core/files/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FilesConfig(AppConfig): 5 | 6 | name = 'biohub.core.files' 7 | -------------------------------------------------------------------------------- /biohub/core/files/handlers.py: -------------------------------------------------------------------------------- 1 | from biohub.core.files import utils, models 2 | 3 | 4 | def handle_permanent_file(request, field_name='file'): 5 | """ 6 | Handles and stores the uploaded file to both file system and database, 7 | returns a File model instance. 8 | """ 9 | return models.File.objects.create_from_file(request.FILES[field_name]) 10 | 11 | 12 | def handle_temporary_file(request, field_name='file'): 13 | """ 14 | Handles and stores the uploaded file to temporary directory. 15 | 16 | NOTE THAT the file will be erased once closed. 17 | """ 18 | return utils.store_temp_file(request.FILES[field_name]) 19 | -------------------------------------------------------------------------------- /biohub/core/files/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-18 12:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='File', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('file', models.FileField(upload_to='')), 21 | ('mime_type', models.CharField(max_length=50)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /biohub/core/files/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/files/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/core/files/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from biohub.core.files.utils import store_file 4 | 5 | 6 | class FileManager(models.Manager): 7 | 8 | def create_from_file(self, fd): 9 | """ 10 | Creates and returns an instance with a file-like object `fd`. 11 | """ 12 | name, mime_type = store_file(fd) 13 | 14 | return self.create( 15 | mime_type=mime_type, 16 | file=name) 17 | 18 | 19 | class File(models.Model): 20 | 21 | file = models.FileField() 22 | mime_type = models.CharField(max_length=50) 23 | 24 | objects = FileManager() 25 | -------------------------------------------------------------------------------- /biohub/core/files/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from biohub.utils.rest.serializers import bind_model, ModelSerializer 3 | from .models import File 4 | 5 | 6 | @bind_model(File) 7 | class FileSerializer(ModelSerializer): 8 | 9 | name = serializers.SerializerMethodField() 10 | 11 | class Meta: 12 | model = File 13 | fields = '__all__' 14 | extra_kwargs = { 15 | 'file': { 16 | 'use_url': True 17 | } 18 | } 19 | 20 | def get_name(self, obj): 21 | import re 22 | from functools import partial 23 | 24 | sub = partial(re.sub, r'\_[a-zA-Z0-9]{7}$', '') 25 | 26 | basename = obj.file.name 27 | if '.' not in basename: 28 | return sub(basename) 29 | else: 30 | base, dot, ext = basename.rpartition('.') 31 | return ''.join((sub(base), dot, ext)) 32 | -------------------------------------------------------------------------------- /biohub/core/files/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_default, url 2 | from .views import upload 3 | 4 | register_default(r'^files/', [ 5 | url(r'^upload/$', upload, name='upload') 6 | ], namespace='files') 7 | -------------------------------------------------------------------------------- /biohub/core/files/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import tempfile 4 | import os.path 5 | import functools 6 | import mimetypes 7 | 8 | from django.conf import settings 9 | from django.core.files.storage import default_storage 10 | from django.core.files import File as FileWrapper 11 | 12 | 13 | def url_to_filename(url): 14 | 15 | try: 16 | return re.findall(r'{}(.+)$'.format(settings.MEDIA_URL), url)[0] 17 | except IndexError: 18 | return None 19 | 20 | 21 | def stash_file_pos(func): 22 | """ 23 | A decorator to ensure the file-like object passed in will be reset to its 24 | original position when the function returns. 25 | """ 26 | 27 | @functools.wraps(func) 28 | def inner(fd): 29 | 30 | current_pos = fd.tell() 31 | result = func(fd) 32 | fd.seek(current_pos) 33 | 34 | return result 35 | 36 | return inner 37 | 38 | 39 | @stash_file_pos 40 | def store_file(fd): 41 | """ 42 | Given a file-like object and stores it with default storage system. 43 | 44 | Returns a tuple (file_name, mime_type), where the file name is relative to 45 | MEDIA_ROOT. 46 | """ 47 | 48 | name = default_storage.save(os.path.basename(fd.name), fd) 49 | 50 | return name, mimetypes.guess_type(name)[0] or '' 51 | 52 | 53 | @stash_file_pos 54 | def store_temp_file(fd): 55 | """ 56 | Given a file-like object and dumps it to the temporary directory. 57 | 58 | Returns the temporary file object. 59 | """ 60 | temp_file = tempfile.NamedTemporaryFile( 61 | encoding=getattr(fd, 'encoding', None)) 62 | 63 | source = FileWrapper(fd) 64 | for chunk in source.chunks(): 65 | temp_file.write(chunk) 66 | 67 | temp_file.seek(0) 68 | 69 | return temp_file 70 | -------------------------------------------------------------------------------- /biohub/core/files/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.decorators import permission_classes, api_view 2 | from rest_framework.response import Response 3 | 4 | from biohub.utils.rest.permissions import IsAuthenticated 5 | from biohub.core.files.serializers import FileSerializer 6 | from biohub.core.files.handlers import handle_permanent_file 7 | from biohub.core.files.utils import store_file, default_storage 8 | 9 | 10 | @api_view(['POST']) 11 | @permission_classes([IsAuthenticated]) 12 | def upload(request): 13 | 14 | store_db = request.GET.get('store_db', False) 15 | 16 | if isinstance(store_db, str) and store_db.lower() in ('false', 'no'): 17 | store_db = False 18 | 19 | if store_db: 20 | instance = handle_permanent_file(request, 'file') 21 | data = FileSerializer(instance).data 22 | else: 23 | name, mime_type = store_file(request.FILES['file']) 24 | data = dict(file=default_storage.url(name), 25 | mime_type=mime_type) 26 | 27 | return Response(data) 28 | -------------------------------------------------------------------------------- /biohub/core/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/management/__init__.py -------------------------------------------------------------------------------- /biohub/core/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/management/commands/__init__.py -------------------------------------------------------------------------------- /biohub/core/management/commands/codestyle.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as path 3 | import sys 4 | 5 | from django.core.management import BaseCommand 6 | from django.conf import settings 7 | 8 | PATH_TO_CHECK = path.join(settings.BIOHUB_DIR, '..') 9 | 10 | 11 | class Command(BaseCommand): 12 | 13 | def handle(self, *args, **kwargs): 14 | cmd = os.system(' '.join(['flake8', PATH_TO_CHECK])) 15 | 16 | if os.WEXITSTATUS(cmd): 17 | sys.exit(1) 18 | -------------------------------------------------------------------------------- /biohub/core/management/commands/startapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import errno 3 | import os.path 4 | 5 | from django.core.management.commands.startapp import Command as StartappCommand 6 | from django.core.management import CommandError 7 | 8 | 9 | from django.conf import settings 10 | 11 | 12 | class Command(StartappCommand): 13 | 14 | def handle(self, **options): 15 | app_name, target = options.pop('name'), options.pop('directory') 16 | 17 | # Set the top directory to root of Biohub instead of current 18 | # path. 19 | if target is None: 20 | target = os.path.join(settings.BIOHUB_DIR, app_name) 21 | 22 | try: 23 | os.makedirs(target) 24 | except OSError as e: 25 | if e.errno == errno.EEXIST: 26 | message = "'%s' already exists" % target 27 | else: 28 | message = e 29 | raise CommandError(message) 30 | 31 | # Use custom app template 32 | if options['template'] is None: 33 | options['template'] = os.path.join( 34 | settings.BIOHUB_CORE_DIR, 'app_template') 35 | 36 | super(StartappCommand, self).handle('app', app_name, target, **options) 37 | -------------------------------------------------------------------------------- /biohub/core/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/models.py -------------------------------------------------------------------------------- /biohub/core/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import PluginConfig # noqa: F401 2 | from .registry import manager as plugins 3 | 4 | install = plugins.install 5 | remove = plugins.remove 6 | prepare_database = plugins.prepare_database 7 | 8 | default_app_config = 'biohub.core.plugins.apps.PluginsConfig' 9 | -------------------------------------------------------------------------------- /biohub/core/plugins/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PluginsConfig(AppConfig): 5 | 6 | label = 'biohub_plugins' 7 | name = 'biohub.core.plugins' 8 | 9 | def ready(self): 10 | from biohub.core.plugins import plugins 11 | 12 | plugins.populate_plugins() 13 | -------------------------------------------------------------------------------- /biohub/core/plugins/config.py: -------------------------------------------------------------------------------- 1 | from django.apps.config import AppConfig as BaseConfig 2 | 3 | 4 | class PluginConfig(BaseConfig): 5 | pass 6 | -------------------------------------------------------------------------------- /biohub/core/plugins/exceptions.py: -------------------------------------------------------------------------------- 1 | class PluginError(Exception): 2 | 3 | def __init__(self, original_exception): 4 | self.original_exception = original_exception 5 | 6 | def __str__(self): 7 | return str(self.original_exception) 8 | 9 | 10 | class RemovalError(PluginError): 11 | pass 12 | 13 | 14 | class InstallationError(PluginError): 15 | pass 16 | 17 | 18 | class DatabaseError(RemovalError, InstallationError): 19 | 20 | pass 21 | 22 | 23 | class URLConfError(RemovalError, InstallationError): 24 | 25 | pass 26 | -------------------------------------------------------------------------------- /biohub/core/plugins/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/plugins/management/__init__.py -------------------------------------------------------------------------------- /biohub/core/plugins/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/plugins/management/commands/__init__.py -------------------------------------------------------------------------------- /biohub/core/plugins/management/commands/installplugin.py: -------------------------------------------------------------------------------- 1 | from ._base import PluginCommand 2 | 3 | from biohub.core.conf import dump_config 4 | 5 | 6 | class Command(PluginCommand): 7 | 8 | help = "Add a new plugin to configuration file." 9 | 10 | def handle(self, plugin, **options): 11 | super(Command, self).handle(plugin, **options) 12 | 13 | dump_config() 14 | -------------------------------------------------------------------------------- /biohub/core/plugins/management/commands/migrateplugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.core.management import call_command 3 | 4 | from biohub.utils.db import patch_test_db 5 | 6 | from ._base import PluginCommand 7 | 8 | 9 | class Command(PluginCommand): 10 | 11 | def add_arguments(self, parser): 12 | super(Command, self).add_arguments(parser) 13 | 14 | parser.add_argument( 15 | 'migration_name', 16 | nargs='?', 17 | help='Database state will be brought to the state after that' 18 | ' migration. Use the name "zero" to unapply all migrations.') 19 | parser.add_argument( 20 | '--no-output', 21 | dest='no_output', 22 | default=False, 23 | action='store_true', 24 | help='Trap the output content when applying migrations.') 25 | parser.add_argument( 26 | '--test', 27 | dest='test', 28 | default=False, 29 | action='store_true', 30 | help='Switch database configuration for testing.') 31 | parser.add_argument( 32 | '--no-input', 33 | action='store_false', 34 | dest='interactive', 35 | default=True, 36 | help='Tells Django to NOT prompt the user for input of any kind.') 37 | 38 | def handle(self, plugin, migration_name, **options): 39 | 40 | super(Command, self).handle(plugin, migration_name, **options) 41 | 42 | plugin_config = self.plugin_config 43 | 44 | with patch_test_db(options['test']): 45 | 46 | stdout = open(os.devnull, 'w', encoding='utf-8') \ 47 | if options['no_output'] else self.stdout 48 | 49 | call_command( 50 | 'migrate', plugin_config.label, migration_name=migration_name, 51 | verbosity=options['verbosity'], stdout=stdout, 52 | interactive=options['interactive']) 53 | 54 | if stdout is not self.stdout: 55 | stdout.close() 56 | -------------------------------------------------------------------------------- /biohub/core/plugins/management/commands/mkpluginmigrations.py: -------------------------------------------------------------------------------- 1 | from django.core.management import call_command 2 | 3 | from ._base import PluginCommand 4 | 5 | 6 | class Command(PluginCommand): 7 | 8 | help = 'Creates new migration(s) for plugins.' 9 | 10 | def handle(self, plugin, **options): 11 | super(Command, self).handle(plugin, **options) 12 | 13 | call_command('makemigrations', self.plugin_config.label) 14 | -------------------------------------------------------------------------------- /biohub/core/plugins/management/commands/removeplugin.py: -------------------------------------------------------------------------------- 1 | from biohub.core.plugins import plugins 2 | 3 | from ._base import PluginCommand 4 | 5 | 6 | class Command(PluginCommand): 7 | 8 | help = "Remove a plugin from the configuration file." 9 | 10 | def handle(self, plugin, **options): 11 | 12 | plugin_name = self.handle_plugin_name(plugin) 13 | 14 | plugins.remove([plugin_name], update_config=True) 15 | -------------------------------------------------------------------------------- /biohub/core/plugins/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/plugins/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/__init__.py-tpl: -------------------------------------------------------------------------------- 1 | default_app_config = '{{ plugin_name }}.apps.{{ camel_case_plugin_label }}Config' 2 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/apps.py-tpl: -------------------------------------------------------------------------------- 1 | from biohub.core.plugins import PluginConfig 2 | 3 | 4 | class {{ camel_case_plugin_label }}Config(PluginConfig): 5 | 6 | name = '{{ plugin_name }}' 7 | title = '' 8 | author = '' 9 | description = '' 10 | js_url = '' # the URL of `plugin.js`, you may leave it to blank 11 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/migrations/__init__.py-tpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/core/plugins/plugin_template/migrations/__init__.py-tpl -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/models.py-tpl: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa 2 | 3 | # Create your models here. 4 | # 5 | # References: 6 | # + https://docs.djangoproject.com/en/stable/topics/db/models/ 7 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/serializers.py-tpl: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers # noqa 2 | 3 | # Create your serializers here. 4 | # 5 | # References: 6 | # + http://www.django-rest-framework.org/api-guide/serializers/ 7 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/urls.py-tpl: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_api, register_default, url # noqa 2 | from rest_framework.routers import DefaultRouter # noqa 3 | 4 | # Place your route definition here. 5 | # 6 | # Example: 7 | # 8 | # from .views import my_view, MyModelViewSet 9 | # 10 | # router = DefaultRouter() 11 | # router.register(r'^mymodel', MyModelViewSet, base_name='mymodel') 12 | # 13 | # register_api('^myplugin/', [ 14 | # url(r'^my_view/$', my_view) 15 | # ], 'myplugin') 16 | # 17 | # References: 18 | # 19 | # + http://www.django-rest-framework.org/api-guide/routers/ 20 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/views.py-tpl: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | 3 | # References: 4 | # + https://docs.djangoproject.com/en/stable/topics/http/views/ 5 | # + http://www.django-rest-framework.org/api-guide/views/ 6 | # + http://www.django-rest-framework.org/api-guide/generic-views/ 7 | # + http://www.django-rest-framework.org/api-guide/viewsets/ 8 | -------------------------------------------------------------------------------- /biohub/core/plugins/plugin_template/ws_handlers.py-tpl: -------------------------------------------------------------------------------- 1 | from biohub.core.websocket import register_handler, register_connected # noqa 2 | 3 | # Place your websocket handlers here. 4 | # 5 | # Example: 6 | # 7 | # @register_handler('my_plugin') 8 | # def handler(message): 9 | # # Simple echo back the data 10 | # message.reply(message.data) 11 | # 12 | # @register_connected 13 | # def connected(message): 14 | # with message.patch_handler_name('my_plugin'): 15 | # # the message will be sent every time a new connection established 16 | # message.reply('connected to my_plugin') 17 | -------------------------------------------------------------------------------- /biohub/core/plugins/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | 4 | class PluginSerializer(serializers.Serializer): 5 | 6 | name = serializers.CharField(read_only=True) 7 | author = serializers.CharField(read_only=True) 8 | title = serializers.CharField(read_only=True) 9 | description = serializers.CharField(read_only=True) 10 | js_url = serializers.CharField(read_only=True) 11 | 12 | class Meta: 13 | fields = '__all__' 14 | -------------------------------------------------------------------------------- /biohub/core/plugins/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_api, url 2 | 3 | from .views import plugins_list, init_plugin, plugin_admin, upgrade_plugin, remove_plugin, plugin_info 4 | 5 | register_api(r'^plugins/', [ 6 | url(r'^$', plugins_list, name='list'), 7 | url(r'^init/$', init_plugin, name='init'), 8 | url(r'^upgrade/$', upgrade_plugin, name='upgrade'), 9 | url(r'^remove/$', remove_plugin, name='remove'), 10 | url(r'^info/$', plugin_info, name='info'), 11 | url(r'^__admin/$', plugin_admin, name='admin'), 12 | ], namespace='plugins') 13 | -------------------------------------------------------------------------------- /biohub/core/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | By default, Biohub will expose two URL prefixes `^` and `^/api`, where 3 | developers can register their own urlpatterns by using `register_api` and 4 | `register_default`. This design can prevent plugin authors from directly 5 | modifying the main urlconf. 6 | """ 7 | 8 | from django.conf.urls import include, url 9 | 10 | from biohub.utils.registry.base import ListRegistryBase 11 | 12 | 13 | class UrlPatternsRegistry(ListRegistryBase): 14 | 15 | submodules_to_populate = ('urls',) 16 | 17 | def __init__(self, prefix, name=None): 18 | self.__prefix = prefix 19 | self.__name = name 20 | 21 | super(UrlPatternsRegistry, self).__init__() 22 | 23 | def register(self, prefix, urls, namespace=None): 24 | """ 25 | Register a list of urlpatterns with given prefix and namespace. 26 | """ 27 | super(UrlPatternsRegistry, self).register([ 28 | url( 29 | prefix, 30 | include((urls, namespace)) 31 | ) 32 | ]) 33 | 34 | @property 35 | def name(self): 36 | return self.__name 37 | 38 | @property 39 | def urls(self): 40 | return self.storage_list 41 | 42 | @property 43 | def prefix(self): 44 | return self.__prefix 45 | 46 | 47 | api_url_patterns = UrlPatternsRegistry(r'^api/', 'api') 48 | default_url_patterns = UrlPatternsRegistry(r'^', 'default') 49 | 50 | register_api = api_url_patterns.register 51 | register_default = default_url_patterns.register 52 | 53 | urlpatterns = [] 54 | 55 | for proxy in (api_url_patterns, default_url_patterns): 56 | urlpatterns.append( 57 | url(proxy.prefix, include(proxy.urls, namespace=proxy.name))) 58 | 59 | 60 | def cache_clear(): 61 | """ 62 | To clear the stored url patterns, used for invalidating. 63 | """ 64 | for proxy in (api_url_patterns, default_url_patterns): 65 | proxy.cache_clear(populate=False) 66 | 67 | UrlPatternsRegistry.populate_submodules() 68 | -------------------------------------------------------------------------------- /biohub/core/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | from .registry import tasks # noqa 2 | from .base import Task # noqa 3 | from .broker import apply_async # noqa 4 | from .status import TaskStatus # noqa 5 | from .result import AsyncResult # noqa 6 | 7 | GONE = TaskStatus.GONE 8 | SUCCESS = TaskStatus.SUCCESS 9 | PENDING = TaskStatus.PENDING 10 | RUNNING = TaskStatus.RUNNING 11 | TIMEOUT = TaskStatus.TIMEOUT 12 | ERROR = TaskStatus.ERROR 13 | -------------------------------------------------------------------------------- /biohub/core/tasks/consumers.py: -------------------------------------------------------------------------------- 1 | from biohub.core.tasks.broker import broker 2 | 3 | 4 | def task_consumer(message): 5 | broker.run_task(message['task_id']) 6 | -------------------------------------------------------------------------------- /biohub/core/tasks/exceptions.py: -------------------------------------------------------------------------------- 1 | class TaskInterruption(Exception): 2 | pass 3 | 4 | 5 | class TaskRegistered(Exception): 6 | 7 | def __init__(self, task_name): 8 | self.task_name = task_name 9 | 10 | def __str__(self): 11 | return "Task '%s' was registered." % self.task_name 12 | 13 | 14 | class TaskInstanceNotExists(Exception): 15 | 16 | def __init__(self, task_id): 17 | self.task_id = task_id 18 | 19 | def __str__(self): 20 | return "Task instance with id '%s' doesn't exist." % self.task_id 21 | -------------------------------------------------------------------------------- /biohub/core/tasks/payload.py: -------------------------------------------------------------------------------- 1 | from biohub.core.tasks.exceptions import TaskInstanceNotExists 2 | from biohub.core.tasks.result import AsyncResult 3 | 4 | 5 | class TaskPayload(object): 6 | """ 7 | A task payload carries information related to a specific task instance, 8 | including task_id, arguments, options, etc. 9 | """ 10 | 11 | __slots__ = ['task_name', 'task_id', 'args', 'kwargs', 'options', 12 | 'packed_data', '_async_result'] 13 | 14 | def __init__(self, task_name, task_id, args, kwargs, options): 15 | self.task_name = task_name 16 | self.task_id = task_id 17 | self.args = args 18 | self.kwargs = kwargs or {} 19 | self.options = options 20 | self.packed_data = (task_name, task_id, args, kwargs, options) 21 | self._async_result = AsyncResult(task_id) 22 | 23 | def store(self): 24 | """ 25 | To store the information into redis. 26 | """ 27 | self._async_result._set_payload(self.packed_data) 28 | 29 | @classmethod 30 | def from_packed_data(cls, packed_data): 31 | """ 32 | A factory function to create a payload from a tuple. 33 | """ 34 | return cls(*packed_data) 35 | 36 | @classmethod 37 | def from_task_id(cls, task_id): 38 | """ 39 | A factory function to fetch task payload through a given task_id. 40 | """ 41 | packed_data = AsyncResult(task_id).payload 42 | 43 | if packed_data is None: 44 | raise TaskInstanceNotExists(task_id) 45 | 46 | return cls.from_packed_data(packed_data) 47 | -------------------------------------------------------------------------------- /biohub/core/tasks/registry.py: -------------------------------------------------------------------------------- 1 | from biohub.utils.registry.base import DictRegistryBase 2 | from biohub.core.tasks.exceptions import TaskRegistered 3 | 4 | 5 | class TaskRegistry(DictRegistryBase): 6 | 7 | submodules_to_populate = ('tasks',) 8 | key_error_class = TaskRegistered 9 | 10 | 11 | tasks = TaskRegistry() 12 | cache_clear = tasks.cache_clear 13 | -------------------------------------------------------------------------------- /biohub/core/tasks/status.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class TaskStatus(enum.Enum): 5 | 6 | GONE = 'GONE' 7 | PENDING = 'PENDING' 8 | RUNNING = 'RUNNING' 9 | TIMEOUT = 'TIMEOUT' 10 | SUCCESS = 'SUCCESS' 11 | ERROR = 'ERROR' 12 | 13 | @property 14 | def is_ready(self): 15 | return self in READY_STATUSES 16 | 17 | 18 | READY_STATUSES = (TaskStatus.SUCCESS, TaskStatus.TIMEOUT, TaskStatus.ERROR) 19 | -------------------------------------------------------------------------------- /biohub/core/tasks/storage.py: -------------------------------------------------------------------------------- 1 | from biohub.utils import redis 2 | 3 | 4 | storage = redis.Storage('__biohub_tasks__') 5 | 6 | 7 | def clear_keys(): 8 | storage.delete_pattern('*') 9 | -------------------------------------------------------------------------------- /biohub/core/websocket/__init__.py: -------------------------------------------------------------------------------- 1 | from .registry import ( # noqa 2 | websocket_handlers, register_connected, register_disconnected 3 | ) 4 | 5 | register_handler = websocket_handlers.register_decorator 6 | unregister_handler = websocket_handlers.unregister 7 | -------------------------------------------------------------------------------- /biohub/core/websocket/consumers.py: -------------------------------------------------------------------------------- 1 | from channels.generic.websockets import JsonWebsocketConsumer 2 | 3 | from .registry import websocket_handlers 4 | 5 | 6 | class MainConsumer(JsonWebsocketConsumer): 7 | 8 | http_user = True 9 | 10 | strict_ordering = False 11 | 12 | def connection_groups(self, **kwargs): 13 | """ 14 | Each connection belongs to 2 groups: broadcast and user_. 15 | The first one is for global broadcasting and the second one is for 16 | user specific broadcasting. 17 | """ 18 | 19 | groups = ['broadcast'] 20 | if self.message.user.is_authenticated(): 21 | groups.append('user_%s' % self.message.user.id) 22 | 23 | return groups 24 | 25 | def connect(self, message, **kwargs): 26 | """ 27 | Rejects if user is not authenticated. 28 | """ 29 | 30 | accept = self.message.user.is_authenticated() 31 | 32 | self.message.reply_channel.send({ 33 | "accept": accept 34 | }) 35 | 36 | if accept: 37 | websocket_handlers.dispatch(self, { 38 | 'handler': '__connect__', 39 | 'data': '' 40 | }) 41 | 42 | def receive(self, content, **kwargs): 43 | """ 44 | Dispatches incoming content to corresponding handlers. 45 | """ 46 | websocket_handlers.dispatch(self) 47 | -------------------------------------------------------------------------------- /biohub/core/websocket/parsers.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | 4 | class WebsocketDataDecodeError(Exception): 5 | pass 6 | 7 | 8 | def encode(handler_name, data): 9 | """ 10 | Combines `handler_name` and `data` to be a dict. 11 | """ 12 | 13 | assert isinstance(handler_name, str), \ 14 | ("`handler_name` should be a string, unexceptedly got `%r`." 15 | % type(handler_name)) 16 | 17 | return { 18 | 'handler': handler_name, 19 | 'data': deepcopy(data) 20 | } 21 | 22 | 23 | def decode(content): 24 | """ 25 | Returns a (handler_name, data) tuple. 26 | 27 | This function extracts the incoming content (a dict) to more detailed 28 | information. 29 | """ 30 | 31 | assert isinstance(content, dict), \ 32 | ("`content` should be a dict, unexceptedly got `%r`." 33 | % type(content)) 34 | 35 | missing_fields = {'handler', 'data'} - set(content) 36 | 37 | if missing_fields: 38 | raise WebsocketDataDecodeError( 39 | "Fields %s missing." % ', '.join(missing_fields)) 40 | 41 | return content['handler'], content['data'] 42 | -------------------------------------------------------------------------------- /biohub/core/websocket/registry.py: -------------------------------------------------------------------------------- 1 | from biohub.utils.registry.base import SignalRegistryBase 2 | from biohub.core.websocket.message import MessageWrapper 3 | 4 | SPECIAL_HANDLER_NAMES = ('__connect__', '__disconnect__') 5 | 6 | 7 | class WebsocketHandlerRegistry(SignalRegistryBase): 8 | """ 9 | A manager class to provide message dispatching, handlers 10 | registration/unregistration utilities. 11 | """ 12 | 13 | submodules_to_populate = ('ws_handlers', ) 14 | providing_args = ('message',) 15 | 16 | def dispatch(self, consumer, content=None): 17 | """ 18 | Given a consumer and optional incoming content, the function packs 19 | essential information into a MessageWrapper object, and dispatches 20 | it to handlers specified by `handler_name` in the content. 21 | """ 22 | 23 | message = MessageWrapper(consumer, content) 24 | 25 | # Returns directly if error occurs while parsing incoming data 26 | if message.handler_name == '__error__': 27 | message.reply(message.packed_data) 28 | return 29 | 30 | handler_name = message.handler_name 31 | # Silently returns if no corresponding handlers found 32 | if handler_name not in self.signal_mapping: 33 | return 34 | 35 | super(WebsocketHandlerRegistry, self).dispatch( 36 | handler_name, message=message) 37 | 38 | 39 | websocket_handlers = WebsocketHandlerRegistry() 40 | 41 | # Shorthand functions 42 | register, unregister, cache_clear = map( 43 | lambda field_name: getattr(websocket_handlers, field_name), 44 | ('register_decorator', 'unregister', 'cache_clear')) 45 | 46 | register_connected, register_disconnected = map( 47 | register, SPECIAL_HANDLER_NAMES) 48 | -------------------------------------------------------------------------------- /biohub/core/websocket/tool.py: -------------------------------------------------------------------------------- 1 | import json 2 | from functools import wraps 3 | 4 | from channels import Group 5 | 6 | from . import parsers 7 | 8 | 9 | def get_group(name): 10 | """ 11 | A shortcut to get and cache a Group object. 12 | """ 13 | return Group(name) 14 | 15 | 16 | def group_send(handler_name, group_name, data): 17 | """ 18 | To send message to a specific group. 19 | """ 20 | return get_group(group_name).send({ 21 | 'text': json.dumps(parsers.encode(handler_name, data)) 22 | }) 23 | 24 | 25 | def broadcast(handler_name, data): 26 | """ 27 | To make a global broadcasting. 28 | """ 29 | return group_send(handler_name, 'broadcast', data) 30 | 31 | 32 | def broadcast_user(handler_name, user, data): 33 | """ 34 | To broadcast to specified user. 35 | """ 36 | if isinstance(user, (int, str)): 37 | id = str(user) 38 | else: 39 | id = user.id 40 | return group_send(handler_name, 'user_%s' % id, data) 41 | 42 | 43 | def broadcast_users(handler_name, users, data): 44 | """ 45 | To broadcast to specified users. 46 | """ 47 | return [ 48 | broadcast_user(handler_name, user, data) 49 | for user in users 50 | ] 51 | 52 | 53 | BROADCAST_FUNCTION_NAMES = ( 54 | 'group_send', 'broadcast', 'broadcast_user', 'broadcast_users' 55 | ) 56 | 57 | 58 | def _method_proxy(name): 59 | """ 60 | Searches a function in the global scope with the given name and wraps it 61 | into a Broadcaster instance method. 62 | """ 63 | 64 | target_function = globals()[name] 65 | 66 | @wraps(target_function) 67 | def _proxy(self, *args, **kwargs): 68 | return target_function(self.handler_name, *args, **kwargs) 69 | 70 | return _proxy 71 | 72 | 73 | Broadcaster = type( 74 | 'Broadcaster', 75 | (), 76 | dict( 77 | __init__=lambda self, name: setattr(self, 'handler_name', name), 78 | __doc__="A helper class to broadcast websocket message.", 79 | **{name: _method_proxy(name) for name in BROADCAST_FUNCTION_NAMES}) 80 | ) 81 | -------------------------------------------------------------------------------- /biohub/forum/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.forum.apps.ForumConfig' 2 | -------------------------------------------------------------------------------- /biohub/forum/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ForumConfig(AppConfig): 5 | name = 'biohub.forum' 6 | times_calling_ready_method = 0 7 | 8 | def ready(self): 9 | # avoid executing for more than once 10 | ForumConfig.times_calling_ready_method += 1 11 | if ForumConfig.times_calling_ready_method <= 1: 12 | from biohub.forum import signals # noqa 13 | -------------------------------------------------------------------------------- /biohub/forum/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/forum/management/__init__.py -------------------------------------------------------------------------------- /biohub/forum/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/forum/management/commands/__init__.py -------------------------------------------------------------------------------- /biohub/forum/management/commands/makedigest.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand 2 | 3 | 4 | class Command(BaseCommand): 5 | 6 | def handle(self, *args, **kwargs): 7 | from biohub.forum.models import Article 8 | 9 | for article in Article.objects.all(): 10 | if not article.digest: 11 | article.make_digest() 12 | article.save() 13 | -------------------------------------------------------------------------------- /biohub/forum/migrations/0002_auto_20170919_1517.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-19 07:17 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('forum', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='experience', 17 | name='pub_time', 18 | field=models.DateTimeField(auto_now_add=True, verbose_name='publish time'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /biohub/forum/migrations/0003_auto_20170919_1520.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-19 07:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('forum', '0002_auto_20170919_1517'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='experience', 17 | options={'ordering': ('-pub_time', 'id')}, 18 | ), 19 | migrations.AlterModelOptions( 20 | name='post', 21 | options={'ordering': ['-pub_time']}, 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /biohub/forum/migrations/0004_activity_brick_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-24 03:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('forum', '0003_auto_20170919_1520'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='activity', 17 | name='brick_name', 18 | field=models.CharField(default='', max_length=20), 19 | preserve_default=False, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /biohub/forum/migrations/0005_auto_20171001_2105.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 13:05 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('contenttypes', '0002_remove_content_type_name'), 13 | ('forum', '0004_activity_brick_name'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='activity', 19 | name='target_id', 20 | field=models.PositiveSmallIntegerField(default=0, null=True), 21 | ), 22 | migrations.AddField( 23 | model_name='activity', 24 | name='target_type', 25 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /biohub/forum/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/forum/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/forum/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .bio_models import Article, Experience # noqa 2 | from .forum_models import Post # noqa 3 | from .activity_model import Activity # noqa 4 | -------------------------------------------------------------------------------- /biohub/forum/models/activity_model.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import models 3 | from django.contrib.contenttypes.fields import GenericForeignKey 4 | 5 | from biohub.utils.db import PackedField 6 | 7 | 8 | class ActivityManager(models.Manager): 9 | 10 | def create_activity(self, **kwargs): 11 | kwargs['params'].update({ 12 | 'user': kwargs['user'].username, 13 | 'type': kwargs['type'] 14 | }) 15 | 16 | return self.create(**kwargs) 17 | 18 | 19 | class Activity(models.Model): 20 | TYPE_CHOICES = ( 21 | ('Experience', 'Experience'), 22 | ('Comment', 'Comment'), 23 | ('Star', 'Star'), 24 | ('Rating', 'Rating'), 25 | ('Watch', 'Watch') 26 | ) 27 | type = models.CharField(default='', max_length=15) 28 | user = models.ForeignKey(settings.AUTH_USER_MODEL, 29 | on_delete=models.CASCADE, related_name='activities') 30 | brick_name = models.CharField(max_length=20) 31 | params = PackedField() 32 | acttime = models.DateTimeField(auto_now_add=True) 33 | 34 | target_type = models.ForeignKey('contenttypes.ContentType', null=True) 35 | target_id = models.PositiveSmallIntegerField(default=0, null=True) 36 | 37 | target = GenericForeignKey('target_type', 'target_id') 38 | 39 | objects = ActivityManager() 40 | -------------------------------------------------------------------------------- /biohub/forum/models/forum_models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from django.contrib.contenttypes.fields import GenericRelation 4 | 5 | from .bio_models import Experience 6 | 7 | 8 | MAX_LEN_FOR_CONTENT = 1000 9 | 10 | 11 | class Post(models.Model): 12 | # when the experience is deleted, the posts attached to it won't be deleted. 13 | # The is_visible fields of those posts will be set False instead. 14 | # That is, they can't be read by the people except the author himself. 15 | experience = models.ForeignKey( 16 | Experience, on_delete=models.CASCADE, null=True, related_name='posts') 17 | content = models.TextField(blank=False, max_length=MAX_LEN_FOR_CONTENT, ) 18 | author = models.ForeignKey( 19 | settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='posts') 20 | update_time = models.DateTimeField( 21 | 'last updated', auto_now=True,) 22 | pub_time = models.DateTimeField('publish time', auto_now_add=True) 23 | is_visible = models.BooleanField(default=True) 24 | 25 | activities = GenericRelation('forum.Activity', 'target_id', 'target_type', related_query_name='post') 26 | notices = GenericRelation('notices.Notice', 'target_id', 'target_type', related_query_name='post') 27 | 28 | class Meta: 29 | ordering = ['-pub_time'] 30 | 31 | def hide(self): 32 | self.is_visible = False 33 | self.save() 34 | 35 | def show(self): 36 | self.is_visible = True 37 | self.save() 38 | 39 | def get_router_arguments(self): 40 | return 'post', self.pk 41 | -------------------------------------------------------------------------------- /biohub/forum/serializers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/forum/serializers/__init__.py -------------------------------------------------------------------------------- /biohub/forum/serializers/activity_serializers.py: -------------------------------------------------------------------------------- 1 | from biohub.utils.rest.serializers import bind_model, ModelSerializer 2 | from biohub.utils.rest.fields import PackedField 3 | 4 | from ..models import Activity 5 | 6 | 7 | @bind_model(Activity) 8 | class ActivitySerializer(ModelSerializer): 9 | 10 | params = PackedField() 11 | 12 | class Meta: 13 | model = Activity 14 | exclude = ('user', 'id', 'target_type', 'target_id') 15 | read_only_fields = ('type', 'params', 'acttime',) 16 | 17 | def to_representation(self, obj): 18 | 19 | result = super(ActivitySerializer, self).to_representation(obj) 20 | 21 | if obj.type == 'Watch': 22 | result['params']['score'] = obj.score 23 | 24 | return result 25 | -------------------------------------------------------------------------------- /biohub/forum/serializers/article_serializers.py: -------------------------------------------------------------------------------- 1 | 2 | from django.db import transaction 3 | from rest_framework import serializers 4 | 5 | from biohub.core.files.serializers import FileSerializer 6 | from biohub.utils.rest.serializers import bind_model, ModelSerializer 7 | from ..models import Article 8 | 9 | 10 | @bind_model(Article) 11 | class ArticleSerializer(ModelSerializer): 12 | 13 | file_ids = serializers.ListField( 14 | child=serializers.IntegerField(), 15 | write_only=True 16 | ) 17 | files = FileSerializer(many=True, read_only=True) 18 | 19 | class Meta: 20 | model = Article 21 | fields = ('text', 'file_ids', 'digest', 'files') 22 | read_only_fields = ('digest',) 23 | 24 | def create(self, validated_data): 25 | files = validated_data.pop('file_ids') 26 | with transaction.atomic(): 27 | article = Article.objects.create(**validated_data) 28 | article.files.add(*files) 29 | return article 30 | 31 | def update(self, instance, validated_data): 32 | with transaction.atomic(): 33 | if 'file_ids' in validated_data: 34 | files = validated_data.pop('file_ids') 35 | instance.update_files(files) 36 | # instance.files.set(File.objects.only('id').filter(pk__in=files), clear=True) 37 | instance.text = validated_data.get('text', instance.text) 38 | instance.save() 39 | return instance 40 | -------------------------------------------------------------------------------- /biohub/forum/serializers/post_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from biohub.utils.rest.serializers import ModelSerializer 4 | from biohub.utils.rest.serializers import bind_model 5 | from biohub.forum.models import Post, Experience 6 | from biohub.accounts.serializers import UserSerializer 7 | 8 | from .experience_serializers import ExperienceSerializer 9 | 10 | 11 | @bind_model(Post) 12 | class PostSerializer(ModelSerializer): 13 | author = UserSerializer(fields=('id', 'username', 'avatar_url'), read_only=True) 14 | experience_id = serializers.PrimaryKeyRelatedField( 15 | write_only=True, queryset=Experience.objects.all() 16 | ) 17 | experience = ExperienceSerializer(read_only=True) 18 | 19 | class Meta: 20 | model = Post 21 | fields = ('id', 'experience_id', 'experience', 22 | 'content', 'update_time', 23 | 'pub_time', 'author') 24 | read_only_fields = ('id', 'update_time', 'pub_time') 25 | 26 | def create(self, validated_data): 27 | experience = validated_data.pop('experience_id') 28 | post = Post.objects.create(experience=experience, **validated_data) 29 | return post 30 | 31 | def update(self, instance, validated_data): 32 | if 'experience_id' in validated_data: 33 | validated_data.pop('experience_id') 34 | instance.content = validated_data.get('content', instance.content) 35 | instance.save() 36 | return instance 37 | -------------------------------------------------------------------------------- /biohub/forum/signals/__init__.py: -------------------------------------------------------------------------------- 1 | from .forum_signals import * # noqa 2 | from .bio_signals import * # noqa 3 | -------------------------------------------------------------------------------- /biohub/forum/signals/bio_signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import receiver 2 | from django.db.models.signals import post_save 3 | 4 | from biohub.forum.models import Experience 5 | from biohub.notices.tool import Dispatcher 6 | 7 | 8 | forum_dispatcher = Dispatcher('Forum') 9 | 10 | 11 | @receiver(post_save, sender=Experience) 12 | def notice_watching_user_when_new_experience_is_posted(instance, created, **kwargs): 13 | if created: 14 | # only send notices when a NEW experience is posted 15 | users_to_send = instance.brick.users_watching.only('id') 16 | 17 | if instance.author is not None: 18 | users_to_send = users_to_send.exclude(pk=instance.author.id) 19 | 20 | forum_dispatcher.group_send( 21 | users_to_send, 22 | '{{experience.author.username|url:experience.author}} ' 23 | 'published a new experience {{experience.title|url:experience}} ' 24 | 'at brick {{brick.part_name|url:brick}}.', 25 | experience=instance, 26 | brick=instance.brick 27 | ) 28 | -------------------------------------------------------------------------------- /biohub/forum/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url # noqa 2 | from biohub.core.routes import register_api 3 | from rest_framework.routers import DefaultRouter 4 | from biohub.forum.views import PostViewSet, ArticleViewSet,\ 5 | ExperienceViewSet, ActivityViewSet, UserExperienceViewSet 6 | 7 | from biohub.biobrick.views import BiobrickViewSet, UserBrickViewSet 8 | 9 | router = DefaultRouter() 10 | router.register(r'posts', PostViewSet, base_name='post') 11 | router.register(r'articles', ArticleViewSet, base_name='article') 12 | router.register(r'bricks', BiobrickViewSet, base_name='biobrick') 13 | router.register(r'experiences', ExperienceViewSet, base_name='experience') 14 | router.register(r'activities', ActivityViewSet, base_name='activity') 15 | router.register(r'experiences/(?P\d+)/posts', PostViewSet, base_name='post') 16 | ExperienceViewSet.add_to_router(router, 'experiences') 17 | 18 | 19 | register_api(r'^forum/', router.urls, 'forum') 20 | 21 | extra_router = DefaultRouter() 22 | UserBrickViewSet.add_to_router(extra_router) 23 | UserExperienceViewSet.add_to_router(extra_router) 24 | register_api(r'^', extra_router.urls) 25 | -------------------------------------------------------------------------------- /biohub/forum/user_defined_signals.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module includes some signals defined to handle different events happening in the forums. 3 | """ 4 | 5 | from django.dispatch import Signal 6 | 7 | # rating_score: The score other user rated. 8 | # curr_score: The score after that user rated. 9 | # user_rating: The person (User instance) who rated. 10 | # instance: the instance which send this signal 11 | rating_brick_signal = Signal(providing_args=('rating_score', 'curr_score', 12 | 'user_rating', 'instance')) 13 | 14 | # instance: the instance sending this signal. 15 | # user_up_voting: the user who vote for the post 16 | # curr_up_vote_num: the number of votes after this voting. 17 | voted_experience_signal = Signal( 18 | providing_args=('instance', 'user_voted', 'current_votes') 19 | ) 20 | unvoted_experience_signal = Signal( 21 | providing_args=('instance', 'user_unvoted') 22 | ) 23 | 24 | watching_brick_signal = Signal(providing_args=('instance', 'user')) 25 | unwatching_brick_signal = Signal(providing_args=('instance', 'user')) 26 | -------------------------------------------------------------------------------- /biohub/forum/views/__init__.py: -------------------------------------------------------------------------------- 1 | from .post_views import PostViewSet # noqa 2 | from .article_views import ArticleViewSet # noqa 3 | # from .brick_views import BrickViewSet, UserBrickViewSet # noqa 4 | from .experience_views import ExperienceViewSet, UserExperienceViewSet # noqa 5 | from .activity_views import ActivityViewSet # noqa 6 | -------------------------------------------------------------------------------- /biohub/forum/views/article_views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets, status 2 | from rest_framework.response import Response 3 | from biohub.forum.serializers.article_serializers import ArticleSerializer 4 | from ..models import Article 5 | 6 | 7 | class ArticleViewSet(viewsets.ReadOnlyModelViewSet): 8 | 9 | serializer_class = ArticleSerializer 10 | queryset = Article.objects.all() 11 | 12 | def list(self, request, *args, **kwargs): 13 | return Response('Unable to list articles.', status=status.HTTP_404_NOT_FOUND) 14 | -------------------------------------------------------------------------------- /biohub/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/main/__init__.py -------------------------------------------------------------------------------- /biohub/main/asgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) 4 | 5 | import biohub.compat # noqa 6 | from channels.asgi import get_channel_layer # noqa 7 | 8 | 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biohub.main.settings.prod") 10 | os.environ.setdefault( 11 | "BIOHUB_CONFIG_PATH", 12 | os.path.join(os.path.dirname(__file__), '..', '..', "config.json") 13 | ) 14 | 15 | channel_layer = get_channel_layer() 16 | -------------------------------------------------------------------------------- /biohub/main/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/main/settings/__init__.py -------------------------------------------------------------------------------- /biohub/main/settings/dev.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | import coloredlogs 3 | 4 | ALLOWED_HOSTS = ['*'] 5 | 6 | TIME_ZONE = 'Asia/Shanghai' 7 | 8 | LOGGING = { 9 | 'version': 1, 10 | 'disable_existing_loggers': True, 11 | 'formatters': { 12 | 'colored_formatter': { 13 | '()': coloredlogs.ColoredFormatter, 14 | 'format': '%(name)s:%(funcName)s:%(lineno)d %(message)s' 15 | } 16 | }, 17 | 'handlers': { 18 | 'console': { 19 | 'class': 'logging.StreamHandler', 20 | 'formatter': 'colored_formatter' 21 | } 22 | }, 23 | 'loggers': { 24 | 'django': { 25 | 'handlers': ['console'], 26 | 'level': 'WARNING', 27 | 'propagate': False 28 | }, 29 | 'django.request': { 30 | 'handlers': ['console'], 31 | 'level': 'ERROR', 32 | 'propagate': False 33 | }, 34 | 'filelock': { 35 | 'handlers': ['console'], 36 | 'level': 'CRITICAL', 37 | 'propagate': False 38 | }, 39 | '': { 40 | 'handlers': ['console'], 41 | 'level': 'ERROR', 42 | 'propagate': True 43 | }, 44 | 'biohub': { 45 | 'handlers': ['console'], 46 | 'level': 'DEBUG', 47 | 'propagate': False 48 | } 49 | } 50 | } 51 | 52 | DEBUG = True 53 | -------------------------------------------------------------------------------- /biohub/main/settings/prod.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | from .base import * # noqa 4 | 5 | ALLOWED_HOSTS = ['*'] 6 | 7 | 8 | DEBUG = False 9 | TIME_ZONE = os.getenv('TZ', 'Asia/Shanghai') 10 | 11 | LOGGING_DIR = os.getenv('BIOHUB_LOGGING_DIR', tempfile.gettempdir()) 12 | LOGGING_REQUEST_DIR = os.path.join(LOGGING_DIR, 'biohub_log', 'request') 13 | LOGGING_ERROR_DIR = os.path.join(LOGGING_DIR, 'biohub_log', 'error') 14 | 15 | os.makedirs(LOGGING_REQUEST_DIR, exist_ok=True) 16 | os.makedirs(LOGGING_ERROR_DIR, exist_ok=True) 17 | 18 | 19 | LOGGING = { 20 | "version": 1, 21 | "disable_existing_handlers": True, 22 | 'formatters': { 23 | 'verbose': { 24 | 'format': '%(levelname)s %(asctime)s %(name)s:%(funcName)s:%(lineno)d %(process)d %(thread)x %(message)s', 25 | 'level': 'WARNING' 26 | }, 27 | }, 28 | "handlers": { 29 | "error_log": { 30 | 'class': 'logging.handlers.TimedRotatingFileHandler', 31 | 'filename': os.path.join(LOGGING_ERROR_DIR, 'log'), 32 | 'formatter': 'verbose', 33 | 'when': 'D' 34 | }, 35 | 'ignore': { 36 | 'class': 'logging.NullHandler' 37 | } 38 | }, 39 | "loggers": { 40 | 'django': { 41 | 'handlers': ['error_log'], 42 | 'propagate': False, 43 | 'level': 'WARNING', 44 | }, 45 | 'biohub': { 46 | 'handlers': ['error_log'], 47 | 'propagate': False, 48 | 'level': 'WARNING', 49 | }, 50 | '': { 51 | 'handlers': ['error_log'], 52 | 'propagate': False, 53 | 'level': 'ERROR', 54 | }, 55 | 'daphne': { 56 | 'handlers': ['ignore'], 57 | 'propagate': False, 58 | 'level': 'DEBUG' 59 | } 60 | } 61 | } 62 | 63 | del os 64 | -------------------------------------------------------------------------------- /biohub/main/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import urlpatterns as biohub_urlpatterns 2 | 3 | from django.conf import settings 4 | from django.conf.urls import url 5 | from django.views.static import serve 6 | 7 | urlpatterns = biohub_urlpatterns[:] 8 | 9 | if settings.DEBUG: 10 | urlpatterns += [ 11 | url(r'^media/(?P.*)$', serve, 12 | dict(document_root=settings.MEDIA_ROOT)) 13 | ] 14 | -------------------------------------------------------------------------------- /biohub/main/views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/main/views.py -------------------------------------------------------------------------------- /biohub/main/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for biohub project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | from os import path 12 | import biohub.compat # noqa 13 | 14 | from django.core.wsgi import get_wsgi_application 15 | 16 | try: 17 | import biohub # noqa: F401 18 | except ImportError: 19 | import sys 20 | sys.path.insert( 21 | 0, 22 | path.join(path.dirname(__file__), '..', '..')) 23 | 24 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biohub.settings") 25 | os.environ.setdefault('BIOHUB_CONFIG_PATH', path.join(path.dirname(__file__), '..', '..', 'config.json')) 26 | 27 | application = get_wsgi_application() 28 | -------------------------------------------------------------------------------- /biohub/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import compat # noqa: F401 4 | 5 | import os 6 | import sys 7 | 8 | from os.path import dirname, join, abspath, realpath 9 | 10 | 11 | if __name__ == "__main__": 12 | 13 | CURRENT_DIR = abspath(dirname(realpath(__file__))) 14 | BIOHUB_BASE_DIR = join(CURRENT_DIR, '..') 15 | 16 | try: 17 | import biohub # noqa: F401 18 | except ImportError: 19 | sys.path.insert(0, BIOHUB_BASE_DIR) 20 | 21 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biohub.main.settings.dev") 22 | os.environ.setdefault("BIOHUB_CONFIG_PATH", 23 | join(BIOHUB_BASE_DIR, 'config.json')) 24 | try: 25 | from django.core.management import execute_from_command_line 26 | except ImportError: 27 | # The above import may fail for some other reason. Ensure that the 28 | # issue is really that Django is missing to avoid masking other 29 | # exceptions on Python 2. 30 | try: 31 | import django # noqa: F401 32 | except ImportError: 33 | raise ImportError( 34 | "Couldn't import Django. Are you sure it's installed and " 35 | "available on your PYTHONPATH environment variable? Did you " 36 | "forget to activate a virtual environment?" 37 | ) 38 | raise 39 | execute_from_command_line(sys.argv) 40 | -------------------------------------------------------------------------------- /biohub/notices/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'biohub.notices.apps.NoticesConfig' 2 | -------------------------------------------------------------------------------- /biohub/notices/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NoticesConfig(AppConfig): 5 | name = 'biohub.notices' 6 | -------------------------------------------------------------------------------- /biohub/notices/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-09-18 12:41 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Notice', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('has_read', models.BooleanField(db_index=True, default=False)), 24 | ('created', models.DateTimeField(auto_now_add=True, db_index=True)), 25 | ('message', models.TextField()), 26 | ('category', models.CharField(db_index=True, max_length=200)), 27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notices', to=settings.AUTH_USER_MODEL)), 28 | ], 29 | options={ 30 | 'ordering': ('-has_read', '-created'), 31 | }, 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /biohub/notices/migrations/0002_auto_20171001_2105.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-01 13:05 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('contenttypes', '0002_remove_content_type_name'), 15 | ('notices', '0001_initial'), 16 | ] 17 | 18 | operations = [ 19 | migrations.AddField( 20 | model_name='notice', 21 | name='actor', 22 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 23 | ), 24 | migrations.AddField( 25 | model_name='notice', 26 | name='target_id', 27 | field=models.PositiveIntegerField(default=0, null=True), 28 | ), 29 | migrations.AddField( 30 | model_name='notice', 31 | name='target_slug', 32 | field=models.CharField(default='', max_length=20), 33 | ), 34 | migrations.AddField( 35 | model_name='notice', 36 | name='target_type', 37 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /biohub/notices/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/notices/migrations/__init__.py -------------------------------------------------------------------------------- /biohub/notices/serializers.py: -------------------------------------------------------------------------------- 1 | from biohub.utils.rest.serializers import ModelSerializer, bind_model 2 | 3 | from .models import Notice 4 | 5 | 6 | @bind_model(Notice) 7 | class NoticeSerializer(ModelSerializer): 8 | 9 | class Meta: 10 | fields = ('id', 'has_read', 'message', 'category', 'created') 11 | model = Notice 12 | -------------------------------------------------------------------------------- /biohub/notices/template/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/notices/template/__init__.py -------------------------------------------------------------------------------- /biohub/notices/template/filters.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.filter 7 | def url(text, target): 8 | 9 | assert hasattr(target, 'get_router_arguments'), \ 10 | "Target %r should define a `get_router_arguments` method." % target 11 | 12 | type, primary_key = target.get_router_arguments() 13 | 14 | return '[[{text}]](({type}))(({primary_key}))'.format(text=text, type=type, primary_key=primary_key) 15 | -------------------------------------------------------------------------------- /biohub/notices/urls.py: -------------------------------------------------------------------------------- 1 | from biohub.core.routes import register_api, url # noqa:F401 2 | from rest_framework.routers import DefaultRouter 3 | 4 | from .views import NoticeViewSet 5 | 6 | router = DefaultRouter() 7 | 8 | router.register(r'^notices', NoticeViewSet, base_name='notice') 9 | 10 | 11 | register_api('', [ 12 | 13 | ] + router.urls, 'notices') 14 | -------------------------------------------------------------------------------- /biohub/notices/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets, mixins, decorators 2 | from rest_framework.response import Response 3 | from biohub.utils.rest import pagination, permissions as p 4 | 5 | from .serializers import NoticeSerializer 6 | from .models import Notice 7 | 8 | 9 | class NoticeViewSet( 10 | mixins.ListModelMixin, 11 | mixins.RetrieveModelMixin, 12 | viewsets.GenericViewSet): 13 | 14 | serializer_class = NoticeSerializer 15 | pagination_class = pagination.factory('PageNumberPagination') 16 | permission_classes = [p.C(p.IsAuthenticated) & 17 | p.check_owner('user', ('GET',))] 18 | filter_fields = ('has_read', 'category') 19 | 20 | def get_queryset(self): 21 | qs = Notice.objects.user_notices(self.request.user) 22 | 23 | id_list = self.request.query_params.get('ids', None) 24 | if id_list is not None: 25 | qs = qs.filter(id__in=id_list.split(',')) 26 | 27 | return qs.order_by('-created') 28 | 29 | @decorators.list_route(['GET']) 30 | def mark_all_as_read(self, *args, **kwargs): 31 | self.get_queryset().mark_read() 32 | 33 | return Response('OK') 34 | 35 | @decorators.detail_route(['GET']) 36 | def mark_read(self, *args, **kwargs): 37 | self.get_object().mark_read() 38 | 39 | return Response('OK') 40 | 41 | @decorators.list_route(['GET']) 42 | def categories(self, *args, **kwargs): 43 | return Response(self.get_queryset().categories()) 44 | 45 | @decorators.list_route(['GET']) 46 | def stats(self, *args, **kwargs): 47 | return Response(self.get_queryset().stats()) 48 | -------------------------------------------------------------------------------- /biohub/notices/ws_handlers.py: -------------------------------------------------------------------------------- 1 | from biohub.core.websocket import register_connected, register_handler 2 | 3 | from .models import Notice 4 | 5 | 6 | def echo_notices_stats(message): 7 | message.reply( 8 | Notice.objects.user_notices(message.user).unread().count() 9 | ) 10 | 11 | 12 | @register_handler('notices') 13 | def notices_handler(message): 14 | if message.data == 'fetch': 15 | echo_notices_stats(message) 16 | 17 | 18 | @register_connected 19 | def ws_connected(message): 20 | """ 21 | Echos stats of user notices every time a connection established. 22 | """ 23 | 24 | with message.patch_handler_name('notices'): 25 | echo_notices_stats(message) 26 | -------------------------------------------------------------------------------- /biohub/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/utils/__init__.py -------------------------------------------------------------------------------- /biohub/utils/collections.py: -------------------------------------------------------------------------------- 1 | def unique(seq): 2 | """ 3 | Remove duplicated items in a sequence whilst preserving the order. 4 | """ 5 | seen = set() 6 | seen_add = seen.add 7 | return [x for x in seq if not (x in seen or seen_add(x))] 8 | -------------------------------------------------------------------------------- /biohub/utils/db.py: -------------------------------------------------------------------------------- 1 | import json 2 | import contextlib 3 | 4 | from django.db import connection, models 5 | from django.conf import settings 6 | 7 | 8 | def _set_database_name(testing, alias, db_name): 9 | 10 | if not testing: 11 | return 12 | 13 | connection.close() 14 | settings.DATABASES[alias]['NAME'] = db_name 15 | connection.settings_dict['NAME'] = db_name 16 | 17 | 18 | @contextlib.contextmanager 19 | def patch_test_db(testing): 20 | """ 21 | Reference: django/db/backends/base/creation.py:57 22 | 23 | A context manager to modify current database connection to simulate a test 24 | environment, just as what `manage.py test` does. 25 | 26 | The helper will be useful when migrating models for test database. 27 | """ 28 | old_db_name = connection.settings_dict['NAME'] 29 | test_db_name = connection.creation._get_test_db_name() 30 | alias = connection.alias 31 | 32 | _set_database_name(testing, alias, test_db_name) 33 | 34 | yield 35 | 36 | _set_database_name(testing, alias, old_db_name) 37 | 38 | 39 | class PackedField(models.TextField): 40 | 41 | description = 'Packed complicated data' 42 | 43 | def from_db_value(self, value, expression, connection, context): 44 | 45 | if value is None: 46 | return value 47 | 48 | return self._decode(value) 49 | 50 | def to_python(self, value): 51 | 52 | return value 53 | 54 | def get_db_prep_value(self, value, connection, prepared=False): 55 | 56 | return self._encode(value) 57 | 58 | def _decode(self, value): 59 | return json.loads(value) 60 | 61 | def _encode(self, value): 62 | return json.dumps(value) 63 | -------------------------------------------------------------------------------- /biohub/utils/detect.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.utils.functional import cached_property 4 | 5 | 6 | class Features: 7 | 8 | @cached_property 9 | def redis(self): 10 | from django.core.cache import cache 11 | return cache.client.__class__.__module__.startswith('django_redis') 12 | 13 | @property 14 | def testing(self): 15 | return 'BIOHUB_TESTING' in os.environ 16 | 17 | 18 | features = Features() 19 | -------------------------------------------------------------------------------- /biohub/utils/download.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import requests 3 | import tempfile 4 | 5 | 6 | def download(url, dest=None, suffix=None, stdout=sys.stdout): 7 | 8 | if dest is None: 9 | dest_fp, dest_name = tempfile.mkstemp(suffix=suffix) 10 | f = open(dest_fp, 'w+b') 11 | else: 12 | f = open(dest, 'w+b') 13 | dest_name = dest 14 | 15 | stdout.write('Downloading from {}...\n'.format(url)) 16 | r = requests.get(url, stream=True) 17 | 18 | total = int(r.headers.get('Content-Length', 0)) 19 | received = 0 20 | last_length = 0 21 | 22 | for chunk in r.iter_content(chunk_size=1024 * 1024): 23 | if chunk: 24 | f.write(chunk) 25 | received += len(chunk) 26 | 27 | if total: 28 | message = 'Completed {}%'.format(received / total * 100) 29 | else: 30 | message = 'Received {} bytes.'.format(received) 31 | 32 | stdout.write('\b' * last_length) 33 | stdout.flush() 34 | stdout.write(message) 35 | stdout.flush() 36 | last_length = len(message) 37 | else: 38 | stdout.write('\b' * last_length) 39 | stdout.flush() 40 | 41 | stdout.write('Downloaded %s.\n' % dest_name) 42 | 43 | f.seek(0) 44 | return f, dest_name 45 | -------------------------------------------------------------------------------- /biohub/utils/http.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from functools import wraps 3 | 4 | from django.http import HttpResponse 5 | from django.core.signing import Signer 6 | 7 | 8 | class basicauth: 9 | 10 | def __init__(self, realm, password): 11 | self.realm = realm 12 | self.password = password 13 | 14 | def __call__(self, view): 15 | 16 | @wraps(view) 17 | def wrapper(request, *args, **kwargs): 18 | 19 | if 'HTTP_AUTHORIZATION' in request.META: 20 | auth_info = request.META['HTTP_AUTHORIZATION'].split() 21 | 22 | if len(auth_info) == 2 and auth_info[0].lower() == 'basic': 23 | password = base64.b64decode(auth_info[1]).decode().split(':')[1] 24 | 25 | if ':'.join((self.password, password)) == Signer().sign(self.password): 26 | return view(request, *args, **kwargs) 27 | 28 | response = HttpResponse() 29 | response.status_code = 401 30 | response['WWW-Authenticate'] = 'Basic realm=%s' % self.realm 31 | 32 | return response 33 | return wrapper 34 | 35 | 36 | def get_ip_from_request(request): 37 | """ 38 | An intelligent method to get real IP address. 39 | """ 40 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') 41 | if x_forwarded_for: 42 | ip = x_forwarded_for.split(',')[0] 43 | else: 44 | ip = request.META.get('REMOTE_ADDR', '') 45 | return ip 46 | -------------------------------------------------------------------------------- /biohub/utils/path.py: -------------------------------------------------------------------------------- 1 | from importlib import import_module 2 | from os.path import dirname, abspath, realpath, expanduser 3 | from os.path import join # noqa 4 | 5 | 6 | def filepath(filename): 7 | """ 8 | Returns the exact directory where `filename` lies, following symlinks. 9 | """ 10 | return realdir(dirname(filename)) 11 | 12 | 13 | def realdir(dirname): 14 | """ 15 | Expand the given directory to an absolute one, following symlinks. 16 | """ 17 | 18 | return realpath(abspath(expanduser(dirname))) 19 | 20 | 21 | def modpath(modname): 22 | """ 23 | Returns the exact directory of a package. 24 | """ 25 | return filepath(import_module(modname).__file__) 26 | -------------------------------------------------------------------------------- /biohub/utils/registry/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/utils/registry/__init__.py -------------------------------------------------------------------------------- /biohub/utils/rest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/biohub/utils/rest/__init__.py -------------------------------------------------------------------------------- /biohub/utils/rest/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import SessionAuthentication 2 | 3 | 4 | class NoCSRFAuthentication(SessionAuthentication): 5 | 6 | def enforce_csrf(self, request): 7 | return 8 | -------------------------------------------------------------------------------- /biohub/utils/rest/fields.py: -------------------------------------------------------------------------------- 1 | from rest_framework.fields import Field 2 | 3 | 4 | class PackedField(Field): 5 | 6 | def to_representation(self, obj): 7 | return obj 8 | 9 | def to_internal_value(self, data): 10 | return data 11 | -------------------------------------------------------------------------------- /biohub/utils/rest/pagination.py: -------------------------------------------------------------------------------- 1 | from rest_framework import pagination, response 2 | 3 | __all__ = ['factory'] 4 | 5 | 6 | def factory(base_class_name, **options): 7 | """ 8 | A factory function to conveniently create pagination class. Base class is 9 | specified by `base_class_name`, which will be used to locate a class in 10 | `rest_framework.pagination` module. Extra options will be applied as class 11 | members of the new pagination class. 12 | """ 13 | 14 | base_cls = getattr(pagination, base_class_name, None) 15 | 16 | assert base_cls is not None, \ 17 | 'Cannot resolve %r into pagination class.' % base_class_name 18 | 19 | options.setdefault('ordering', 'id') 20 | 21 | return type( 22 | 'Pagination', 23 | (base_cls,), 24 | options 25 | ) 26 | 27 | 28 | def paginate_queryset(queryset, view): 29 | """ 30 | Uses `view`'s `paginate_queryset` method to get a page object and returns 31 | a response. 32 | """ 33 | page = view.paginate_queryset(queryset) 34 | if page is not None: 35 | serializer = view.get_serializer(page, many=True) 36 | return view.get_paginated_response(serializer.data) 37 | 38 | serializer = view.get_serializer(queryset, many=True) 39 | return response.Response(serializer.data) 40 | -------------------------------------------------------------------------------- /biohub/utils/rest/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import ( # noqa:F403 2 | BasePermission, AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly) # noqa: F403 3 | from rest_condition import C # noqa:F403 4 | 5 | 6 | def check_owner(field_name=None, methods=('PUT', 'PATCH')): 7 | """ 8 | A factory function to create permission class to check whether a user 9 | 'owns' an object. The user field of the object is specified by `field_name` 10 | (None for itself). An optional argument `methods` may be provided to 11 | indicate under what circumstances the check will occur. 12 | """ 13 | 14 | class P(BasePermission): 15 | 16 | def has_object_permission(self, request, view, obj): 17 | 18 | if field_name is not None: 19 | assert hasattr(obj, field_name),\ 20 | 'Cannot resolve %r into a field of %r.' % ( 21 | field_name, obj.__class__) 22 | user_to_check = getattr(obj, field_name) 23 | else: 24 | user_to_check = obj 25 | 26 | if request.method not in methods: 27 | return True 28 | 29 | user = request.user 30 | return user.is_authenticated() and user.pk == user_to_check.id 31 | 32 | return P 33 | -------------------------------------------------------------------------------- /biohub/utils/test.py: -------------------------------------------------------------------------------- 1 | def skip_if_no_environ(name): 2 | import os 3 | import unittest 4 | 5 | return unittest.skipIf(not os.environ.get(name, 0), '') 6 | -------------------------------------------------------------------------------- /biohub/utils/url.py: -------------------------------------------------------------------------------- 1 | import urllib.parse as parse 2 | 3 | 4 | def add_params(url, **params): 5 | """ 6 | Append or replace query params in `url` using `params`. 7 | """ 8 | url = parse.unquote(url) 9 | parsed = parse.urlparse(url) 10 | existing = dict(parse.parse_qsl(parsed.query)) 11 | existing.update(params) 12 | return parse.urlunparse( 13 | parse.ParseResult( 14 | scheme=parsed.scheme, 15 | netloc=parsed.netloc, 16 | path=parsed.path, 17 | params=parsed.params, 18 | query=parse.urlencode(existing), 19 | fragment=parsed.fragment 20 | ) 21 | ) 22 | 23 | 24 | def get_params(url): 25 | """ 26 | Extract query parameters from url, and return as a dict. 27 | """ 28 | url = parse.unquote(url) 29 | parsed = parse.urlparse(url) 30 | return dict(parse.parse_qsl(parsed.query)) 31 | -------------------------------------------------------------------------------- /config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "DATABASE": { 3 | "NAME": "", 4 | "USER": "", 5 | "PASSWORD": "", 6 | "HOST": "", 7 | "PORT": 3306, 8 | "TEST": { 9 | "NAME": "", 10 | "CHARSET": "utf8", 11 | "COLLATION": null, 12 | "MIRROR": null 13 | } 14 | }, 15 | "PLUGINS": [ 16 | "biohub.abacus", 17 | "biohub.biocircuit", 18 | "biohub.biomap" 19 | ], 20 | "TIMEZONE": "UTC", 21 | "UPLOAD_DIR": "", 22 | "REDIS_URI": "", 23 | "SECRET_KEY": "", 24 | "MAX_TASKS": 20, 25 | "TASK_MAX_TIMEOUT": 180, 26 | "ES_URL": "http://127.0.0.1:9200/", 27 | "EMAIL": { 28 | "HOST_PASSWORD": "", 29 | "HOST_USER": "", 30 | "HOST": "", 31 | "PORT": 465 32 | }, 33 | "CORS": [], 34 | "THROTTLE": { 35 | "rate": 15, 36 | "experience": 86400, 37 | "post": 15, 38 | "vote": 15, 39 | "register": 3600 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to Biohub 2 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Biohub 2 | -------------------------------------------------------------------------------- /requirements/abacus-server.txt: -------------------------------------------------------------------------------- 1 | Django==1.11.7 2 | psutil==5.4.1 3 | celery[redis] 4 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | django-cors-headers==2.1.0 2 | Django==1.11.7 3 | djangorestframework==3.7.3 4 | django-filter==1.1.0 5 | rest-condition==1.0.3 6 | channels==1.1.8 7 | PyMySQL==0.7.11 8 | filelock==2.0.13 9 | asgi-redis==1.4.3 10 | cryptography==2.1.4 11 | django-redis==4.8.0 12 | lxml==4.1.1 13 | requests==2.18.4 14 | beautifulsoup4==4.6.0 15 | html2text==2017.10.4 16 | sqlparse==0.2.4 17 | python-crontab==2.2.7 18 | psutil==5.4.1 19 | -------------------------------------------------------------------------------- /requirements/biobrick-base.txt: -------------------------------------------------------------------------------- 1 | django-haystack==2.6.1 2 | elasticsearch==6.0.0 3 | -------------------------------------------------------------------------------- /requirements/biocircuit-base.txt: -------------------------------------------------------------------------------- 1 | networkx==1.11 2 | -------------------------------------------------------------------------------- /requirements/codestyle-base.txt: -------------------------------------------------------------------------------- 1 | pep8==1.7.1 2 | flake8==3.5.0 3 | -------------------------------------------------------------------------------- /requirements/dev.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | -r test-base.txt 3 | -r doc-base.txt 4 | -r codestyle-base.txt 5 | 6 | -r biobrick-base.txt 7 | -r biocircuit-base.txt 8 | 9 | coloredlogs==7.3.1 10 | -------------------------------------------------------------------------------- /requirements/doc-base.txt: -------------------------------------------------------------------------------- 1 | mkdocs==0.17.0 2 | -------------------------------------------------------------------------------- /requirements/prod.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | -r biobrick-base.txt 3 | -r biocircuit-base.txt 4 | 5 | uWSGI==2.0.15 6 | -------------------------------------------------------------------------------- /requirements/test-base.txt: -------------------------------------------------------------------------------- 1 | pytest==3.3.0 2 | pytest-django==3.1.2 3 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/tests/__init__.py -------------------------------------------------------------------------------- /tests/abacus/FakeAbacus/bin/FakeAbacus.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igemsoftware2017/USTC-Software-2017/b5fd616510fc6dae0970b066a0804d4ee785ed41/tests/abacus/FakeAbacus/bin/FakeAbacus.jar -------------------------------------------------------------------------------- /tests/abacus/FakeAbacus/compile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIRNAME=`dirname $0` 4 | 5 | cd $DIRNAME/src 6 | javac ./**/*.java ./*.java 7 | 8 | if [ $? -eq 0 ]; then 9 | jar cevf FakeAbacus ../bin/FakeAbacus.jar ./**/*.class ./*.class 10 | else 11 | exit 1 12 | fi 13 | 14 | find . -name '*.class' -delete 15 | -------------------------------------------------------------------------------- /tests/abacus/FakeAbacus/src/FakeAbacus.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.TimeUnit; 2 | import cmd.ArgsTemplement; 3 | import java.io.*; 4 | import java.nio.file.*; 5 | 6 | public class FakeAbacus { 7 | public static void main (String args[]) throws InterruptedException, IOException { 8 | ArgsTemplement at = new ArgsTemplement(args); 9 | 10 | if (!at.specifiedOption("-in") || !at.specifiedOption("-out")) error(); 11 | String input = at.getValue("-in"); 12 | String output = at.getValue("-out"); 13 | 14 | checkFile(input); 15 | Files.copy(new File(input).toPath(), new File(output).toPath(), StandardCopyOption.REPLACE_EXISTING); 16 | 17 | TimeUnit.SECONDS.sleep(1); 18 | System.out.println("ENERGY"); 19 | } 20 | 21 | public static void error () { 22 | System.out.println("ERROR"); 23 | System.exit(1); 24 | } 25 | 26 | public static void checkFile (String name) { 27 | File f = new File(name); 28 | System.out.println(name); 29 | if (!(f.exists() && !f.isDirectory())) error(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/abacus/FakeAbacus/src/cmd/ArgsTemplement.java: -------------------------------------------------------------------------------- 1 | package cmd; 2 | import java.util.*; 3 | 4 | public class ArgsTemplement { 5 | 6 | private ArrayList