├── .eslintignore
├── src
├── snovault
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_snowflake_hash.py
│ │ ├── testing_auditor.py
│ │ ├── testing_key.py
│ │ ├── testing_upgrader.py
│ │ ├── pyramidfixtures.py
│ │ ├── test_schema_utils.py
│ │ ├── test_indexer_state.py
│ │ ├── test_searches_configs.py
│ │ ├── test_upgrader.py
│ │ ├── toolfixtures.py
│ │ ├── test_searches_fields.py
│ │ └── test_link.py
│ ├── commands
│ │ ├── __init__.py
│ │ ├── es_index_data.py
│ │ ├── jsonld_rdf.py
│ │ └── spreadsheet_to_json.py
│ ├── elasticsearch
│ │ ├── searches
│ │ │ ├── __init__.py
│ │ │ ├── interfaces.py
│ │ │ ├── fields.py
│ │ │ └── configs.py
│ │ ├── tests
│ │ │ └── __init__.py
│ │ ├── uuid_queue
│ │ │ ├── tests
│ │ │ │ └── __init__.py
│ │ │ └── __init__.py
│ │ ├── interfaces.py
│ │ └── cached_views.py
│ ├── interfaces.py
│ ├── predicates.py
│ ├── nginx-dev.conf
│ ├── json_renderer.py
│ ├── jsongraph.py
│ ├── cache.py
│ ├── snowflake_hash.py
│ ├── local_storage.py
│ └── validators.py
└── snowflakes
│ ├── tests
│ ├── features
│ │ ├── __init__.py
│ │ ├── test_workbook.py
│ │ ├── test_admin_user.py
│ │ ├── test_submitter_user.py
│ │ ├── test_nodata.py
│ │ ├── user.feature
│ │ ├── toolbar.feature
│ │ ├── title.feature
│ │ ├── page.feature
│ │ ├── test_generics.py
│ │ ├── search.feature
│ │ ├── generics.feature
│ │ └── forms.feature
│ ├── data
│ │ ├── inserts
│ │ │ ├── small_db.tsv
│ │ │ ├── image.json
│ │ │ └── snowfort.json
│ │ └── documents
│ │ │ ├── selma.jpg
│ │ │ ├── jocelyn.jpg
│ │ │ └── frowny_gel.png
│ ├── test_create_mapping.py
│ ├── test_renderers.py
│ ├── test_graph.py
│ ├── __init__.py
│ ├── test_upgrade_award.py
│ ├── test_webuser_auth.py
│ ├── test_schemas.py
│ └── test_server_defaults.py
│ ├── commands
│ ├── __init__.py
│ ├── es_index_data.py
│ ├── create_admin_user.py
│ ├── jsonld_rdf.py
│ └── spreadsheet_to_json.py
│ ├── static
│ ├── scss
│ │ ├── snowflakes
│ │ │ ├── _bootstrap-lib.scss
│ │ │ ├── modules
│ │ │ │ ├── _lists.scss
│ │ │ │ ├── _breadcrumbs.scss
│ │ │ │ ├── _loading-spinner.scss
│ │ │ │ ├── _modals.scss
│ │ │ │ ├── _signin-box.scss
│ │ │ │ ├── _lightbox.scss
│ │ │ │ ├── _tooltip.scss
│ │ │ │ └── _layout-editor.scss
│ │ │ ├── _theme.scss
│ │ │ ├── _mixins-custom.scss
│ │ │ └── _state.scss
│ │ ├── fontawesome
│ │ │ ├── _fixed-width.scss
│ │ │ ├── _core.scss
│ │ │ ├── _bordered-pulled.scss
│ │ │ ├── _larger.scss
│ │ │ ├── _rotated-flipped.scss
│ │ │ ├── _list.scss
│ │ │ ├── _font-awesome.scss
│ │ │ ├── _stacked.scss
│ │ │ ├── _path.scss
│ │ │ ├── _mixins.scss
│ │ │ ├── _spinning.scss
│ │ │ └── _extras.scss
│ │ ├── react-forms
│ │ │ ├── _CheckboxGroup.scss
│ │ │ └── _RadioButtonGroup.scss
│ │ └── bootstrap
│ │ │ ├── _responsive-980px-1199px.scss
│ │ │ ├── _wells.scss
│ │ │ ├── _component-animations.scss
│ │ │ ├── _breadcrumbs.scss
│ │ │ ├── _close.scss
│ │ │ ├── _thumbnails.scss
│ │ │ ├── _utilities.scss
│ │ │ ├── _jumbotron.scss
│ │ │ ├── _media.scss
│ │ │ ├── _pager.scss
│ │ │ ├── _badges.scss
│ │ │ ├── _labels.scss
│ │ │ ├── _code.scss
│ │ │ ├── _alerts.scss
│ │ │ ├── _grid.scss
│ │ │ ├── _progress-bars.scss
│ │ │ ├── _print.scss
│ │ │ ├── _responsive-utilities.scss
│ │ │ └── _pagination.scss
│ ├── dev-robots.txt
│ ├── components
│ │ ├── lib
│ │ │ └── index.js
│ │ ├── testdata
│ │ │ ├── lab.js
│ │ │ ├── award.js
│ │ │ └── submitter.js
│ │ ├── blocks
│ │ │ ├── index.js
│ │ │ ├── fallback.js
│ │ │ └── item.js
│ │ ├── inputs
│ │ │ ├── index.js
│ │ │ └── file.js
│ │ ├── ImpersonateUserSchema.js
│ │ ├── JSONNode.js
│ │ ├── __tests__
│ │ │ ├── .jshintrc
│ │ │ └── server-render-test.js
│ │ ├── testing.js
│ │ ├── index.js
│ │ ├── objectutils.js
│ │ ├── page.js
│ │ ├── home.js
│ │ ├── statuslabel.js
│ │ ├── StickyHeader.js
│ │ └── schema.js
│ ├── google63612883561ae8ff.html
│ ├── img
│ │ ├── asc.gif
│ │ ├── bg.gif
│ │ ├── desc.gif
│ │ ├── file.png
│ │ ├── favicon.ico
│ │ ├── file-pdf.png
│ │ ├── som-logo.png
│ │ ├── spinner1.gif
│ │ ├── su-logo.png
│ │ ├── ucsc-logo.png
│ │ ├── close-icon.png
│ │ ├── file-broken.png
│ │ ├── som-logo-red.png
│ │ ├── su-logo-white.png
│ │ ├── su-logo-white-2x.png
│ │ ├── ucsc-logo-white.png
│ │ ├── orientation-icons.png
│ │ ├── spinner-orange-bg.gif
│ │ ├── ucsc-logo-white-alt.png
│ │ ├── glyphicons-halflings.png
│ │ ├── ucsc-logo-white-alt-2x.png
│ │ ├── glyphicons-halflings-white.png
│ │ ├── checker.svg
│ │ ├── hiding-dots.svg
│ │ └── close-icon.svg
│ ├── build
│ │ └── .gitignore
│ ├── mime.types
│ ├── robots.txt
│ ├── font
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ └── fontawesome-webfont.woff
│ ├── libs
│ │ ├── react-patches.js
│ │ ├── closest.js
│ │ ├── noarg-memoize.js
│ │ ├── offset.js
│ │ ├── __tests__
│ │ │ ├── .jshintrc
│ │ │ └── registry-test.js
│ │ ├── jsonScriptEscape.js
│ │ ├── origin.js
│ │ ├── bootstrap
│ │ │ ├── dropdown-menu.js
│ │ │ └── panel.js
│ │ ├── registry.js
│ │ ├── compat.js
│ │ ├── react-middleware.js
│ │ └── svg-icons.js
│ ├── inline.js
│ ├── browser.js
│ └── server.js
│ ├── audit
│ └── __init__.py
│ ├── schemas
│ ├── namespaces.json
│ ├── changelogs
│ │ ├── example.md
│ │ └── award.md
│ ├── image.json
│ └── access_key.json
│ ├── upgrade
│ ├── user.py
│ ├── snowset.py
│ ├── award.py
│ └── __init__.py
│ ├── types
│ ├── image.py
│ └── __init__.py
│ ├── schema_formats.py
│ ├── memlimit.py
│ ├── typedsheets.py
│ └── authorization.py
├── MANIFEST.in
├── setup.cfg
├── demo.cfg
├── node_shims
├── ckeditor
│ ├── index.js
│ └── package.json
└── google-analytics
│ ├── index.js
│ └── package.json
├── etc
└── logging-apache.conf
├── test.cfg
├── Makefile
├── cloudwatchmon-requirements.txt
├── docs
├── rendering-overview.pdf
└── search.rst
├── conf
├── elasticsearch.yml
└── jvm.options
├── requirements.txt
├── requirements.osx.txt
├── candidate.cfg
├── .babelrc
├── .eslintrc.json
├── jest
└── environment.js
├── .gitignore
├── circle-tests.sh
├── gulpfile.js
├── pytest.ini
├── config.rb
├── scripts
├── embeds.py
├── LogToCsv.py
└── blackholes.py
├── LICENSE.txt
├── production.ini.in
├── examples
└── s3cp.py
├── development.ini
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/testdata
2 |
--------------------------------------------------------------------------------
/src/snovault/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.rst
2 | include *.md
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 100
3 |
--------------------------------------------------------------------------------
/src/snovault/commands/__init__.py:
--------------------------------------------------------------------------------
1 | # package
2 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/searches/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo.cfg:
--------------------------------------------------------------------------------
1 | [buildout]
2 | extends = buildout.cfg
3 |
--------------------------------------------------------------------------------
/src/snowflakes/commands/__init__.py:
--------------------------------------------------------------------------------
1 | # package
2 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/uuid_queue/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/_bootstrap-lib.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/node_shims/ckeditor/index.js:
--------------------------------------------------------------------------------
1 | module.exports = global.CKEDITOR;
2 |
--------------------------------------------------------------------------------
/src/snowflakes/static/dev-robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/src/snowflakes/audit/__init__.py:
--------------------------------------------------------------------------------
1 | def includeme(config):
2 | config.scan()
3 |
--------------------------------------------------------------------------------
/etc/logging-apache.conf:
--------------------------------------------------------------------------------
1 | CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined_stats
2 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./store');
4 |
--------------------------------------------------------------------------------
/src/snowflakes/static/google63612883561ae8ff.html:
--------------------------------------------------------------------------------
1 | google-site-verification: google63612883561ae8ff.html
--------------------------------------------------------------------------------
/test.cfg:
--------------------------------------------------------------------------------
1 | [buildout]
2 | extends = buildout.cfg
3 |
4 | [production-ini]
5 | indexer_processes = 16
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | clean:
2 | rm -rf node_modules parts
3 | rm -rf .sass-cache
4 | rm -rf src/snowflakes/static/css
5 |
--------------------------------------------------------------------------------
/cloudwatchmon-requirements.txt:
--------------------------------------------------------------------------------
1 | argparse==1.2.1
2 | boto==2.38.0
3 | cloudwatchmon==2.0.3
4 | wsgiref==0.1.2
5 |
--------------------------------------------------------------------------------
/docs/rendering-overview.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/docs/rendering-overview.pdf
--------------------------------------------------------------------------------
/src/snowflakes/schemas/namespaces.json:
--------------------------------------------------------------------------------
1 | {
2 | "_comment": "This should contain keys and URL references"
3 | }
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/searches/interfaces.py:
--------------------------------------------------------------------------------
1 | NON_SORTABLE = 'non_sortable'
2 | SEARCH_CONFIG = 'search_config'
3 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/asc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/asc.gif
--------------------------------------------------------------------------------
/src/snowflakes/static/img/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/bg.gif
--------------------------------------------------------------------------------
/src/snowflakes/static/img/desc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/desc.gif
--------------------------------------------------------------------------------
/src/snowflakes/static/img/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/file.png
--------------------------------------------------------------------------------
/src/snowflakes/static/build/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/inserts/small_db.tsv:
--------------------------------------------------------------------------------
1 | 7a5e9183-b52f-4f75-9708-8e077b086b4e
2 | e2f35c88-a792-4dea-b5d2-30dc52ed2495
3 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/favicon.ico
--------------------------------------------------------------------------------
/src/snowflakes/static/img/file-pdf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/file-pdf.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/som-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/som-logo.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/spinner1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/spinner1.gif
--------------------------------------------------------------------------------
/src/snowflakes/static/img/su-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/su-logo.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/ucsc-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/ucsc-logo.png
--------------------------------------------------------------------------------
/src/snowflakes/static/mime.types:
--------------------------------------------------------------------------------
1 | application/font-woff woff
2 | text/autosql as
3 | text/markdown md
4 | image/tiff svs
5 |
--------------------------------------------------------------------------------
/src/snowflakes/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /*?*limit=all
3 | Disallow: /files/*download
4 | Disallow: /cgi-bin/
5 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/close-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/close-icon.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/file-broken.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/file-broken.png
--------------------------------------------------------------------------------
/conf/elasticsearch.yml:
--------------------------------------------------------------------------------
1 | cluster.name: elasticsearch_test_fixture
2 | discovery.type: single-node
3 | indices.query.bool.max_clause_count: 8192
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pip==20.0.2
2 | psycopg2==2.8.4
3 | redis==3.5.3
4 | redis-server==5.0.7
5 | setuptools==45.1.0
6 | zc.buildout==2.13.2
7 |
--------------------------------------------------------------------------------
/src/snowflakes/static/font/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/font/FontAwesome.otf
--------------------------------------------------------------------------------
/src/snowflakes/static/img/som-logo-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/som-logo-red.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/su-logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/su-logo-white.png
--------------------------------------------------------------------------------
/requirements.osx.txt:
--------------------------------------------------------------------------------
1 | pip==20.0.2
2 | psycopg2==2.8.4
3 | redis==3.5.3
4 | redis-server==5.0.7
5 | setuptools==45.1.0
6 | zc.buildout==2.13.2
7 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/su-logo-white-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/su-logo-white-2x.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/ucsc-logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/ucsc-logo-white.png
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/documents/selma.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/tests/data/documents/selma.jpg
--------------------------------------------------------------------------------
/src/snowflakes/static/img/orientation-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/orientation-icons.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/spinner-orange-bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/spinner-orange-bg.gif
--------------------------------------------------------------------------------
/src/snowflakes/static/img/ucsc-logo-white-alt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/ucsc-logo-white-alt.png
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/documents/jocelyn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/tests/data/documents/jocelyn.jpg
--------------------------------------------------------------------------------
/src/snowflakes/static/font/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/font/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/src/snowflakes/static/font/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/font/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/src/snowflakes/static/font/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/font/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/src/snowflakes/static/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/documents/frowny_gel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/tests/data/documents/frowny_gel.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/ucsc-logo-white-alt-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/ucsc-logo-white-alt-2x.png
--------------------------------------------------------------------------------
/src/snowflakes/static/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ENCODE-DCC/snovault/HEAD/src/snowflakes/static/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/src/snowflakes/static/components/testdata/lab.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "@id": "/labs/thomas-gingeras/",
3 | "@type": ["lab", "item"],
4 | "title": "Thomas Gingeras, CSHL"
5 | };
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/react-patches.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // These patches must be executed before any call to require('react').
3 |
4 | // There are currently no patches pending ;)
5 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/blocks/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./fallback');
4 | require('./richtext');
5 | require('./item');
6 | require('./search');
7 | require('./teaser');
8 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/react-forms/_CheckboxGroup.scss:
--------------------------------------------------------------------------------
1 | .rf-CheckboxGroup__button {
2 | @include checkbox;
3 | }
4 |
5 | .rf-CheckboxGroup__caption {
6 | margin-left: 10px;
7 | @include user-select(none);
8 | }
--------------------------------------------------------------------------------
/node_shims/google-analytics/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /* global ga */
3 | global.ga = global.ga || function () {
4 | (ga.q = ga.q || []).push(arguments);
5 | };
6 | ga.l = +new Date();
7 | module.exports = global.ga;
8 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/closest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = function closest(el, selector) {
3 | while (el) {
4 | if (el.matches(selector)) return el;
5 | el = el.parentElement;
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/react-forms/_RadioButtonGroup.scss:
--------------------------------------------------------------------------------
1 | .rf-RadioButtonGroup__button {
2 | @include radio;
3 | }
4 |
5 | .rf-RadioButtonGroup__caption {
6 | margin-left: 10px;
7 | @include user-select(none);
8 | }
9 |
--------------------------------------------------------------------------------
/candidate.cfg:
--------------------------------------------------------------------------------
1 | [buildout]
2 | extends = buildout.cfg
3 |
4 | [production-ini]
5 | accession_factory = snowflakes.server_defaults.enc_accession
6 | file_upload_bucket = snowflake-files
7 | blob_bucket = snovault-blobs
8 | indexer_processes = 16
--------------------------------------------------------------------------------
/src/snowflakes/schemas/changelogs/example.md:
--------------------------------------------------------------------------------
1 | =================================
2 | Example Change Log
3 | =================================
4 |
5 | Schema version 2
6 | ----------------
7 |
8 | * *object_type had the some properties removed
--------------------------------------------------------------------------------
/src/snowflakes/static/components/testdata/award.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "@id": "/awards/RC2HG005602/",
3 | "@type": ["Award", "Item"],
4 | "name": "RC2HG005602",
5 | "project": "ENCODE",
6 | "rfa": "ENCODE3"
7 | };
8 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | presets: [
3 | '@babel/preset-env',
4 | '@babel/preset-react',
5 | '@babel/flow',
6 | ],
7 | plugins: [
8 | '@babel/plugin-proposal-object-rest-spread',
9 | '@babel/plugin-transform-modules-commonjs',
10 | ]
11 | }
--------------------------------------------------------------------------------
/src/snowflakes/upgrade/user.py:
--------------------------------------------------------------------------------
1 | from snovault import upgrade_step
2 |
3 |
4 | @upgrade_step('user', '', '3')
5 | def user_0_3(value, system):
6 | # http://encode.stanford.edu/issues/1307
7 | if 'status' in value:
8 | value['status'] = value['status'].lower()
9 |
--------------------------------------------------------------------------------
/node_shims/ckeditor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ckeditor",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "MIT"
11 | }
12 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/testdata/submitter.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "@id": "/users/0abbd494-b852-433c-b360-93996f679dae/",
3 | "@type": ["User", "Item"],
4 | "id": "0abbd494-b852-433c-b360-93996f679dae",
5 | "lab": "/labs/thomas-gingeras/",
6 | "title": "Ad Est"
7 | };
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/test_workbook.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_bdd import scenarios
3 |
4 | pytestmark = [
5 | pytest.mark.bdd,
6 | pytest.mark.usefixtures('workbook'),
7 | ]
8 |
9 | scenarios(
10 | 'search.feature',
11 | strict_gherkin=False
12 | )
13 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/checker.svg:
--------------------------------------------------------------------------------
1 | checker
--------------------------------------------------------------------------------
/node_shims/google-analytics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-analytics",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "MIT"
11 | }
12 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/inputs/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var FileInput = module.exports.FileInput = require('./file');
4 | var ObjectPicker = module.exports.ObjectPicker = require('./object').ObjectPicker;
5 | var ItemPreview = module.exports.ItemPreview = require('./object').ItemPreview;
6 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/test_admin_user.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_bdd import scenarios
3 |
4 | pytestmark = [
5 | pytest.mark.bdd,
6 | pytest.mark.usefixtures('workbook', 'admin_user'),
7 | ]
8 |
9 |
10 | scenarios(
11 | 'forms.feature',
12 | 'page.feature',
13 | strict_gherkin=False
14 | )
15 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/ImpersonateUserSchema.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var ReactForms = require('react-forms');
3 |
4 | module.exports = ReactForms.schema.Mapping({}, {
5 | userid: ReactForms.schema.Scalar({
6 | label: 'User',
7 | hint: 'Enter the email of the user you want to impersonate.',
8 | }),
9 | });
10 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_lists.scss:
--------------------------------------------------------------------------------
1 | // For displaying a list in a panel where you'd normally find the dl/dt/dd value pairs.
2 | .non-dl-list {
3 | list-style: none;
4 | margin: 0 0 0 60px;
5 | }
6 |
7 | // For displaying a single-item where you'd otherwise find lists
8 | .non-dl-item {
9 | margin-left: 60px;
10 | }
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/test_submitter_user.py:
--------------------------------------------------------------------------------
1 |
2 | # import pytest
3 | # from pytest_bdd import scenarios
4 |
5 | # pytestmark = [
6 | # pytest.mark.bdd,
7 | # pytest.mark.usefixtures('workbook', 'submitter_user'),
8 | # ]
9 |
10 |
11 | # scenarios(
12 | # 'user.feature',
13 | # strict_gherkin=False
14 | # )
15 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/test_nodata.py:
--------------------------------------------------------------------------------
1 | """ These scenarios do not require the workbook fixture, but get it anyway.
2 | """
3 | import pytest
4 | from pytest_bdd import scenarios
5 |
6 | pytestmark = [
7 | pytest.mark.bdd,
8 | ]
9 |
10 | scenarios(
11 | 'title.feature',
12 | 'toolbar.feature',
13 | strict_gherkin=False
14 | )
15 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/interfaces.py:
--------------------------------------------------------------------------------
1 | from zope.interface import Interface
2 |
3 | # Registry tool id
4 | APP_FACTORY = 'app_factory'
5 | ELASTIC_SEARCH = 'elasticsearch'
6 | SNP_SEARCH_ES = 'snp_search'
7 | INDEXER = 'indexer'
8 | RESOURCES_INDEX = 'snovault-resources'
9 |
10 |
11 | class ICachedItem(Interface):
12 | """ Marker for cached Item
13 | """
14 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_core.scss:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix} {
5 | display: inline-block;
6 | font-family: FontAwesome;
7 | font-style: normal;
8 | font-weight: normal;
9 | line-height: 1;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/noarg-memoize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // Exists to work around https://github.com/prometheusresearch/react-forms/issues/70
3 | module.exports = function noarg_memoize(fn) {
4 | var value;
5 | return function () {
6 | if (value === undefined) {
7 | value = fn();
8 | }
9 | return value;
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/offset.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // http://www.quirksmode.org/js/findpos.html
3 | module.exports = function offset(el) {
4 | var curleft = 0;
5 | var curtop = 0;
6 | do {
7 | curleft += el.offsetLeft;
8 | curtop += el.offsetTop;
9 | } while (el = el.offsetParent);
10 | return {left: curleft, top: curtop};
11 | };
12 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/hiding-dots.svg:
--------------------------------------------------------------------------------
1 | Indicates hiddent content
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_create_mapping.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from ..loadxl import ORDER
3 |
4 |
5 | @pytest.mark.parametrize('item_type', ORDER)
6 | def test_create_mapping(registry, item_type):
7 | from snovault.elasticsearch.create_mapping import type_mapping
8 | from snovault import TYPES
9 | mapping = type_mapping(registry[TYPES], item_type)
10 | assert mapping
11 |
--------------------------------------------------------------------------------
/src/snowflakes/static/img/close-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/user.feature:
--------------------------------------------------------------------------------
1 | Feature: User Profile
2 |
3 | Scenario Outline: View profile
4 | When I visit "/"
5 | And I click the link with text that contains "J. Michael Cherry"
6 | And I click the link with text that contains "Profile"
7 | Then I should see "J. Michael Cherry, Stanford"
8 | And I should see an element with the css selector ".access-keys"
9 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/__tests__/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "node": true,
4 | "predef": [
5 | "afterEach",
6 | "beforeEach",
7 | "describe",
8 | "expect",
9 | "it",
10 | "jest",
11 | "pit",
12 | "xdescribe",
13 | "xit"
14 | ],
15 | "globalstrict": true,
16 | "newcap": false,
17 | "sub": true
18 | }
19 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/jsonScriptEscape.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var SUBS = {'&': '\\u0026', '<': '\\u003C', '>': '\\u003E'};
3 | var unsafe_re = /[\<\>\&]/g;
4 |
5 |
6 | var sub = function (match) {
7 | return SUBS[match];
8 | };
9 |
10 |
11 | var jsonScriptEscape = function (json_string) {
12 | return json_string.replace(unsafe_re, sub);
13 | };
14 |
15 |
16 | module.exports = jsonScriptEscape;
17 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/JSONNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var ReactForms = require('react-forms');
3 |
4 | class JSONNode extends ReactForms.schema.ScalarNode {
5 | serialize(value) {
6 | return JSON.stringify(value, null, 4);
7 | }
8 | deserialize(value) {
9 | return (typeof value === 'string') ? JSON.parse(value) : value;
10 | }
11 | }
12 |
13 | module.exports = JSONNode;
14 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_breadcrumbs.scss:
--------------------------------------------------------------------------------
1 | .breadcrumb {
2 | padding: 5px 0;
3 | background-color: transparent;
4 | border-bottom: 1px solid #c0c0c0;
5 |
6 | > li {
7 | text-transform: uppercase;
8 | font-weight: bold;
9 | font-size: 12px;
10 | color: #808080;
11 |
12 | + li:before {
13 | padding: 0 5px 0 10px;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/__tests__/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "node": true,
4 | "predef": [
5 | "DOMParser",
6 | "afterEach",
7 | "beforeEach",
8 | "describe",
9 | "expect",
10 | "it",
11 | "jest",
12 | "pit",
13 | "xdescribe",
14 | "xit"
15 | ],
16 | "globalstrict": true,
17 | "newcap": false,
18 | "sub": true
19 | }
20 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_bordered-pulled.scss:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em $fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .pull-right { float: right; }
11 | .pull-left { float: left; }
12 |
13 | .#{$fa-css-prefix} {
14 | &.pull-left { margin-right: .3em; }
15 | &.pull-right { margin-left: .3em; }
16 | }
17 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_larger.scss:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .#{$fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/testing.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var globals = require('./globals');
4 |
5 |
6 | var TestingRenderErrorPanel = module.exports.TestingRenderErrorPanel = React.createClass({
7 | render: function() {
8 | console.log('log');
9 | console.warn('warn');
10 | this.method_does_not_exist();
11 | }
12 | });
13 |
14 | globals.panel_views.register(TestingRenderErrorPanel, 'TestingRenderError');
15 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/uuid_queue/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Uuid Queue Module Adapter
3 |
4 | - QueueAdapter Class allows access to all queue types
5 | defined in QueueTypes through a set of standard methods.
6 | - All queues in ./queues should adhere to QueueAdapter standards.
7 | - Adapter queue has a server and a worker.
8 | - Another important object is the meta data needed to run the queue.
9 | """
10 | from .adapter_queue import QueueAdapter
11 | from .adapter_queue import QueueTypes
12 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_rotated-flipped.scss:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
7 |
8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
10 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/toolbar.feature:
--------------------------------------------------------------------------------
1 | @toolbar
2 | Feature: Toolbar
3 |
4 | Scenario: Active section
5 | When I visit "/"
6 | #Then I should see an element with the css selector "#global-sections > li.active > a[href='/']"
7 | Then I should not see an element with the css selector "#global-sections > li.active > a:not([href='/'])"
8 | And I should see an element with the css selector "#loginbtn"
9 | And I should see "Where all objects are special"
10 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_list.scss:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: $fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .#{$fa-css-prefix}-li {
11 | position: absolute;
12 | left: -$fa-li-width;
13 | width: $fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.#{$fa-css-prefix}-lg {
17 | left: -$fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/title.feature:
--------------------------------------------------------------------------------
1 | @title
2 | Feature: Title
3 |
4 | Scenario: Title updates
5 | When I visit "/"
6 | And I wait for the content to load
7 | Then the title should contain the text "Snowflakes... By SnoVault"
8 | When I click the link with text that contains "Data"
9 | And I click the link to "/search/?type=Snowfort"
10 | And I wait for the content to load
11 | Then the title should contain the text "Search – SNOWFLAKES"
12 |
--------------------------------------------------------------------------------
/src/snowflakes/schemas/changelogs/award.md:
--------------------------------------------------------------------------------
1 | Change log for award.json
2 | =========================
3 |
4 |
5 | Schema version 2
6 | ----------------
7 |
8 | * Default values of '' were removed. You can no longer submit a blank url (url='')
9 |
10 | * *status* was brought into line with other objects that are shared. Disabled grants with rfa in ['ENCODE2', 'ENCODE2-Mouse']:
11 |
12 | "enum" : [
13 | "current",
14 | "deleted",
15 | "replaced",
16 | "disabled"
17 | ]
18 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_font-awesome.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables";
7 | @import "mixins";
8 | @import "path";
9 | @import "core";
10 | //@import "larger";
11 | //@import "fixed-width";
12 | //@import "list";
13 | //@import "bordered-pulled";
14 | //@import "spinning";
15 | //@import "rotated-flipped";
16 | //@import "stacked";
17 | @import "icons";
18 |
--------------------------------------------------------------------------------
/src/snowflakes/upgrade/snowset.py:
--------------------------------------------------------------------------------
1 | from snovault import upgrade_step
2 |
3 | ''' Note these are not relevant to anything beyond testing upgrader '''
4 |
5 |
6 | @upgrade_step('snowball', '', '2')
7 | @upgrade_step('snowfort', '', '2')
8 | def snowset_0_2(value, system):
9 | # example upgrade for tests
10 | if 'status' in value:
11 | if value['status'] == 'DELETED':
12 | value['status'] = 'deleted'
13 | elif value['status'] == 'CURRENT':
14 | value['status'] = 'submitted' # there is a dependency on date_released+"released"
15 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_snowflake_hash.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | TEST_HASHES = {
4 | "test": "Jnh+8wNnELksNFVbxkya8RDrxJNL13dUWTXhp5DCx/quTM2/cYn7azzl2Uk3I2zc",
5 | "test2": "sh33L5uQeLr//jJULb7mAnbVADkkWZrgcXx97DCacueGtEU5G2HtqUv73UTS0EI0",
6 | "testing100" * 10: "5rznDSIcDPd/9rjom6P/qkJGtJSV47y/u5+KlkILROaqQ6axhEyVIQTahuBYerLG",
7 | }
8 |
9 |
10 | @pytest.mark.parametrize(('password', 'pwhash'), TEST_HASHES.items())
11 | def test_snowflake_hash(password, pwhash):
12 | from snovault.snowflake_hash import SNOWHash
13 | assert SNOWHash.hash(password) == pwhash
14 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_responsive-980px-1199px.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Tablet to desktop
3 | // --------------------------------------------------
4 |
5 |
6 | @media (min-width: 980px) and (max-width: 1199px) {
7 |
8 | // Fixed grid
9 | @include grid-core($gridColumnWidth, $gridGutterWidth);
10 |
11 | // Fluid grid
12 | @include grid-fluid($fluidGridColumnWidth, $fluidGridGutterWidth);
13 |
14 | // Input grid
15 | @include grid-input($gridColumnWidth, $gridGutterWidth);
16 |
17 | // No need to reset .thumbnails here since it's the same $gridGutterWidth
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_stacked.scss:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_loading-spinner.scss:
--------------------------------------------------------------------------------
1 | .black-overlay {
2 | display: none;
3 | background-color: black;
4 | left: 0%;
5 | opacity: 0.5;
6 | position: absolute;
7 | height: 100%;
8 | top: 0%;
9 | width: 100%;
10 | z-index: 1001;
11 | }
12 |
13 | .loading-spinner {
14 | background: url('../img/spinner1.gif') no-repeat;
15 | display: none;
16 | left: 50%;
17 | overflow: auto;
18 | position: absolute;
19 | top: 9%;
20 | width: 30px;
21 | height: 30px;
22 | z-index: 1002;
23 | }
24 |
25 | .communicating .black-overlay, .communicating .loading-spinner {
26 | display: block;
27 | }
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "indent": [2, 4, {"SwitchCase": 1}],
4 | "linebreak-style": [2, "unix"],
5 | "semi": [2, "always"],
6 | "no-console": 0,
7 | "no-unused-vars": 0,
8 | "no-empty": 0
9 | },
10 | "env": {
11 | "es6": true,
12 | "browser": true,
13 | "commonjs": true
14 | },
15 | "extends": "eslint:recommended",
16 | "parserOptions": {
17 | "ecmaVersion": 6,
18 | "sourceType": "module",
19 | "ecmaFeatures": {
20 | "jsx": true
21 | }
22 | },
23 | "plugins": [
24 | "react"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/jest/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | jest.mock('scriptjs');
3 | var jsdom = require('jsdom').jsdom;
4 |
5 | if (window.DOMParser === undefined) {
6 | // jsdom
7 | window.DOMParser = function DOMParser() {};
8 | window.DOMParser.prototype.parseFromString = function parseFromString(markup, type) {
9 | var parsingMode = 'auto';
10 | type = type || '';
11 | if (type.indexOf('xml') >= 0) {
12 | parsingMode = 'xml';
13 | } else if (type.indexOf('html') >= 0) {
14 | parsingMode = 'html';
15 | }
16 | var doc = jsdom(markup, {parsingMode: parsingMode});
17 | return doc;
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/inserts/image.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Selma the cat",
4 | "attachment": "selma.jpg",
5 | "submitted_by": "david@glicksoftware.com",
6 | "uuid": "b4589f52-521e-45db-8dfa-378d92cbbd86"
7 | },
8 | {
9 | "caption": "Jocelyn",
10 | "attachment": "jocelyn.jpg",
11 | "submitted_by": "fytanaka@stanford.edu",
12 | "uuid": "8a91ae78-731a-4a92-ae7c-273214be408a"
13 | },
14 | {
15 | "caption": "frowny gel",
16 | "attachment": "frowny_gel.png",
17 | "submitted_by": "cricketsloan@stanford.edu",
18 | "uuid": "1a0df3d4-2496-4d1f-ba4f-519bc2a22cfd"
19 | }
20 | ]
--------------------------------------------------------------------------------
/src/snowflakes/static/components/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Require all components to ensure javascript load ordering
4 | require('./lib');
5 | require('./app');
6 | require('./image');
7 | require('./collection');
8 | require('./errors');
9 | require('./footer');
10 | require('./globals');
11 | require('./graph');
12 | require('./doc');
13 | require('./home');
14 | require('./item');
15 | require('./page');
16 | require('./mixins');
17 | require('./search');
18 | require('./report');
19 | require('./testing');
20 | require('./edit');
21 | require('./inputs');
22 | require('./blocks');
23 | require('./user');
24 | require('./schema');
25 |
26 |
27 | module.exports = require('./app');
28 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_renderers.py:
--------------------------------------------------------------------------------
1 | def test_render_error(anonhtmltestapp):
2 | res = anonhtmltestapp.get('/testing-render-error', status=500)
3 | assert res.body.startswith(b'')
4 |
5 |
6 | def test_render_error_multiple_times(anonhtmltestapp):
7 | anonhtmltestapp.get('/testing-render-error', status=500)
8 | res = anonhtmltestapp.get('/testing-render-error', status=500)
9 | assert res.body.startswith(b'')
10 |
11 |
12 | def test_render_error_then_success(anonhtmltestapp):
13 | anonhtmltestapp.get('/testing-render-error', status=500)
14 | res = anonhtmltestapp.get('/', status=200)
15 | assert res.body.startswith(b'')
16 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/origin.js:
--------------------------------------------------------------------------------
1 | /*jshint scripturl:true */
2 | 'use strict';
3 | var url = require('url');
4 |
5 | function same(from, to) {
6 | if (typeof to === 'undefined') {
7 | to = from;
8 | from = document.location.href;
9 | }
10 | if (typeof from === 'string') from = url.parse(from);
11 | if (typeof to === 'string') to = url.parse(url.resolve(from.href, to));
12 |
13 | if (to.protocol === 'data:' || to.protocol === 'javascript:') return true;
14 | if (from.protocol !== to.protocol) return false;
15 | if (from.protocol === 'file:') return from.pathname === to.pathname;
16 | return from.host === to.host;
17 | }
18 |
19 | module.exports.same = same;
20 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_wells.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Wells
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .well {
8 | min-height: 20px;
9 | padding: 19px;
10 | margin-bottom: 20px;
11 | background-color: $well-bg;
12 | border: 1px solid $well-border;
13 | border-radius: $border-radius-base;
14 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
15 | blockquote {
16 | border-color: #ddd;
17 | border-color: rgba(0,0,0,.15);
18 | }
19 | }
20 |
21 | // Sizes
22 | .well-lg {
23 | padding: 24px;
24 | border-radius: $border-radius-large;
25 | }
26 | .well-sm {
27 | padding: 9px;
28 | border-radius: $border-radius-small;
29 | }
30 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/objectutils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var SingleTreatment = module.exports.SingleTreatment = function(treatment) {
4 | var treatmentText = '';
5 |
6 | if (treatment.concentration) {
7 | treatmentText += treatment.concentration + (treatment.concentration_units ? ' ' + treatment.concentration_units : '') + ' ';
8 | }
9 | treatmentText += treatment.treatment_term_name + (treatment.treatment_term_id ? ' (' + treatment.treatment_term_id + ')' : '') + ' ';
10 | if (treatment.duration) {
11 | treatmentText += 'for ' + treatment.duration + ' ' + (treatment.duration_units ? treatment.duration_units : '');
12 | }
13 | return treatmentText;
14 | };
15 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_component-animations.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Component animations
3 | // --------------------------------------------------
4 |
5 | // Heads up!
6 | //
7 | // We don't use the `.opacity()` mixin here since it causes a bug with text
8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.
9 |
10 | .fade {
11 | opacity: 0;
12 | @include transition(opacity .15s linear);
13 | &.in {
14 | opacity: 1;
15 | }
16 | }
17 |
18 | .collapse {
19 | display: none;
20 | &.in {
21 | display: block;
22 | }
23 | }
24 | .collapsing {
25 | position: relative;
26 | height: 0;
27 | overflow: hidden;
28 | @include transition(height .35s ease);
29 | }
30 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_graph.py:
--------------------------------------------------------------------------------
1 | def test_graph_dot(testapp):
2 | res = testapp.get('/profiles/graph.dot', status=200)
3 | assert res.content_type == 'text/vnd.graphviz'
4 | assert res.text
5 |
6 |
7 | def test_graph_svg(testapp):
8 | res = testapp.get('/profiles/graph.svg', status=200)
9 | if not res.content_type == 'image/svg+xml' and res.json.get('status_code') == 404:
10 | # graphviz is probably not installed
11 | msg = res.json.get('message')
12 | assert msg == 'graph.svg is not available'
13 | # Force fail since graphviz is not installed on the system
14 | assert False
15 | assert res.content_type == 'image/svg+xml'
16 | assert res.text
17 |
--------------------------------------------------------------------------------
/src/snowflakes/upgrade/award.py:
--------------------------------------------------------------------------------
1 | from snovault import upgrade_step
2 |
3 | ''' Note these are not relevant to anything beyond testing upgrader '''
4 |
5 |
6 | @upgrade_step('award', '', '2')
7 | def award_0_2(value, system):
8 | # Sample upgrades with tests
9 |
10 | rfa_mapping = ['ENCODE2', 'ENCODE2-Mouse']
11 | if value['rfa'] in rfa_mapping:
12 | value['status'] = 'disabled'
13 | else:
14 | value['status'] = 'current'
15 |
16 | if 'url' in value:
17 | if value['url'] == '':
18 | del value['url']
19 |
20 |
21 | @upgrade_step('award', '2', '3')
22 | def award_2_3(value, system):
23 |
24 | if value['viewing_group'] == 'ENCODE3':
25 | value['viewing_group'] = 'ENCODE'
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .coverage
2 | /.installed.cfg
3 | /.mr.developer.cfg
4 | /.cache/
5 | /.sass-cache/
6 | /annotations.json
7 | /aws-ip-ranges.json
8 | /bin/
9 | /develop/
10 | /develop-eggs/
11 | /downloads/
12 | /eggs/
13 | /extends/
14 | /node_modules/
15 | /npm-shrinkwrap.json
16 | /ontology.json
17 | /parts/
18 | /production.ini
19 | /session-secret.b64
20 | /dist/
21 | /build/
22 | /src/snowflakes/static/css/bootstrap.css
23 | /src/snowflakes/static/css/responsive.css
24 | /src/snowflakes/static/css/style.css
25 | /src/snowflakes/static/scss/_variables.original.scss
26 |
27 | *.DS_Store
28 | *.egg-info
29 | *.pyc
30 | +.project
31 | +.pydevproject
32 | *.log
33 | .python-version
34 | ~$*
35 | .~*#
36 |
37 | .python-version
38 | .vscode
39 | venv
40 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_breadcrumbs.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Breadcrumbs
3 | // --------------------------------------------------
4 |
5 |
6 | .breadcrumb {
7 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal;
8 | margin-bottom: $line-height-computed;
9 | list-style: none;
10 | background-color: $breadcrumb-bg;
11 | border-radius: $border-radius-base;
12 |
13 | > li {
14 | display: inline-block;
15 |
16 | + li:before {
17 | content: "#{$breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
18 | padding: 0 5px;
19 | color: $breadcrumb-color;
20 | }
21 | }
22 |
23 | > .active {
24 | color: $breadcrumb-active-color;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/snovault/tests/testing_auditor.py:
--------------------------------------------------------------------------------
1 | from snovault.auditor import (
2 | audit_checker,
3 | AuditFailure,
4 | )
5 |
6 |
7 | def includeme(config):
8 | config.scan('.testing_views')
9 | config.scan(__name__)
10 |
11 |
12 | def has_condition1(value, system):
13 | return value.get('condition1')
14 |
15 |
16 | @audit_checker('testing_link_source', condition=has_condition1)
17 | def checker1(value, system):
18 | if not value.get('checker1'):
19 | return AuditFailure('testchecker', 'Missing checker1')
20 |
21 |
22 | @audit_checker('testing_link_target')
23 | def testing_link_target_status(value, system):
24 | if value.get('status') == 'CHECK':
25 | if not len(value['reverse']):
26 | return AuditFailure('status', 'Missing reverse items')
27 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_path.scss:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
8 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
9 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
10 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
11 | //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var Layout = require('./layout').Layout;
4 | var globals = require('./globals');
5 | var _ = require('underscore');
6 |
7 |
8 | var Page = module.exports.Page = React.createClass({
9 | render: function() {
10 | var context = this.props.context;
11 | return (
12 |
20 | );
21 | }
22 | });
23 |
24 |
25 | globals.content_views.register(Page, 'Page');
26 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/_theme.scss:
--------------------------------------------------------------------------------
1 | .btn {
2 | font-weight: bold;
3 | @include box-shadow(0 1px 1px rgba(0,0,0,.05), inset 0 -1px 0 rgba(0,0,0,.12), inset 0 1px 0 rgba(255,255,255,.3));
4 | }
5 |
6 | .btn-link {
7 | font-weight: normal;
8 | @include box-shadow(none);
9 | }
10 |
11 | .btn-svgicon {
12 | line-height: 0.9;
13 | margin-right: 0 !important;
14 | border-radius: 0;
15 |
16 | .svg-icon {
17 | fill: #fff;
18 | }
19 |
20 | &:first-child {
21 | border-top-left-radius: $border-radius-small;
22 | border-bottom-left-radius: $border-radius-small;
23 | }
24 |
25 | &:last-child {
26 | border-top-right-radius: $border-radius-small;
27 | border-bottom-right-radius: $border-radius-small;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/circle-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## Helper to run tests locally using same commands as circle ci config
4 | # See: encoded/.circleci/config.yml
5 | #
6 | # Use Cases: No argument defaults to not bdd tests
7 | # $ circle-tests.sh bdd
8 | # $ circle-tests.sh npm
9 | # $ circle-tests.sh
10 | ##
11 |
12 | if [ "$1" == "bdd" ]; then
13 | pytest -v -v --timeout=400 -m "bdd" --tb=short --splinter-implicit-wait 10 --splinter-webdriver chrome --splinter-socket-timeout 300 --chrome-options "--headless --disable-gpu --no-sandbox --disable-dev-shm-usage --disable-extensions --whitelisted-ips --window-size=1920,1080"
14 | exit
15 | fi
16 |
17 | if [ "$1" == "npm" ]; then
18 | npm test
19 | exit
20 | fi
21 |
22 | if [ -z "$1" ]; then
23 | pytest -v -v --timeout=400 -m "not bdd"
24 | exit
25 | fi
26 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_modals.scss:
--------------------------------------------------------------------------------
1 | /* Modals */
2 | .modal {
3 | border: 9px solid #333;
4 | border: 9px solid rgba(0,0,0,.4);
5 | *border: 9px solid #333; /* IE6-7 */
6 | @include border-radius(6px);
7 | @include box-shadow(none);
8 | }
9 |
10 | /* used to mask who screen, for modals etc */
11 | /* STILL USING THE BELOW OR JUST FOR A MOCKUP???????? */
12 | #mask {
13 | position: absolute;
14 | z-index: 9000;
15 | background-color: rgba(0,0,0,0.6);
16 | display: none; // js switches to display: block
17 | height: 4000px; // this should be set in the js to fit screen perfectly
18 | width: 4000px; // this should be set in the js to fit screen perfectly
19 | top: 0;
20 | left: 0;
21 | }
22 |
23 | .unmask {
24 | position: relative;
25 | z-index: 9999;
26 | }
--------------------------------------------------------------------------------
/src/snowflakes/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This has to be part of a plugin to support adding the command line option
2 | import argparse
3 |
4 |
5 | class AppendInt2(argparse._AppendAction):
6 | def __call__(self, parser, namespace, values, option_string=None):
7 | values = (values[0], int(values[1]))
8 | return super(AppendInt2, self).__call__(parser, namespace, values, option_string)
9 |
10 |
11 | def pytest_addoption(parser):
12 | parser.addoption('--browser-arg', nargs=2, dest='browser_args', action='append', type='string')
13 | parser.addoption('--browser-arg-int', nargs=2, dest='browser_args', action=AppendInt2, type='string')
14 | parser.addoption('--chrome-options', action='store', type='string')
15 | parser.addoption('--wsgi-arg', nargs=2, dest='wsgi_args', action='append', type='string')
16 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | @mixin fa-icon-rotate($degrees, $rotation) {
5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
6 | -webkit-transform: rotate($degrees);
7 | -moz-transform: rotate($degrees);
8 | -ms-transform: rotate($degrees);
9 | -o-transform: rotate($degrees);
10 | transform: rotate($degrees);
11 | }
12 |
13 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
15 | -webkit-transform: scale($horiz, $vert);
16 | -moz-transform: scale($horiz, $vert);
17 | -ms-transform: scale($horiz, $vert);
18 | -o-transform: scale($horiz, $vert);
19 | transform: scale($horiz, $vert);
20 | }
21 |
--------------------------------------------------------------------------------
/src/snovault/tests/testing_key.py:
--------------------------------------------------------------------------------
1 | from snovault import (
2 | Item,
3 | collection,
4 | )
5 |
6 | # Test class for keys
7 |
8 |
9 | def includeme(config):
10 | config.scan(__name__)
11 |
12 |
13 | @collection(
14 | 'testing-keys',
15 | properties={
16 | 'title': 'Test keys',
17 | 'description': 'Testing. Testing. 1, 2, 3.',
18 | },
19 | unique_key='testing_alias',
20 | )
21 | class TestingKey(Item):
22 | item_type = 'testing_key'
23 | schema = {
24 | 'type': 'object',
25 | 'properties': {
26 | 'name': {
27 | 'type': 'string',
28 | 'uniqueKey': True,
29 | },
30 | 'alias': {
31 | 'type': 'string',
32 | 'uniqueKey': 'testing_alias',
33 | },
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/snovault/tests/testing_upgrader.py:
--------------------------------------------------------------------------------
1 | from snovault import (
2 | Item,
3 | collection,
4 | )
5 | from snovault.upgrader import (
6 | upgrade_step,
7 | upgrade_finalizer,
8 | )
9 |
10 |
11 | def includeme(config):
12 | config.scan(__name__)
13 | config.add_upgrade('testing_upgrader', '3')
14 |
15 |
16 | @collection('testing-upgrader')
17 | class TestingUpgrader(Item):
18 | item_type = 'testing_upgrader'
19 |
20 |
21 | @upgrade_step('testing_upgrader', '', '2')
22 | def step1(value, system):
23 | value['step1'] = True
24 | return value
25 |
26 |
27 | @upgrade_step('testing_upgrader', '2', '3')
28 | def step2(value, system):
29 | value['step2'] = True
30 | return value
31 |
32 |
33 | @upgrade_finalizer('testing_upgrader')
34 | def finalizer(value, system, version):
35 | value['schema_version'] = version
36 | return value
37 |
--------------------------------------------------------------------------------
/src/snovault/tests/pyramidfixtures.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | # Fixtures for pyramid, embedding
4 |
5 |
6 | @pytest.yield_fixture
7 | def config():
8 | from pyramid.testing import setUp, tearDown
9 | yield setUp()
10 | tearDown()
11 |
12 |
13 | @pytest.yield_fixture
14 | def threadlocals(request, dummy_request, registry):
15 | from pyramid.threadlocal import manager
16 | manager.push({'request': dummy_request, 'registry': registry})
17 | yield manager.get()
18 | manager.pop()
19 |
20 |
21 | @pytest.fixture
22 | def dummy_request(root, registry, app):
23 | from pyramid.request import apply_request_extensions
24 | request = app.request_factory.blank('/dummy')
25 | request.root = root
26 | request.registry = registry
27 | request._stats = {}
28 | request.invoke_subrequest = app.invoke_subrequest
29 | apply_request_extensions(request)
30 | return request
31 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_close.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Close icons
3 | // --------------------------------------------------
4 |
5 |
6 | .close {
7 | float: right;
8 | font-size: ($font-size-base * 1.5);
9 | font-weight: $close-font-weight;
10 | line-height: 1;
11 | color: $close-color;
12 | text-shadow: $close-text-shadow;
13 | @include opacity(.2);
14 |
15 | &:hover,
16 | &:focus {
17 | color: $close-color;
18 | text-decoration: none;
19 | cursor: pointer;
20 | @include opacity(.5);
21 | }
22 |
23 | // [converter] extracted button& to button.close
24 | }
25 |
26 | // Additional properties for button version
27 | // iOS requires the button element instead of an anchor tag.
28 | // If you want the anchor version, it requires `href="#"`.
29 | button.close {
30 | padding: 0;
31 | cursor: pointer;
32 | background: transparent;
33 | border: 0;
34 | -webkit-appearance: none;
35 | }
36 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_spinning.scss:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-spin {
5 | -webkit-animation: spin 2s infinite linear;
6 | -moz-animation: spin 2s infinite linear;
7 | -o-animation: spin 2s infinite linear;
8 | animation: spin 2s infinite linear;
9 | }
10 |
11 | @-moz-keyframes spin {
12 | 0% { -moz-transform: rotate(0deg); }
13 | 100% { -moz-transform: rotate(359deg); }
14 | }
15 | @-webkit-keyframes spin {
16 | 0% { -webkit-transform: rotate(0deg); }
17 | 100% { -webkit-transform: rotate(359deg); }
18 | }
19 | @-o-keyframes spin {
20 | 0% { -o-transform: rotate(0deg); }
21 | 100% { -o-transform: rotate(359deg); }
22 | }
23 | @keyframes spin {
24 | 0% {
25 | -webkit-transform: rotate(0deg);
26 | transform: rotate(0deg);
27 | }
28 | 100% {
29 | -webkit-transform: rotate(359deg);
30 | transform: rotate(359deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/data/inserts/snowfort.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "accession": "SNOSS727WCB",
4 | "award": "U54HG007004",
5 | "description": "Sample Snowfort",
6 | "status": "released",
7 | "lab": "michael-snyder",
8 | "submitted_by": "facilisi.tristique@potenti.vivamus",
9 | "uuid": "4eafdd35-40ea-463a-9e05-c85fb91d25d0",
10 | "method": "shovel",
11 | "size": "grande",
12 | "date_released": "2014-10-07"
13 | },
14 | {
15 | "accession": "SNOSS000AER",
16 | "award": "U54HG007004",
17 | "description": "Sample empty snowfort",
18 | "lab": "thomas-gingeras",
19 | "status": "released",
20 | "date_released": "2016-01-01",
21 | "method": "bulldozer",
22 | "size": "venti",
23 | "submitted_by": "dignissim.euismod@amet.habitant",
24 | "uuid": "5a6d5a57-e62d-44b9-a1bd-5d1815247348"
25 | }
26 | ]
27 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/bootstrap/dropdown-menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 |
4 |
5 | // Render a dropdown menu. All components within the dropdown get wrapped in tags, so the 'a' elements in:
6 | //
7 | //
8 | // First
9 | // Second
10 | //
11 | //
12 | // ...get rendered as
13 | // First
14 | // Second
15 |
16 | var DropdownMenu = module.exports.DropdownMenu = React.createClass({
17 | propTypes: {
18 | label: React.PropTypes.string.isRequired // id attribute value for the button that controls this menu
19 | },
20 |
21 | render: function() {
22 | return (
23 |
24 | {this.props.children.map((child, i) => {child} )}
25 |
26 | );
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/searches/fields.py:
--------------------------------------------------------------------------------
1 | from snosearch.fields import ResponseField
2 | from snovault.elasticsearch.create_mapping import TEXT_FIELDS
3 | from snovault.elasticsearch.searches.interfaces import NON_SORTABLE
4 |
5 |
6 | class NonSortableResponseField(ResponseField):
7 |
8 | def __init__(self, *args, **kwargs):
9 | super().__init__(*args, **kwargs)
10 |
11 | def render(self, *args, **kwargs):
12 | self.parent = kwargs.get('parent')
13 | return {
14 | NON_SORTABLE: TEXT_FIELDS
15 | }
16 |
17 |
18 | class PassThroughResponseField(ResponseField):
19 | '''
20 | Passes input values (dictionary) to output.
21 | '''
22 | def __init__(self, *args, **kwargs):
23 | self.values_to_pass_through = kwargs.pop('values_to_pass_through', {})
24 | super().__init__(*args, **kwargs)
25 |
26 | def render(self, *args, **kwargs):
27 | return self.values_to_pass_through
28 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_schema_utils.py:
--------------------------------------------------------------------------------
1 | from snovault.schema_utils import validate
2 | import pytest
3 |
4 |
5 | targets = [
6 | {'name': 'one', 'uuid': '775795d3-4410-4114-836b-8eeecf1d0c2f'},
7 | ]
8 |
9 |
10 | @pytest.fixture
11 | def content(testapp):
12 | url = '/testing-link-targets/'
13 | for item in targets:
14 | testapp.post_json(url, item, status=201)
15 |
16 |
17 | def test_uniqueItems_validates_normalized_links(content, threadlocals):
18 | schema = {
19 | 'uniqueItems': True,
20 | 'items': {
21 | 'linkTo': 'TestingLinkTarget',
22 | }
23 | }
24 | uuid = targets[0]['uuid']
25 | data = [
26 | uuid,
27 | '/testing-link-targets/{}'.format(uuid),
28 | ]
29 | validated, errors = validate(schema, data)
30 | assert len(errors) == 1
31 | assert (
32 | errors[0].message == "['{}', '{}'] has non-unique elements".format(
33 | uuid, uuid)
34 | )
35 |
--------------------------------------------------------------------------------
/src/snovault/interfaces.py:
--------------------------------------------------------------------------------
1 | # Tool names
2 | AUDITOR = 'auditor'
3 | BLOBS = 'blobs'
4 | CALCULATED_PROPERTIES = 'calculated_properties'
5 | COLLECTIONS = 'collections'
6 | CONNECTION = 'connection'
7 | DBSESSION = 'dbsession'
8 | STORAGE = 'storage'
9 | ROOT = 'root'
10 | TYPES = 'types'
11 | UPGRADER = 'upgrader'
12 |
13 | # Constants
14 | PHASE1_5_CONFIG = -15
15 | PHASE2_5_CONFIG = -5
16 |
17 |
18 | # Events
19 | class Created(object):
20 | def __init__(self, object, request):
21 | self.object = object
22 | self.request = request
23 |
24 |
25 | class BeforeModified(object):
26 | def __init__(self, object, request):
27 | self.object = object
28 | self.request = request
29 |
30 |
31 | class AfterModified(object):
32 | def __init__(self, object, request):
33 | self.object = object
34 | self.request = request
35 |
36 |
37 | class AfterUpgrade(object):
38 | def __init__(self, object):
39 | self.object = object
40 |
--------------------------------------------------------------------------------
/src/snovault/predicates.py:
--------------------------------------------------------------------------------
1 | def includeme(config):
2 | config.add_view_predicate('subpath_segments', SubpathSegmentsPredicate)
3 | config.add_view_predicate('additional_permission', AdditionalPermissionPredicate)
4 |
5 |
6 | class SubpathSegmentsPredicate(object):
7 | def __init__(self, val, config):
8 | if isinstance(val, int):
9 | val = (val,)
10 | self.val = frozenset(val)
11 |
12 | def text(self):
13 | return 'subpath_segments in %r' % sorted(self.val)
14 |
15 | phash = text
16 |
17 | def __call__(self, context, request):
18 | return len(request.subpath) in self.val
19 |
20 |
21 | class AdditionalPermissionPredicate(object):
22 | def __init__(self, val, config):
23 | self.val = val
24 |
25 | def text(self):
26 | return 'additional_permission = %r' % self.val
27 |
28 | phash = text
29 |
30 | def __call__(self, context, request):
31 | return request.has_permission(self.val, context)
32 |
--------------------------------------------------------------------------------
/src/snowflakes/types/image.py:
--------------------------------------------------------------------------------
1 | from snovault import (
2 | collection,
3 | load_schema,
4 | )
5 | from .base import (
6 | Item,
7 | )
8 | from snovault.attachment import ItemWithAttachment
9 |
10 |
11 | @collection(
12 | name='images',
13 | unique_key='image:filename',
14 | properties={
15 | 'title': 'Image',
16 | 'description': 'Listing of portal images',
17 | })
18 | class Image(ItemWithAttachment, Item):
19 | item_type = 'image'
20 | schema = load_schema('snowflakes:schemas/image.json')
21 | schema['properties']['attachment']['properties']['type']['enum'] = [
22 | 'image/png',
23 | 'image/jpeg',
24 | 'image/gif',
25 | ]
26 | embedded = ['submitted_by']
27 |
28 | def unique_keys(self, properties):
29 | keys = super(Image, self).unique_keys(properties)
30 | value = properties['attachment']['download']
31 | keys.setdefault('image:filename', []).append(value)
32 | return keys
33 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/_mixins-custom.scss:
--------------------------------------------------------------------------------
1 | // Custom Mixins
2 | // -----------------------------------------------
3 |
4 | // STICKY FOOTER
5 | // Compass' sticky footer with added top-border variable to allow use of a top border on your footer.
6 | // @include sticky-footer(54px, "#my-root", "#my-root-footer", "#my-footer")
7 | @mixin sticky-footer(
8 | $footer-height,
9 | $footer-border-top,
10 | $root-selector: unquote("#root"),
11 | $root-footer-selector: unquote("#root_footer"),
12 | $footer-selector: unquote("#footer")) {
13 | html, body {
14 | height: 100%;
15 | }
16 | #{$root-selector} {
17 | clear: both;
18 | min-height: 100%;
19 | height: auto !important;
20 | height: 100%;
21 | margin-bottom: (-$footer-height) - $footer-border-top;
22 | #{$root-footer-selector} {
23 | height: $footer-height; }
24 | }
25 | #{$footer-selector} {
26 | clear: both;
27 | position: relative;
28 | height: $footer-height;
29 | }
30 | }
--------------------------------------------------------------------------------
/src/snovault/tests/test_indexer_state.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def test_heterogeneous_stream():
5 | from snovault.elasticsearch.indexer_state import heterogeneous_stream
6 | gm = {'e': (x for x in [1, 2, 3, 4, 5])}
7 | assert list(heterogeneous_stream(gm)) == [1, 2, 3, 4, 5]
8 | gm = {
9 | 'e': (x for x in [1, 2, 3, 4, 5]),
10 | 'f': (x for x in ['a', 'b', 'c'])
11 | }
12 | assert list(heterogeneous_stream(gm)) == [1, 'a', 2, 'b', 3, 'c', 4, 5]
13 | gm = {
14 | 'e': (x for x in [1, 2, 3, 4, 5]),
15 | 'f': (x for x in ['a', 'b', 'c']),
16 | 'g': (x for x in (t for t in (None, True, False, None, None, True)))
17 | }
18 | assert list(heterogeneous_stream(gm)) == [
19 | 1, 'a', 2, 'b', True, 3, 'c', False, 4, 5, True
20 | ]
21 | gm = {
22 | 'e': [1.0, 2.0, 3.0],
23 | 'f': [x for x in range(10)]
24 | }
25 | assert list(heterogeneous_stream(gm)) == [
26 | 1.0, 0, 2.0, 1, 3.0, 2, 3, 4, 5, 6, 7, 8, 9
27 | ]
28 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_signin-box.scss:
--------------------------------------------------------------------------------
1 | /* Sign in box (as used on home page) */
2 | #signin-box {
3 | @include background-image(linear-gradient(rgba(255,255,255,0.1), rgba(0,0,0,0.1)));
4 | @include border-radius(5px);
5 | border: 1px solid #cacaca;
6 | display: inline-block;
7 | padding: 20px;
8 | float: right;
9 | @include box-shadow(rgba(180,180,180,0.9) 0 2px 10px, 0 1px 0 #fff inset);
10 | h4 {
11 | margin: 0 0 10px 0;
12 | padding: 0;
13 | }
14 | }
15 | .signin-button {
16 | display: block !important;
17 | @include font-size(1.4);
18 | font-weight: bold;
19 | margin-bottom: 7px;
20 | }
21 |
22 | .three-d-box {
23 | @include background-image(linear-gradient(rgba(255,255,255,0.1), rgba(0,0,0,0.1)));
24 | @include border-radius(5px);
25 | border: 1px solid #cacaca;
26 | display: block;
27 | padding: 20px;
28 | @include box-shadow(rgba(180,180,180,0.9) 0 2px 10px, 0 1px 0 #fff inset);
29 | margin-bottom: 20px;
30 | form {
31 | margin: 0;
32 | input { margin: 0; width: 98.5%;}
33 | }
34 | }
--------------------------------------------------------------------------------
/src/snowflakes/schemas/image.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Image",
3 | "description": "Schema for images embedded in page objects",
4 | "id": "/profiles/image.json",
5 | "$schema": "http://json-schema.org/draft-04/schema#",
6 | "type": "object",
7 | "required": [ "attachment" ],
8 | "identifyingProperties": ["uuid"],
9 | "additionalProperties": false,
10 | "mixinProperties": [
11 | { "$ref": "mixins.json#/schema_version" },
12 | { "$ref": "mixins.json#/uuid" },
13 | { "$ref": "mixins.json#/attachment" },
14 | { "$ref": "mixins.json#/submitted" },
15 | { "$ref": "mixins.json#/standard_status"}
16 | ],
17 | "properties": {
18 | "schema_version": {
19 | "default": "1"
20 | },
21 | "status": {
22 | "default": "released"
23 | },
24 | "caption": {
25 | "title": "Caption",
26 | "type": "string"
27 | }
28 | },
29 | "boost_values": {
30 | "caption": 1.0
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/page.feature:
--------------------------------------------------------------------------------
1 | @page
2 | Feature: Portal pages
3 |
4 | Scenario: Render page layout
5 | When I visit "/"
6 | And I wait for the content to load
7 | Then I should see 3 elements with the css selector "div.col-md-4"
8 | And I should see an element with the css selector ".project-info"
9 |
10 | Scenario: Override column class
11 | When I visit "/test-section/"
12 | And I wait for the content to load
13 | Then I should see an element with the css selector ".class_override"
14 |
15 | Scenario: Add a page
16 | When I visit "/pages/"
17 | And I wait for the table to fully load
18 | And I press "Add"
19 | And I wait for the form to fully load
20 | And I fill in "name" with "test"
21 | And I fill in "title" with "Test"
22 | And I press "Save"
23 | And I wait for the content to load
24 | Then the browser's URL should contain "/test/"
25 | And the title should contain the text "Test"
26 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const log = require('fancy-log');
3 | const webpack = require('webpack');
4 |
5 | const setProduction = function (cb) {
6 | process.env.NODE_ENV = 'production';
7 | if (cb) {
8 | cb();
9 | }
10 | };
11 |
12 | const webpackOnBuild = function (done) {
13 | return function (err, stats) {
14 | if (err) {
15 | throw new log.error(err);
16 | }
17 | log(stats.toString({
18 | colors: true
19 | }));
20 | if (done) { done(err); }
21 | };
22 | };
23 |
24 | const webpackSetup = function (cb) {
25 | var webpackConfig = require('./webpack.config.js');
26 | webpack(webpackConfig).run(webpackOnBuild(cb));
27 | };
28 |
29 | const watch = function (cb) {
30 | var webpackConfig = require('./webpack.config.js');
31 | webpack(webpackConfig).watch(300, webpackOnBuild(cb));
32 | };
33 |
34 | const series = gulp.series;
35 |
36 | gulp.task('default', series(webpackSetup, watch));
37 | gulp.task('dev', series('default'));
38 | gulp.task('build', series(setProduction, webpackSetup));
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts =
3 | --pyargs snowflakes.tests
4 | --pyargs snovault.tests
5 | --pyargs snovault.elasticsearch.tests
6 | # --pyargs snovault.elasticsearch.uuid_queue.tests
7 | -p snowflakes.tests
8 | --instafail
9 | --splinter-make-screenshot-on-failure=false
10 | --splinter-implicit-wait=5
11 | # Ignore warnings from splinter, we don't use browser.find_by_{href,link} directly
12 | filterwarnings =
13 | error
14 | ignore:browser\.find_link_by_href is deprecated\. Use browser\.links\.find_by_href instead\.:FutureWarning
15 | ignore:browser\.find_link_by_text is deprecated\. Use browser\.links\.find_by_text instead\.:FutureWarning
16 | markers =
17 | bdd: Encoded Scenario
18 | forms: Encoded Scenario
19 | generics: Encoded Scenario
20 | indexing: Encoded Scenario
21 | page: Encoded Scenario
22 | report: Encoded Scenario
23 | search: Encoded Scenario
24 | slow: Encoded Scenario
25 | storage: storage tests
26 | title: Encoded Scenario
27 | toolbar: Encoded Scenario
28 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_searches_configs.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def included(config):
5 | def new_item_search_config():
6 | return {
7 | 'facets': {'a': 'b'}
8 | }
9 | config.register_search_config(
10 | 'OtherConfigItem', new_item_search_config
11 | )
12 |
13 |
14 | def test_searches_configs_search_config_decorator(config, dummy_request):
15 | from snovault.elasticsearch.searches.interfaces import SEARCH_CONFIG
16 | from snovault.elasticsearch.searches.configs import search_config
17 | assert dummy_request.registry[SEARCH_CONFIG].get('TestConfigItem').facets == {'a': 'b'}
18 | config.include('snovault.elasticsearch.searches.configs')
19 | config.include(included)
20 | config.commit()
21 | assert config.registry[SEARCH_CONFIG].registry.get('OtherConfigItem').facets == {'a': 'b'}
22 | config.register_search_config('OtherConfigItem', lambda: {'facets': {'c': 'd'}})
23 | config.commit()
24 | assert config.registry[SEARCH_CONFIG].registry.get('OtherConfigItem').facets == {'c': 'd'}
25 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/searches/configs.py:
--------------------------------------------------------------------------------
1 | import venusian
2 |
3 | from snosearch.configs import SearchConfigRegistry
4 | from snovault.elasticsearch.searches.interfaces import SEARCH_CONFIG
5 |
6 |
7 | def includeme(config):
8 | registry = config.registry
9 | registry[SEARCH_CONFIG] = SearchConfigRegistry()
10 | config.add_directive('register_search_config', register_search_config)
11 |
12 |
13 | def register_search_config(config, name, factory):
14 | config.action(
15 | ('set-search-config', name),
16 | config.registry[SEARCH_CONFIG].register_from_func,
17 | args=(
18 | name,
19 | factory
20 | )
21 | )
22 |
23 |
24 | def search_config(name, **kwargs):
25 | '''
26 | Register a custom search config by name.
27 | '''
28 | def decorate(config):
29 | def callback(scanner, factory_name, factory):
30 | scanner.config.register_search_config(name, factory)
31 | venusian.attach(config, callback, category='pyramid')
32 | return config
33 | return decorate
34 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_thumbnails.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Thumbnails
3 | // --------------------------------------------------
4 |
5 |
6 | // Mixin and adjust the regular image class
7 | .thumbnail {
8 | display: block;
9 | padding: $thumbnail-padding;
10 | margin-bottom: $line-height-computed;
11 | line-height: $line-height-base;
12 | background-color: $thumbnail-bg;
13 | border: 1px solid $thumbnail-border;
14 | border-radius: $thumbnail-border-radius;
15 | @include transition(all .2s ease-in-out);
16 |
17 | > img,
18 | a > img {
19 | @include img-responsive();
20 | margin-left: auto;
21 | margin-right: auto;
22 | }
23 |
24 | // [converter] extracted a&:hover, a&:focus, a&.active to a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active
25 |
26 | // Image captions
27 | .caption {
28 | padding: $thumbnail-caption-padding;
29 | color: $thumbnail-caption-color;
30 | }
31 | }
32 |
33 | // Add a hover state for linked versions only
34 | a.thumbnail:hover,
35 | a.thumbnail:focus,
36 | a.thumbnail.active {
37 | border-color: $link-color;
38 | }
39 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_utilities.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // Floats
7 | // -------------------------
8 |
9 | .clearfix {
10 | @include clearfix();
11 | }
12 | .center-block {
13 | @include center-block();
14 | }
15 | .pull-right {
16 | float: right !important;
17 | }
18 | .pull-left {
19 | float: left !important;
20 | }
21 |
22 |
23 | // Toggling content
24 | // -------------------------
25 |
26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1
27 | .hide {
28 | display: none !important;
29 | }
30 | .show {
31 | display: block !important;
32 | }
33 | .invisible {
34 | visibility: hidden;
35 | }
36 | .text-hide {
37 | @include text-hide();
38 | }
39 |
40 |
41 | // Hide from screenreaders and browsers
42 | //
43 | // Credit: HTML5 Boilerplate
44 |
45 | .hidden {
46 | display: none !important;
47 | visibility: hidden !important;
48 | }
49 |
50 |
51 | // For Affix plugin
52 | // -------------------------
53 |
54 | .affix {
55 | position: fixed;
56 | }
57 |
--------------------------------------------------------------------------------
/docs/search.rst:
--------------------------------------------------------------------------------
1 | Search Documentation:
2 | =====================
3 |
4 | **URIS**
5 |
6 | 1. http://{SERVER_NAME}/search/?searchTerm={term}
7 | Fetches all the documents which contain the text 'term'.
8 | The result set includes wild card searches and the 'term' should be atleast 3 characters long.
9 |
10 | - SERVER_NAME: ENCODE server
11 | - term: string that can be searched accross four item_types (i.e., experiment, biosample, antibody_approval, target)
12 |
13 | 2. http://{SERVER_NAME}/search/?type={item_type}
14 | Fetches all the documents of that particular 'item_type'
15 |
16 | - SERVER_NAME: ENCODE server
17 | - item_type: ENCODE item type (values can be: biosample, experiment, antibody_approval and target)
18 |
19 | 3. http://{SERVER_NAME}/search/?type={item_type}&{field_name}={text}
20 | Fetches and then filters all the documents of a particular item_type on that field
21 |
22 | - SERVER_NAME: ENCODE server
23 | - item_type: ENCODE item type (values can be: biosample, experiment, antibody_approval and target)
24 | - field_name: Any of the json property in the ENCODE 'item_type' schema
25 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var globals = require('./globals');
4 |
5 | var Home = module.exports.Home = React.createClass({
6 | render: function() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
Snowflakes: Where every object is unique
14 |
15 |
16 |
Snowflake Portal
17 |
Enter a search term in the toolbar above.
18 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 | });
26 |
27 |
28 | globals.content_views.register(Home, 'Portal');
29 |
--------------------------------------------------------------------------------
/src/snowflakes/static/inline.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Read and clear stats cookie
4 | import * as cookie from 'js-cookie';
5 | window.stats_cookie = cookie.get('X-Stats') || '';
6 | cookie.set('X-Stats', '', {path: '/', expires: new Date(0)});
7 |
8 | // Use a separate tracker for dev / test
9 | var ga = require('google-analytics');
10 | var trackers = {'www.encodeproject.org': 'UA-47809317-1'};
11 | var tracker = trackers[document.location.hostname] || 'UA-47809317-2';
12 | ga('create', tracker, {'cookieDomain': 'none', 'siteSpeedSampleRate': 100});
13 | ga('send', 'pageview');
14 |
15 | // Need to know if onload event has fired for safe history api usage.
16 | window.onload = function () {
17 | window._onload_event_fired = true;
18 | };
19 |
20 | var $script = require('scriptjs');
21 | $script.path('/static/build/');
22 | $script('https://login.persona.org/include.js', 'persona');
23 |
24 | // Load the rest of the app as a separate chunk.
25 | require.ensure(['./libs/compat', './browser'], function(require) {
26 | require('./libs/compat'); // Shims first
27 | require('./browser');
28 | }, 'bundle');
29 |
--------------------------------------------------------------------------------
/config.rb:
--------------------------------------------------------------------------------
1 | # Require any additional compass plugins here.
2 |
3 | # Set this to the root of your project when deployed:
4 | http_path = "/"
5 | css_dir = "src/snowflakes/static/css"
6 | sass_dir = "src/snowflakes/static/scss"
7 | images_dir = "src/snowflakes/static/img"
8 | javascripts_dir = "src/snowflakes/static/modules"
9 | fonts_dir = "src/snowflakes/static/fonts"
10 |
11 | # To export minified css, uncomment :compress, and comments out :nested
12 | output_style = :compressed
13 | # output_style = :nested
14 |
15 | # To enable relative paths to assets via compass helper functions. Uncomment:
16 | # relative_assets = true
17 |
18 | # To disable debugging comments that display the original location of your selectors. Uncomment:
19 | # line_comments = false
20 | color_output = false
21 |
22 |
23 | # If you prefer the indented syntax, you might want to regenerate this
24 | # project again passing --syntax sass, or you can uncomment this:
25 | # preferred_syntax = :sass
26 | # and then run:
27 | # sass-convert -R --from scss --to sass src/snowflakes/static/sass scss && rm -rf sass && mv scss sass
28 | preferred_syntax = :scss
--------------------------------------------------------------------------------
/src/snowflakes/types/__init__.py:
--------------------------------------------------------------------------------
1 | from snovault.attachment import ItemWithAttachment
2 | from snovault import (
3 | calculated_property,
4 | collection,
5 | load_schema,
6 | )
7 | from pyramid.traversal import find_root
8 | from .base import (
9 | Item,
10 | paths_filtered_by_status,
11 | )
12 |
13 |
14 | def includeme(config):
15 | config.scan()
16 |
17 |
18 | @collection(
19 | name='labs',
20 | unique_key='lab:name',
21 | properties={
22 | 'title': 'Labs',
23 | 'description': 'Listing of Snowflake labs',
24 | })
25 | class Lab(Item):
26 | item_type = 'lab'
27 | schema = load_schema('snowflakes:schemas/lab.json')
28 | name_key = 'name'
29 | embedded = ['awards']
30 |
31 |
32 | @collection(
33 | name='awards',
34 | unique_key='award:name',
35 | properties={
36 | 'title': 'Awards (Grants)',
37 | 'description': 'Listing of awards (aka grants)',
38 | })
39 | class Award(Item):
40 | item_type = 'award'
41 | schema = load_schema('snowflakes:schemas/award.json')
42 | name_key = 'name'
43 | embedded = ['pi']
44 |
--------------------------------------------------------------------------------
/src/snowflakes/static/browser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // Entry point for browser
3 | require('./libs/react-patches');
4 | var React = require('react');
5 | var ReactMount = require('react/lib/ReactMount');
6 | ReactMount.allowFullPageRender = true;
7 |
8 | var App = require('./components');
9 | var domready = require('domready');
10 |
11 | // Treat domready function as the entry point to the application.
12 | // Inside this function, kick-off all initialization, everything up to this
13 | // point should be definitions.
14 | if (!window.TEST_RUNNER) domready(function ready() {
15 | console.log('ready');
16 | // Set class depending on browser features
17 | var BrowserFeat = require('./components/browserfeat').BrowserFeat;
18 | BrowserFeat.setHtmlFeatClass();
19 | var props = App.getRenderedProps(document);
20 | var server_stats = require('querystring').parse(window.stats_cookie);
21 | App.recordServerStats(server_stats, 'html');
22 |
23 | var app = React.render( , document);
24 |
25 | // Simplify debugging
26 | window.app = app;
27 | window.React = React;
28 | });
29 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_jumbotron.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Jumbotron
3 | // --------------------------------------------------
4 |
5 |
6 | .jumbotron {
7 | padding: $jumbotron-padding;
8 | margin-bottom: $jumbotron-padding;
9 | color: $jumbotron-color;
10 | background-color: $jumbotron-bg;
11 |
12 | h1,
13 | .h1 {
14 | color: $jumbotron-heading-color;
15 | }
16 | p {
17 | margin-bottom: ($jumbotron-padding / 2);
18 | font-size: $jumbotron-font-size;
19 | font-weight: 200;
20 | }
21 |
22 | .container & {
23 | border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container
24 | }
25 |
26 | .container {
27 | max-width: 100%;
28 | }
29 |
30 | @media screen and (min-width: $screen-sm-min) {
31 | padding-top: ($jumbotron-padding * 1.6);
32 | padding-bottom: ($jumbotron-padding * 1.6);
33 |
34 | .container & {
35 | padding-left: ($jumbotron-padding * 2);
36 | padding-right: ($jumbotron-padding * 2);
37 | }
38 |
39 | h1,
40 | .h1 {
41 | font-size: ($font-size-base * 4.5);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_media.scss:
--------------------------------------------------------------------------------
1 | // Media objects
2 | // Source: http://stubbornella.org/content/?p=497
3 | // --------------------------------------------------
4 |
5 |
6 | // Common styles
7 | // -------------------------
8 |
9 | // Clear the floats
10 | .media,
11 | .media-body {
12 | overflow: hidden;
13 | zoom: 1;
14 | }
15 |
16 | // Proper spacing between instances of .media
17 | .media,
18 | .media .media {
19 | margin-top: 15px;
20 | }
21 | .media:first-child {
22 | margin-top: 0;
23 | }
24 |
25 | // For images and videos, set to block
26 | .media-object {
27 | display: block;
28 | }
29 |
30 | // Reset margins on headings for tighter default spacing
31 | .media-heading {
32 | margin: 0 0 5px;
33 | }
34 |
35 |
36 | // Media image alignment
37 | // -------------------------
38 |
39 | .media {
40 | > .pull-left {
41 | margin-right: 10px;
42 | }
43 | > .pull-right {
44 | margin-left: 10px;
45 | }
46 | }
47 |
48 |
49 | // Media list variation
50 | // -------------------------
51 |
52 | // Undo default ul/ol styles
53 | .media-list {
54 | padding-left: 0;
55 | list-style: none;
56 | }
57 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_pager.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Pager pagination
3 | // --------------------------------------------------
4 |
5 |
6 | .pager {
7 | padding-left: 0;
8 | margin: $line-height-computed 0;
9 | list-style: none;
10 | text-align: center;
11 | @include clearfix();
12 | li {
13 | display: inline;
14 | > a,
15 | > span {
16 | display: inline-block;
17 | padding: 5px 14px;
18 | background-color: $pager-bg;
19 | border: 1px solid $pager-border;
20 | border-radius: $pager-border-radius;
21 | }
22 |
23 | > a:hover,
24 | > a:focus {
25 | text-decoration: none;
26 | background-color: $pager-hover-bg;
27 | }
28 | }
29 |
30 | .next {
31 | > a,
32 | > span {
33 | float: right;
34 | }
35 | }
36 |
37 | .previous {
38 | > a,
39 | > span {
40 | float: left;
41 | }
42 | }
43 |
44 | .disabled {
45 | > a,
46 | > a:hover,
47 | > a:focus,
48 | > span {
49 | color: $pager-disabled-color;
50 | background-color: $pager-bg;
51 | cursor: not-allowed;
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/test_generics.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_bdd import (
3 | scenarios,
4 | then,
5 | when,
6 | )
7 | from . import browsersteps
8 |
9 | pytestmark = [
10 | pytest.mark.bdd,
11 | pytest.mark.usefixtures('workbook'),
12 | ]
13 |
14 | scenarios('generics.feature', strict_gherkin=False)
15 |
16 |
17 | # https://github.com/pytest-dev/pytest-bdd/issues/124
18 |
19 |
20 | @when('I visit "//"')
21 | def i_visit_the_collection_for_type_name(browser, base_url, type_name):
22 | url = '/{}/'.format(type_name)
23 | browsersteps.when_i_visit_url(browser, base_url, url)
24 |
25 |
26 | @when('I click the link with text that contains ""')
27 | def click_link_with_text_that_contains_link_text(browser, link_text):
28 | browsersteps.click_link_with_text_that_contains(browser, link_text)
29 |
30 |
31 | @then('I should see an element with the css selector ".view-item.type-"')
32 | def should_see_element_with_css_type_name(browser, type_name):
33 | css = ".view-item.type-{}".format(type_name)
34 | browsersteps.should_see_element_with_css(browser, css)
35 |
--------------------------------------------------------------------------------
/scripts/embeds.py:
--------------------------------------------------------------------------------
1 | '''
2 | For each object, count the number of objects in which it is embedded.
3 |
4 | Usage: python embeds.py > embeds.jsonlines
5 | '''
6 |
7 | from elasticsearch import Elasticsearch
8 | from elasticsearch.helpers import scan
9 | import json
10 |
11 | es = Elasticsearch('localhost:9200')
12 |
13 |
14 | def embeds_uuid(es, uuid, item_type):
15 | query = {
16 | 'query': {'terms': {'embedded_uuids': [uuid]}},
17 | 'aggregations': {
18 | 'item_type': {'terms': {'field': 'item_type'}},
19 | },
20 | }
21 | res = es.search(index='encoded', search_type='count', body=query)
22 | return {
23 | 'uuid': uuid,
24 | 'item_type': item_type,
25 | 'embeds': res['hits']['total'],
26 | 'buckets': res['aggregations']['item_type']['buckets'],
27 | }
28 |
29 |
30 | uuid_type = [(hit['_id'], hit['_type']) for hit in scan(es, query={'fields': []})]
31 |
32 |
33 | # rows = [embeds_uuid(es, uuid, item_type) for uuid, item_type in uuid_type]
34 | for uuid, item_type in uuid_type:
35 | data = embeds_uuid(es, uuid, item_type)
36 | print(json.dumps(data))
37 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013 Stanford University
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/snowflakes/static/server.js:
--------------------------------------------------------------------------------
1 | // Entry point for server rendering subprocess
2 |
3 | /* jshint strict: false */
4 |
5 | if (process.env.NODE_ENV === undefined) {
6 | require("@babel/register")({
7 | only: ['react-forms', 'src/snowflakes/static'],
8 | });
9 | } else {
10 | require('source-map-support').install();
11 | }
12 |
13 | require('./libs/react-patches');
14 |
15 | var argv = process.argv.slice(2);
16 | var debug = (argv[0] === '--debug');
17 |
18 | var app = require('./libs/react-middleware').build(require('./components'));
19 | var http_stream = require('subprocess-middleware').HTTPStream({app: app, captureConsole: !debug});
20 | http_stream.pipe(process.stdout);
21 | if (debug) {
22 | var value = argv[1] || '{}';
23 | if (value.slice(0, 5) === 'file:') {
24 | value = require('fs').readFileSync(value.slice(5));
25 | } else {
26 | value = new Buffer(value, 'utf8');
27 | }
28 | http_stream.write('HTTP/1.1 200 OK\r\nX-Request-URL: http://localhost/\r\nContent-Length: ' + value.length + '\r\n\r\n');
29 | http_stream.write(value);
30 | http_stream.end();
31 | } else {
32 | process.stdin.pipe(http_stream);
33 | process.stdin.resume();
34 | }
35 |
--------------------------------------------------------------------------------
/scripts/LogToCsv.py:
--------------------------------------------------------------------------------
1 | import re
2 | #regex = '([(\d\.)]+) - - \[(.*?)\] "(.*?)" (\d+) "-" (.*?) "(.*?)" "(.*?)"'
3 | pattern = re.compile('([(\d\.)]+) - - \[(.*?)\] "(.*?)" (\d+) (\d+) "-" "(.*?)" "(.*?)"')
4 | pattern_trim = re.compile('([(\d\.)]+) - (.*?) \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)" (.*?)')
5 |
6 | none_count = 0
7 | elif_count = 0
8 |
9 | file = open('encodeproject.org.log', 'r')
10 |
11 | outputFile = open('encodeProjectToCSV.csv', 'w')
12 |
13 | for line in file:
14 | result = re.search(pattern, line)
15 | result_trim = re.search(pattern_trim, line)
16 |
17 | if result:
18 | split_line = result.group(7)
19 | do_split = re.split(r'[-&?()]', split_line)
20 | completed_line = result.group(1,2,3,4,5,6), do_split
21 |
22 | outputFile.write(str(completed_line))
23 | outputFile.write("\n")
24 |
25 | elif not result:
26 | elif_count = +1
27 | split_line = result_trim.group(7)
28 | do_split = re.split(r'[-&?()]', split_line)
29 | completed_line = result_trim.group(1,3,4,5,6,8), do_split
30 |
31 | outputFile.write(str(completed_line))
32 | outputFile.write("\n")
33 |
34 | else:
35 | none_count = +1
36 | outputFile.write(line)
37 |
38 | print("None count: %d" % none_count)
39 | file.close()
40 | outputFile.close()
--------------------------------------------------------------------------------
/conf/jvm.options:
--------------------------------------------------------------------------------
1 | -Xms2g
2 | -Xmx2g
3 |
4 | ## GC configuration
5 | -XX:+UseConcMarkSweepGC
6 | -XX:CMSInitiatingOccupancyFraction=75
7 | -XX:+UseCMSInitiatingOccupancyOnly
8 |
9 | # disable calls to System#gc
10 | -XX:+DisableExplicitGC
11 |
12 | # pre-touch memory pages used by the JVM during initialization
13 | -XX:+AlwaysPreTouch
14 |
15 | # force the server VM (remove on 32-bit client JVMs)
16 | -server
17 |
18 | # explicitly set the stack size (reduce to 320k on 32-bit client JVMs)
19 | -Xss1m
20 |
21 | # set to headless, just in case
22 | -Djava.awt.headless=true
23 |
24 | # ensure UTF-8 encoding by default (e.g. filenames)
25 | -Dfile.encoding=UTF-8
26 |
27 | # use our provided JNA always versus the system one
28 | -Djna.nosys=true
29 |
30 | # use old-style file permissions on JDK9
31 | -Djdk.io.permissionsUseCanonicalPath=true
32 |
33 | # flags to configure Netty
34 | -Dio.netty.noUnsafe=true
35 | -Dio.netty.noKeySetOptimization=true
36 | -Dio.netty.recycler.maxCapacityPerThread=0
37 |
38 | # log4j 2
39 | -Dlog4j.shutdownHookEnabled=false
40 | -Dlog4j2.disable.jmx=true
41 | -Dlog4j.skipJansi=true
42 |
43 | # generate a heap dump when an allocation from the Java heap fails
44 | # heap dumps are created in the working directory of the JVM
45 | -XX:+HeapDumpOnOutOfMemoryError
--------------------------------------------------------------------------------
/scripts/blackholes.py:
--------------------------------------------------------------------------------
1 | '''
2 | Report on 'blackhole' objects, those which are embedded in over many other objects.
3 |
4 | Care should be taken with the reverse links from blackhole objects.
5 | '''
6 |
7 | from collections import defaultdict
8 | import json
9 |
10 |
11 | rows = [json.loads(line) for line in open('embeds.txt')]
12 | uuid_row = {row['uuid']: row for row in rows}
13 |
14 | CUTOFF = 1000
15 |
16 | by_type = defaultdict(list)
17 | for row in rows:
18 | if row['embeds'] >= CUTOFF:
19 | by_type[row['item_type']].append(row)
20 |
21 | print(json.dumps({k: len(v) for k, v in by_type.items()}, sort_keys=True, indent=4))
22 |
23 | '''
24 | Report on number of transacations that invalidate many objects.
25 |
26 | $ sudo -u encoded psql -tAc "select row_to_json(transactions) from transactions where timestamp::date = '2016-01-20'::date;" > transactions.txt
27 |
28 | Beware that reverse link invalidations are entered into the transaction log, so any changes will not be reflected.
29 | '''
30 |
31 | transactions = [json.loads(line) for line in open('transactions.txt')]
32 |
33 | sum_txn = [(sum(uuid_row[uuid]['embeds'] for uuid in txn['data']['updated']), txn) for txn in transactions]
34 | print('Transactions > {}'.format(CUTOFF), sum(s > CUTOFF for s, row in sum_txn))
35 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_badges.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Badges
3 | // --------------------------------------------------
4 |
5 |
6 | // Base classes
7 | .badge {
8 | display: inline-block;
9 | min-width: 10px;
10 | padding: 3px 7px;
11 | font-size: $font-size-small;
12 | font-weight: $badge-font-weight;
13 | color: $badge-color;
14 | line-height: $badge-line-height;
15 | vertical-align: baseline;
16 | white-space: nowrap;
17 | text-align: center;
18 | background-color: $badge-bg;
19 | border-radius: $badge-border-radius;
20 |
21 | // Empty badges collapse automatically (not available in IE8)
22 | &:empty {
23 | display: none;
24 | }
25 |
26 | // Quick fix for badges in buttons
27 | .btn & {
28 | position: relative;
29 | top: -1px;
30 | }
31 | .btn-xs & {
32 | top: 0;
33 | padding: 1px 5px;
34 | }
35 | }
36 |
37 | // Hover state, but only for links
38 | a.badge {
39 | &:hover,
40 | &:focus {
41 | color: $badge-link-hover-color;
42 | text-decoration: none;
43 | cursor: pointer;
44 | }
45 | }
46 |
47 | // Account for counters in navs
48 | a.list-group-item.active > .badge,
49 | .nav-pills > .active > a > .badge {
50 | color: $badge-active-color;
51 | background-color: $badge-active-bg;
52 | }
53 | .nav-pills > li > a > .badge {
54 | margin-left: 3px;
55 | }
56 |
--------------------------------------------------------------------------------
/src/snovault/nginx-dev.conf:
--------------------------------------------------------------------------------
1 | # Minimal nginx proxy for development
2 | # brew install nginx
3 | # nginx -p . nginx-dev.conf
4 |
5 | events {
6 | worker_connections 2048;
7 | }
8 | error_log stderr info;
9 | http {
10 | access_log /dev/stdout;
11 |
12 | resolver 8.8.8.8;
13 | upstream app {
14 | server 127.0.0.1:6543;
15 | keepalive 10;
16 | }
17 |
18 | map $http_x_forwarded_proto $forwarded_proto {
19 | default $http_x_forwarded_proto;
20 | '' $scheme;
21 | }
22 |
23 | server {
24 | listen 8000;
25 | location / {
26 | # Normalize duplicate slashes
27 | if ($request ~ ^(GET|HEAD)\s([^?]*)//(.*)\sHTTP/[0-9.]+$) {
28 | return 301 $2/$3;
29 | }
30 | proxy_set_header Host $http_host;
31 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
32 | proxy_set_header X-Forwarded-Proto $forwarded_proto;
33 | proxy_pass http://app;
34 | proxy_set_header Connection "";
35 | }
36 | location ~ ^/_proxy/(.*)$ {
37 | internal;
38 | proxy_set_header Authorization "";
39 | proxy_set_header Content-Type "";
40 | proxy_buffering off;
41 | proxy_pass $1$is_args$args;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/blocks/fallback.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var globals = require('../globals');
4 | var item = require('../item');
5 | var noarg_memoize = require('../../libs/noarg-memoize');
6 |
7 | var FallbackBlockView = React.createClass({
8 | render: function() {
9 | var Panel = item.Panel;
10 | return (
11 |
12 |
{this.props.blocktype.label}
13 |
14 |
15 | );
16 | }
17 | });
18 |
19 | var FallbackBlockEdit = module.exports.FallbackBlockEdit = React.createClass({
20 | render: function() {
21 | var ReactForms = require('react-forms');
22 | return ;
23 | }
24 | });
25 |
26 |
27 | // Use this as a fallback for any block we haven't registered
28 | globals.blocks.fallback = function (obj) {
29 | return {
30 | label: obj['@type'].join(','),
31 | schema: noarg_memoize(function() {
32 | var JSONNode = require('../JSONNode');
33 | return JSONNode.create({
34 | label: 'JSON',
35 | input: ,
36 | });
37 | }),
38 | view: FallbackBlockView
39 | };
40 | };
41 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/search.feature:
--------------------------------------------------------------------------------
1 | @search
2 | Feature: Search
3 | Background:
4 | When I visit "/search"
5 | And I wait for the content to load
6 |
7 |
8 | Scenario: Search
9 | Then the title should contain the text "Search"
10 |
11 |
12 | Scenario: Search Snowballs
13 | When I click the link with text that contains "Data"
14 | And I click the link to "/search/?type=Snowball"
15 | And I wait for the content to load
16 | Then I should see at least 13 elements with the css selector "ul.nav.result-table > li"
17 | And I should see at least 3 elements with the css selector "div.box.facets > div.facet"
18 |
19 | When I click the link to "?type=Snowball&method=hand-packed"
20 | And I wait for the content to load
21 | Then I should see at least 3 elements with the css selector "ul.nav.result-table > li"
22 |
23 | When I click the link to "?type=Snowball&method=hand-packed&method=scoop-formed"
24 | And I wait for the content to load
25 | Then I should see at least 5 elements with the css selector "ul.nav.result-table > li"
26 |
27 |
28 | Scenario: Search BoxI
29 | When I fill in "searchTerm" with "hand-packed"
30 | Then I should see at least 10 elements with the css selector "ul.nav.result-table > li"
31 |
32 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_labels.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Labels
3 | // --------------------------------------------------
4 |
5 | .label {
6 | display: inline;
7 | padding: .2em .6em .3em;
8 | font-size: 75%;
9 | font-weight: bold;
10 | line-height: 1;
11 | color: $label-color;
12 | text-align: center;
13 | white-space: nowrap;
14 | vertical-align: baseline;
15 | border-radius: .25em;
16 |
17 | // Add hover effects, but only for links
18 | &[href] {
19 | &:hover,
20 | &:focus {
21 | color: $label-link-hover-color;
22 | text-decoration: none;
23 | cursor: pointer;
24 | }
25 | }
26 |
27 | // Empty labels collapse automatically (not available in IE8)
28 | &:empty {
29 | display: none;
30 | }
31 |
32 | // Quick fix for labels in buttons
33 | .btn & {
34 | position: relative;
35 | top: -1px;
36 | }
37 | }
38 |
39 | // Colors
40 | // Contextual variations (linked labels get darker on :hover)
41 |
42 | .label-default {
43 | @include label-variant($label-default-bg);
44 | }
45 |
46 | .label-primary {
47 | @include label-variant($label-primary-bg);
48 | }
49 |
50 | .label-success {
51 | @include label-variant($label-success-bg);
52 | }
53 |
54 | .label-info {
55 | @include label-variant($label-info-bg);
56 | }
57 |
58 | .label-warning {
59 | @include label-variant($label-warning-bg);
60 | }
61 |
62 | .label-danger {
63 | @include label-variant($label-danger-bg);
64 | }
65 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/fontawesome/_extras.scss:
--------------------------------------------------------------------------------
1 | /* EXTRAS
2 | * -------------------------- */
3 |
4 | /* Stacked and layered icon */
5 |
6 | /* Animated rotating icon */
7 | .#{$fa-css-prefix}-spin {
8 | -webkit-animation: spin 2s infinite linear;
9 | -moz-animation: spin 2s infinite linear;
10 | -o-animation: spin 2s infinite linear;
11 | animation: spin 2s infinite linear;
12 | }
13 |
14 | @-moz-keyframes spin {
15 | 0% { -moz-transform: rotate(0deg); }
16 | 100% { -moz-transform: rotate(359deg); }
17 | }
18 | @-webkit-keyframes spin {
19 | 0% { -webkit-transform: rotate(0deg); }
20 | 100% { -webkit-transform: rotate(359deg); }
21 | }
22 | @-o-keyframes spin {
23 | 0% { -o-transform: rotate(0deg); }
24 | 100% { -o-transform: rotate(359deg); }
25 | }
26 | @-ms-keyframes spin {
27 | 0% { -ms-transform: rotate(0deg); }
28 | 100% { -ms-transform: rotate(359deg); }
29 | }
30 | @keyframes spin {
31 | 0% { transform: rotate(0deg); }
32 | 100% { transform: rotate(359deg); }
33 | }
34 |
35 |
36 | // Icon rotations & flipping
37 | // -------------------------
38 |
39 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
40 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
41 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
42 |
43 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
44 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
45 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/bootstrap/panel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 |
4 |
5 | var Panel = module.exports.Panel = React.createClass({
6 | propTypes: {
7 | addClasses: React.PropTypes.string // Classes to add to outer panel div
8 | },
9 |
10 | render: function() {
11 | return (
12 |
13 | {this.props.children}
14 |
15 | );
16 | }
17 | });
18 |
19 |
20 | var PanelBody = module.exports.PanelBody = React.createClass({
21 | propTypes: {
22 | addClasses: React.PropTypes.string // Classes to add to outer panel div
23 | },
24 |
25 | render: function() {
26 | return (
27 |
28 | {this.props.children}
29 |
30 | );
31 | }
32 | });
33 |
34 |
35 | var PanelHeading = module.exports.PanelHeading = React.createClass({
36 | propTypes: {
37 | addClasses: React.PropTypes.string // Classes to add to outer panel div
38 | },
39 |
40 | render: function() {
41 | return (
42 |
43 | {this.props.children}
44 |
45 | );
46 | }
47 | });
48 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/generics.feature:
--------------------------------------------------------------------------------
1 | @generics
2 | Feature: Generics
3 |
4 | Scenario Outline: Generics
5 | When I visit "//"
6 | Then I should see an element with the css selector ".collection-table"
7 | When I wait for the table to fully load
8 | Then I should see an element with the css selector ".collection-table > tbody > tr"
9 | And I should not see "N/A"
10 | And I should not see "NULL"
11 | And I should not see "null"
12 |
13 | When I click the link with text that contains ""
14 | Then I should see an element with the css selector ".view-item.type-"
15 | And I should not see "N/A"
16 | And I should not see "NULL"
17 | And I should not see "null"
18 |
19 | When I go back
20 | Then I should see an element with the css selector ".collection-table"
21 |
22 | Examples: Collections
23 | | type_name | link_text |
24 | | Award | A DATA COORDINATING CENTER FOR ENCODE |
25 | | Snowflake | SNOFL000LSP |
26 | | Snowball | SNOSS704RSS |
27 | | Snowfort | SNOSS000AER |
28 | | Lab | Cherry |
29 |
30 | # must log in for users
31 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/__tests__/registry-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.dontMock('../registry');
4 | jest.dontMock('underscore');
5 | var Registry = require('../registry');
6 |
7 | var test_obj = {'@type': ['Test', 'Item']};
8 | var specific_obj = {'@type': ['Specific', 'Item']};
9 | var other_obj = {'@type': ['Other']};
10 |
11 | var views = [
12 | {for_: 'Item'},
13 | {for_: 'Specific'},
14 | {name: 'named', for_: 'Item'}
15 | ];
16 |
17 | var make_one = function () {
18 | var registry = new Registry();
19 | views.forEach(function (view) {
20 | registry.register(view, view.for_, view.name);
21 | });
22 | registry.fallback = function () {
23 | return {fallback: true};
24 | };
25 | return registry;
26 | };
27 |
28 | describe("The registry library", function() {
29 |
30 | it("is able to lookup views for item in order of specificity", function() {
31 | var registry = make_one();
32 | expect(registry.lookup(test_obj).for_).toBe('Item');
33 | expect(registry.lookup(specific_obj).for_).toBe('Specific');
34 | });
35 |
36 | it("is able to lookup named views for item", function() {
37 | var registry = make_one();
38 | expect(registry.lookup(test_obj, 'named').name).toBe('named');
39 | });
40 |
41 | it("is able to fallback to view for objects with unknown types", function() {
42 | var registry = make_one();
43 | expect(registry.lookup(other_obj).fallback).toBe(true);
44 | });
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/production.ini.in:
--------------------------------------------------------------------------------
1 | [app:app]
2 | use = config:base.ini#app
3 | session.secret = %(here)s/session-secret.b64
4 | file_upload_bucket = ${file_upload_bucket}
5 | blob_bucket = ${blob_bucket}
6 | blob_store_profile_name = encoded-files-upload
7 | accession_factory = ${accession_factory}
8 | indexer.processes = ${indexer_processes}
9 |
10 | [composite:indexer]
11 | use = config:base.ini#indexer
12 |
13 | [composite:regionindexer]
14 | use = config:base.ini#regionindexer
15 |
16 | [pipeline:main]
17 | pipeline =
18 | config:base.ini#memlimit
19 | egg:PasteDeploy#prefix
20 | app
21 |
22 | [pipeline:debug]
23 | pipeline =
24 | egg:repoze.debug#pdbpm
25 | app
26 | set pyramid.includes =
27 | pyramid_translogger
28 |
29 | [server:main]
30 | use = egg:waitress#main
31 | host = 0.0.0.0
32 | port = 6543
33 | threads = 1
34 |
35 | [loggers]
36 | keys = root, snovault, snovault_listener
37 |
38 | [handlers]
39 | keys = console
40 |
41 | [formatters]
42 | keys = generic
43 |
44 | [logger_root]
45 | level = WARN
46 | handlers = console
47 |
48 | [logger_snovault]
49 | level = WARN
50 | handlers = console
51 | qualname = snovault
52 | propagate = 0
53 |
54 | [logger_snovault_listener]
55 | level = INFO
56 | handlers = console
57 | qualname = snovault.elasticsearch.es_index_listener
58 | propagate = 0
59 |
60 | [handler_console]
61 | class = StreamHandler
62 | args = (sys.stderr,)
63 | level = NOTSET
64 | formatter = generic
65 |
66 | [formatter_generic]
67 | format = %(levelname)s [%(name)s][%(threadName)s] %(message)s
68 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/registry.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var _ = require('underscore');
3 |
4 | class Registry {
5 | constructor(options) {
6 | // May provide custom providedBy and fallback functions
7 | this.views = {};
8 | _.extend(this, options);
9 | }
10 |
11 | providedBy(obj) {
12 | return obj['@type'] || [];
13 | }
14 |
15 | register(view, for_, name) {
16 | name = name || '';
17 | var views = this.views[name];
18 | if (!views) {
19 | this.views[name] = views = {};
20 | }
21 | views[for_] = view;
22 | }
23 |
24 | unregister(for_, name) {
25 | var views = this.views[name || ''];
26 | if (!views) {
27 | return;
28 | }
29 | delete views[for_];
30 | }
31 |
32 | lookup(obj, name) {
33 | var views = this.views[name || ''];
34 | if (!views) {
35 | return this.fallback(obj, name);
36 | }
37 |
38 | var provided = this.providedBy(obj);
39 | for (var i = 0, len = provided.length; i < len; i++) {
40 | var view = views[provided[i]];
41 | if (view) {
42 | return view;
43 | }
44 | }
45 | return this.fallback(obj, name);
46 | }
47 |
48 | getAll(name) {
49 | var views = this.views[name || ''];
50 | return views || {};
51 | }
52 |
53 | fallback(obj, name) {
54 | return;
55 | }
56 | }
57 |
58 | module.exports = Registry;
59 |
--------------------------------------------------------------------------------
/src/snowflakes/upgrade/__init__.py:
--------------------------------------------------------------------------------
1 | from pyramid.interfaces import PHASE2_CONFIG
2 | from snovault import (
3 | TYPES,
4 | UPGRADER,
5 | )
6 | from snovault.upgrader import default_upgrade_finalizer
7 |
8 | LATE = 10
9 |
10 |
11 | def includeme(config):
12 | config.scan()
13 |
14 | def callback():
15 | """ add_upgrade for all item types
16 | """
17 | upgrader = config.registry[UPGRADER]
18 | types = config.registry[TYPES]
19 | for type_info in types.by_item_type.values():
20 | version = type_info.schema_version
21 | if version is not None:
22 | upgrader.add_upgrade(type_info.name, version)
23 |
24 | config.action('add_upgrades', callback, order=PHASE2_CONFIG)
25 |
26 | def default_upgrades():
27 | """ add_upgrade for all item types
28 | """
29 | upgrader = config.registry[UPGRADER]
30 | types = config.registry[TYPES]
31 | for type_info in types.by_item_type.values():
32 | if type_info.name not in upgrader:
33 | continue
34 | if not upgrader[type_info.name].upgrade_steps:
35 | upgrader[type_info.name].add_upgrade_step(run_finalizer)
36 |
37 | config.action('add_default_upgrades', default_upgrades, order=LATE)
38 |
39 |
40 | @default_upgrade_finalizer
41 | def finalizer(value, system, version):
42 | # Update the default properties
43 | value['schema_version'] = version
44 |
45 |
46 | def run_finalizer(value, system):
47 | pass
48 |
--------------------------------------------------------------------------------
/src/snovault/json_renderer.py:
--------------------------------------------------------------------------------
1 | from pyramid.threadlocal import get_current_request
2 | import json
3 | import pyramid.renderers
4 | import uuid
5 |
6 |
7 | def includeme(config):
8 | config.add_renderer(None, json_renderer)
9 |
10 |
11 | class JSON(pyramid.renderers.JSON):
12 | '''Provide easier access to the configured serializer
13 | '''
14 | def dumps(self, value):
15 | request = get_current_request()
16 | default = self._make_default(request)
17 | return json.dumps(value, default=default, **self.kw)
18 |
19 |
20 | class BinaryFromJSON:
21 | def __init__(self, app_iter):
22 | self.app_iter = app_iter
23 |
24 | def __len__(self):
25 | return len(self.app_iter)
26 |
27 | def __iter__(self):
28 | for s in self.app_iter:
29 | yield s.encode('utf-8')
30 |
31 |
32 | class JSONResult(object):
33 | def __init__(self):
34 | self.app_iter = []
35 | self.write = self.app_iter.append
36 |
37 | @classmethod
38 | def serializer(cls, value, **kw):
39 | fp = cls()
40 | json.dump(value, fp, **kw)
41 | if str is bytes:
42 | return fp.app_iter
43 | else:
44 | return BinaryFromJSON(fp.app_iter)
45 |
46 |
47 | json_renderer = JSON(serializer=JSONResult.serializer)
48 |
49 |
50 | def uuid_adapter(obj, request):
51 | return str(obj)
52 |
53 |
54 | def listy_adapter(obj, request):
55 | return list(obj)
56 |
57 |
58 | json_renderer.add_adapter(uuid.UUID, uuid_adapter)
59 | json_renderer.add_adapter(set, listy_adapter)
60 | json_renderer.add_adapter(frozenset, listy_adapter)
61 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_upgrader.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def step1(value, system):
5 | value['step1'] = True
6 | return value
7 |
8 |
9 | def step2(value, system):
10 | value['step2'] = True
11 | return value
12 |
13 |
14 | def finalizer(value, system, version):
15 | value['schema_version'] = version
16 | return value
17 |
18 |
19 | @pytest.fixture
20 | def schema_upgrader():
21 | from snovault.upgrader import SchemaUpgrader
22 | schema_upgrader = SchemaUpgrader('test', '3')
23 | schema_upgrader.add_upgrade_step(step1, dest='2')
24 | schema_upgrader.add_upgrade_step(step2, source='2', dest='3')
25 | return schema_upgrader
26 |
27 |
28 | def test_upgrade(schema_upgrader):
29 | value = schema_upgrader.upgrade({}, '')
30 | assert value['step1']
31 | assert value['step2']
32 |
33 |
34 | def test_finalizer(schema_upgrader):
35 | schema_upgrader.finalizer = finalizer
36 | value = schema_upgrader.upgrade({})
37 | assert value['schema_version'] == '3'
38 |
39 |
40 | def test_declarative_config():
41 | from pyramid.config import Configurator
42 | from snovault.interfaces import UPGRADER
43 | config = Configurator()
44 | config.include('snovault.config')
45 | config.include('snovault.upgrader')
46 | config.include('.testing_upgrader')
47 | config.include('snovault.elasticsearch.searches.configs')
48 | config.commit()
49 |
50 | upgrader = config.registry[UPGRADER]
51 | value = upgrader.upgrade('testing_upgrader', {}, '')
52 | assert value['step1']
53 | assert value['step2']
54 | assert value['schema_version'] == '3'
55 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_lightbox.scss:
--------------------------------------------------------------------------------
1 | .lightbox {
2 | visibility: hidden;
3 | position: fixed;
4 | top: 0;
5 | left: 0;
6 | bottom: 0;
7 | right: 0;
8 | background-color: #fff;
9 | background-color: rgba(255,255,255,.8);
10 | opacity: 0;
11 | z-index: 1100;
12 | @include transition(all 200ms);
13 | text-align: center;
14 | cursor: pointer;
15 |
16 | &.active {
17 | visibility: visible;
18 | opacity: 1;
19 | @include transition(all 200ms);
20 | }
21 |
22 | img {
23 | display: block;
24 | border: 1px solid #808080;
25 | }
26 | }
27 |
28 |
29 | .lightbox-img {
30 | position: relative;
31 | display: inline-block;
32 | margin-top: 20px;
33 | text-align: center;
34 | max-width: 90%;
35 |
36 | a {
37 | position: relative;
38 | display: block;
39 | vertical-align: middle;
40 | margin: 0 auto;
41 | text-align: left;
42 |
43 | img {
44 | display: block;
45 | width: auto;
46 | height: auto;
47 | margin: 0 auto;
48 | max-width: 100%;
49 | line-height: 0;
50 | }
51 | }
52 | }
53 |
54 |
55 | .lightbox-close {
56 | position: absolute;
57 | top: -12px;
58 | right: -12px;
59 | width: 24px;
60 | height: 24px;
61 | background: transparent;
62 | border: none;
63 | background-image: inline-image('../img/close-icon.svg');
64 |
65 | .no-svg & {
66 | background-image: inline-image('../img/close-icon.png');
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_code.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Code (inline and block)
3 | // --------------------------------------------------
4 |
5 |
6 | // Inline and block code styles
7 | code,
8 | kbd,
9 | pre,
10 | samp {
11 | font-family: $font-family-monospace;
12 | }
13 |
14 | // Inline code
15 | code {
16 | padding: 2px 4px;
17 | font-size: 90%;
18 | color: $code-color;
19 | background-color: $code-bg;
20 | white-space: nowrap;
21 | border-radius: $border-radius-base;
22 | }
23 |
24 | // User input typically entered via keyboard
25 | kbd {
26 | padding: 2px 4px;
27 | font-size: 90%;
28 | color: $kbd-color;
29 | background-color: $kbd-bg;
30 | border-radius: $border-radius-small;
31 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);
32 | }
33 |
34 | // Blocks of code
35 | pre {
36 | display: block;
37 | padding: (($line-height-computed - 1) / 2);
38 | margin: 0 0 ($line-height-computed / 2);
39 | font-size: ($font-size-base - 1); // 14px to 13px
40 | line-height: $line-height-base;
41 | word-break: break-all;
42 | word-wrap: break-word;
43 | color: $pre-color;
44 | background-color: $pre-bg;
45 | border: 1px solid $pre-border-color;
46 | border-radius: $border-radius-base;
47 |
48 | // Account for some code outputs that place code tags in pre tags
49 | code {
50 | padding: 0;
51 | font-size: inherit;
52 | color: inherit;
53 | white-space: pre-wrap;
54 | background-color: transparent;
55 | border-radius: 0;
56 | }
57 | }
58 |
59 | // Enable scrollable blocks of code
60 | .pre-scrollable {
61 | max-height: $pre-scrollable-max-height;
62 | overflow-y: scroll;
63 | }
64 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/compat.js:
--------------------------------------------------------------------------------
1 | /*jshint strict:false */
2 | require('@babel/polyfill');
3 |
4 | // Chrome 42 fetch does not have abort.
5 | window.fetch = undefined;
6 | require('whatwg-fetch');
7 |
8 | (function () {
9 |
10 | if (typeof console === 'undefined') {
11 | window.console = {
12 | log: function () {}
13 | };
14 | }
15 | var console_methods = [
16 | 'count',
17 | 'dir',
18 | 'error',
19 | 'group',
20 | 'groupCollapsed',
21 | 'groupEnd',
22 | 'info',
23 | 'time',
24 | 'timeEnd',
25 | 'trace',
26 | 'warn',
27 | 'debug',
28 | 'table',
29 | 'assert'
30 | ];
31 | for (var i=0, l=console_methods.length; i < l; i++) {
32 | var name = console_methods[i];
33 | if (window.console[name] === undefined) {
34 | window.console[name] = window.console.log;
35 | }
36 | }
37 |
38 | })();
39 |
40 | // https://gist.github.com/elijahmanor/6452535
41 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
42 | if (!Element.prototype.matches) (function () {
43 | var proto = Element.prototype;
44 | proto.matches = proto.matchesSelector ||
45 | proto.mozMatchesSelector || proto.msMatchesSelector ||
46 | proto.oMatchesSelector || proto.webkitMatchesSelector || (function (selector) {
47 | var element = this;
48 | var matches = (element.document || element.ownerDocument).querySelectorAll(selector);
49 | var i = 0;
50 | while (matches[i] && matches[i] !== element) {
51 | i++;
52 | }
53 | return matches[i] ? true : false;
54 | });
55 | })();
56 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/__tests__/server-render-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.autoMockOff();
4 |
5 | // Fixes https://github.com/facebook/jest/issues/78
6 | jest.dontMock('react');
7 | jest.dontMock('underscore');
8 |
9 | describe("Server rendering", function () {
10 | var React;
11 | var App;
12 | var document;
13 | var home_url = "http://localhost/";
14 | var home = {
15 | "@id": "/",
16 | "@type": ["Portal"],
17 | "portal_title": "SNOWFLAKES",
18 | "title": "Home"
19 | };
20 |
21 | beforeEach(function () {
22 | require('../../libs/react-patches');
23 | React = require('react');
24 | App = require('..');
25 | var server_app = ;
26 | var markup = '\n' + React.renderToString(server_app);
27 | var parser = new DOMParser();
28 | document = parser.parseFromString(markup, 'text/html');
29 | window.location.href = home_url;
30 | });
31 |
32 | it("renders the application to html", function () {
33 | expect(document.title).toBe(home.portal_title);
34 | });
35 |
36 | it("react render http-equiv correctly", function () {
37 | var meta_http_equiv = document.querySelectorAll('meta[http-equiv]');
38 | expect(meta_http_equiv.length).not.toBe(0);
39 | });
40 |
41 | it("mounts the application over the rendered html", function () {
42 | var props = App.getRenderedProps(document);
43 | var app = React.render( , document);
44 | expect(app.getDOMNode()).toBe(document.documentElement);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/src/snovault/tests/toolfixtures.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | # Fixtures for app
5 |
6 | @pytest.fixture
7 | def registry(app):
8 | return app.registry
9 |
10 |
11 | @pytest.fixture
12 | def auditor(registry):
13 | import snovault.interfaces
14 | return registry[snovault.interfaces.AUDITOR]
15 |
16 |
17 | @pytest.fixture
18 | def blobs(registry):
19 | import snovault.interfaces
20 | return registry[snovault.interfaces.BLOBS]
21 |
22 |
23 | @pytest.fixture
24 | def calculated_properties(registry):
25 | import snovault.interfaces
26 | return registry[snovault.interfaces.CALCULATED_PROPERTIES]
27 |
28 |
29 | @pytest.fixture
30 | def collections(registry):
31 | import snovault.interfaces
32 | return registry[snovault.interfaces.COLLECTIONS]
33 |
34 |
35 | @pytest.fixture
36 | def connection(registry):
37 | import snovault.interfaces
38 | return registry[snovault.interfaces.CONNECTION]
39 |
40 |
41 | @pytest.fixture
42 | def elasticsearch(registry):
43 | from snovault.elasticsearch import ELASTIC_SEARCH
44 | return registry[ELASTIC_SEARCH]
45 |
46 |
47 | @pytest.fixture
48 | def storage(registry):
49 | import snovault.interfaces
50 | return registry[snovault.interfaces.STORAGE]
51 |
52 |
53 | @pytest.fixture
54 | def root(registry):
55 | import snovault.interfaces
56 | return registry[snovault.interfaces.ROOT]
57 |
58 |
59 | @pytest.fixture
60 | def types(registry):
61 | import snovault.interfaces
62 | return registry[snovault.interfaces.TYPES]
63 |
64 |
65 | @pytest.fixture
66 | def upgrader(registry):
67 | import snovault.interfaces
68 | return registry[snovault.interfaces.UPGRADER]
69 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/statuslabel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var globals = require('./globals');
4 |
5 | var StatusLabel = module.exports.StatusLabel = React.createClass({
6 | render: function() {
7 | var status = this.props.status;
8 | var title = this.props.title;
9 | if (typeof status === 'string') {
10 | // Display simple string and optional title in badge
11 | return (
12 |
13 |
14 | {title ? {title + ': '} : null}
15 | {this.props.buttonLabel ? this.props.buttonLabel : status}
16 |
17 |
18 | );
19 | } else if (typeof status === 'object') {
20 | // Display a list of badges from array of objects with status and optional title
21 | return (
22 |
23 | {status.map(function (status) {
24 | return(
25 |
26 | {status.title ? {status.title + ': '} : null}
27 | {status.status}
28 |
29 | );
30 | })}
31 |
32 | );
33 | } else {
34 | return null;
35 | }
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/src/snovault/commands/es_index_data.py:
--------------------------------------------------------------------------------
1 | from pyramid.paster import get_app
2 | import logging
3 | from webtest import TestApp
4 |
5 | index = 'snovault'
6 |
7 | EPILOG = __doc__
8 |
9 |
10 | def run(app, collections=None, record=False):
11 | environ = {
12 | 'HTTP_ACCEPT': 'application/json',
13 | 'REMOTE_USER': 'INDEXER',
14 | }
15 | testapp = TestApp(app, environ)
16 | testapp.post_json('/index', {
17 | 'last_xmin': None,
18 | 'types': collections,
19 | 'recovery': True
20 | }
21 | )
22 |
23 |
24 | def main():
25 | ''' Indexes app data loaded to elasticsearch '''
26 |
27 | import argparse
28 | parser = argparse.ArgumentParser(
29 | description="Index data in Elastic Search", epilog=EPILOG,
30 | formatter_class=argparse.RawDescriptionHelpFormatter,
31 | )
32 | parser.add_argument('--item-type', action='append', help="Item type")
33 | parser.add_argument('--record', default=False, action='store_true', help="Record the xmin in ES meta")
34 | parser.add_argument('--app-name', help="Pyramid app name in configfile")
35 | parser.add_argument('config_uri', help="path to configfile")
36 | args = parser.parse_args()
37 |
38 | logging.basicConfig()
39 | options = {
40 | 'embed_cache.capacity': '5000',
41 | 'indexer': 'true',
42 | }
43 | app = get_app(args.config_uri, args.app_name, options)
44 |
45 | # Loading app will have configured from config file. Reconfigure here:
46 | logging.getLogger('snovault').setLevel(logging.DEBUG)
47 | return run(app, args.item_type, args.record)
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/src/snowflakes/commands/es_index_data.py:
--------------------------------------------------------------------------------
1 | from pyramid.paster import get_app
2 | import logging
3 | from webtest import TestApp
4 |
5 | index = 'snowflakes'
6 |
7 | EPILOG = __doc__
8 |
9 |
10 | def run(app, collections=None, record=False):
11 | environ = {
12 | 'HTTP_ACCEPT': 'application/json',
13 | 'REMOTE_USER': 'INDEXER',
14 | }
15 | testapp = TestApp(app, environ)
16 | testapp.post_json('/index', {
17 | 'last_xmin': None,
18 | 'types': collections,
19 | 'recovery': True
20 | }
21 | )
22 |
23 |
24 | def main():
25 | ''' Indexes app data loaded to elasticsearch '''
26 |
27 | import argparse
28 | parser = argparse.ArgumentParser(
29 | description="Index data in Elastic Search", epilog=EPILOG,
30 | formatter_class=argparse.RawDescriptionHelpFormatter,
31 | )
32 | parser.add_argument('--item-type', action='append', help="Item type")
33 | parser.add_argument('--record', default=False, action='store_true', help="Record the xmin in ES meta")
34 | parser.add_argument('--app-name', help="Pyramid app name in configfile")
35 | parser.add_argument('config_uri', help="path to configfile")
36 | args = parser.parse_args()
37 |
38 | logging.basicConfig()
39 | options = {
40 | 'embed_cache.capacity': '5000',
41 | 'indexer': 'true',
42 | }
43 | app = get_app(args.config_uri, args.app_name, options)
44 |
45 | # Loading app will have configured from config file. Reconfigure here:
46 | logging.getLogger('snovault').setLevel(logging.DEBUG)
47 | return run(app, args.item_type, args.record)
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/src/snowflakes/schemas/access_key.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Admin access key",
3 | "id": "/profiles/access_key_admin.json",
4 | "$schema": "http://json-schema.org/draft-04/schema#",
5 | "required": [],
6 | "additionalProperties": false,
7 | "mixinProperties": [
8 | { "$ref": "mixins.json#/schema_version" },
9 | { "$ref": "mixins.json#/uuid" }
10 | ],
11 | "type": "object",
12 | "properties": {
13 | "schema_version": {
14 | "default": "2"
15 | },
16 | "status": {
17 | "title": "Status",
18 | "type": "string",
19 | "default": "current",
20 | "enum" : [
21 | "current",
22 | "deleted"
23 | ]
24 | },
25 | "user": {
26 | "title": "User",
27 | "comment": "Only admins are allowed to set this value.",
28 | "type": "string",
29 | "linkTo": "User",
30 | "permission": "import_items"
31 | },
32 | "description": {
33 | "title": "Description",
34 | "type": "string",
35 | "default": ""
36 | },
37 | "access_key_id": {
38 | "title": "Access key ID",
39 | "comment": "Only admins are allowed to set this value.",
40 | "type": "string",
41 | "permission": "import_items",
42 | "uniqueKey": true
43 | },
44 | "secret_access_key_hash": {
45 | "title": "Secret access key Hash",
46 | "comment": "Only admins are allowed to set this value.",
47 | "type": "string",
48 | "permission": "import_items"
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/snowflakes/commands/create_admin_user.py:
--------------------------------------------------------------------------------
1 | from pyramid.paster import get_app
2 | import logging
3 | from webtest import TestApp
4 |
5 | EPILOG = __doc__
6 |
7 |
8 | def run(app, first_name, last_name, email, lab):
9 | environ = {
10 | 'HTTP_ACCEPT': 'application/json',
11 | 'REMOTE_USER': 'TEST',
12 | }
13 | testapp = TestApp(app, environ)
14 | testapp.post_json('/user', {
15 | 'first_name': first_name,
16 | 'last_name': last_name,
17 | 'email': email,
18 | 'lab': lab,
19 | 'groups': ['admin']
20 | })
21 |
22 |
23 | def main():
24 | ''' Creates an admin user '''
25 |
26 | import argparse
27 | parser = argparse.ArgumentParser(
28 | description="Creates an admin user", epilog=EPILOG,
29 | formatter_class=argparse.RawDescriptionHelpFormatter,
30 | )
31 | parser.add_argument('--first-name', default='Admin', help="First name")
32 | parser.add_argument('--last-name', default='Test', help="Last name")
33 | parser.add_argument('--email', default='admin_test@example.org', help="E-mail")
34 | parser.add_argument('--lab', default='/labs/j-michael-cherry/', help="Lab")
35 | parser.add_argument('--app-name', help="Pyramid app name in configfile")
36 | parser.add_argument('config_uri', help="path to configfile")
37 | args = parser.parse_args()
38 |
39 | logging.basicConfig()
40 | options = {
41 | 'embed_cache.capacity': '5000',
42 | 'indexer': 'true',
43 | }
44 | app = get_app(args.config_uri, args.app_name, options)
45 |
46 | logging.getLogger('encoded').setLevel(logging.DEBUG)
47 | return run(app, args.first_name, args.last_name, args.email, args.lab)
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/src/snowflakes/schema_formats.py:
--------------------------------------------------------------------------------
1 | import re
2 | import rfc3987
3 | from jsonschema_serialize_fork import FormatChecker
4 | from pyramid.threadlocal import get_current_request
5 | from uuid import UUID
6 |
7 | accession_re = re.compile(r'^SNO(SS|FL)[0-9][0-9][0-9][A-Z][A-Z][A-Z]$')
8 | test_accession_re = re.compile(r'^TST(SS|FL)[0-9][0-9][0-9]([0-9][0-9][0-9]|[A-Z][A-Z][A-Z])$')
9 | uuid_re = re.compile(r'(?i)\{?(?:[0-9a-f]{4}-?){8}\}?')
10 |
11 | @FormatChecker.cls_checks("uuid")
12 | def is_uuid(instance):
13 | # Python's UUID ignores all dashes, whereas Postgres is more strict
14 | # http://www.postgresql.org/docs/9.2/static/datatype-uuid.html
15 | return bool(uuid_re.match(instance))
16 |
17 |
18 | def is_accession(instance):
19 | ''' just a pattern checker '''
20 | # Unfortunately we cannot access the accessionType here
21 | return (
22 | accession_re.match(instance) is not None or
23 | test_accession_re.match(instance) is not None
24 | )
25 |
26 |
27 | @FormatChecker.cls_checks("accession")
28 | def is_accession_for_server(instance):
29 | from .server_defaults import (
30 | ACCESSION_FACTORY,
31 | test_accession,
32 | )
33 | # Unfortunately we cannot access the accessionType here
34 | if accession_re.match(instance):
35 | return True
36 | request = get_current_request()
37 | if request.registry[ACCESSION_FACTORY] is test_accession:
38 | if test_accession_re.match(instance):
39 | return True
40 | return False
41 |
42 |
43 | @FormatChecker.cls_checks("uri", raises=ValueError)
44 | def is_uri(instance):
45 | if ':' not in instance:
46 | # We want only absolute uris
47 | return False
48 | return rfc3987.parse(instance, rule="URI_reference")
49 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_alerts.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Alerts
3 | // --------------------------------------------------
4 |
5 |
6 | // Base styles
7 | // -------------------------
8 |
9 | .alert {
10 | padding: $alert-padding;
11 | margin-bottom: $line-height-computed;
12 | border: 1px solid transparent;
13 | border-radius: $alert-border-radius;
14 |
15 | // Headings for larger alerts
16 | h4 {
17 | margin-top: 0;
18 | // Specified for the h4 to prevent conflicts of changing $headings-color
19 | color: inherit;
20 | }
21 | // Provide class for links that match alerts
22 | .alert-link {
23 | font-weight: $alert-link-font-weight;
24 | }
25 |
26 | // Improve alignment and spacing of inner content
27 | > p,
28 | > ul {
29 | margin-bottom: 0;
30 | }
31 | > p + p {
32 | margin-top: 5px;
33 | }
34 | }
35 |
36 | // Dismissable alerts
37 | //
38 | // Expand the right padding and account for the close button's positioning.
39 |
40 | .alert-dismissable {
41 | padding-right: ($alert-padding + 20);
42 |
43 | // Adjust close link position
44 | .close {
45 | position: relative;
46 | top: -2px;
47 | right: -21px;
48 | color: inherit;
49 | }
50 | }
51 |
52 | // Alternate styles
53 | //
54 | // Generate contextual modifier classes for colorizing the alert.
55 |
56 | .alert-success {
57 | @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text);
58 | }
59 | .alert-info {
60 | @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text);
61 | }
62 | .alert-warning {
63 | @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text);
64 | }
65 | .alert-danger {
66 | @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text);
67 | }
68 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/blocks/item.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var fetched = require('../fetched');
4 | var globals = require('../globals');
5 | var noarg_memoize = require('../../libs/noarg-memoize');
6 | var ObjectPicker = require('../inputs').ObjectPicker;
7 |
8 |
9 | var ItemBlockView = module.exports.ItemBlockView = React.createClass({
10 | render: function() {
11 | var ViewComponent = globals.content_views.lookup(this.props.context);
12 | return ;
13 | }
14 | });
15 |
16 |
17 | var FetchedItemBlockView = React.createClass({
18 |
19 | shouldComponentUpdate: function(nextProps) {
20 | return (nextProps.value.item != this.props.value.item);
21 | },
22 |
23 | render: function() {
24 | var context = this.props.value.item;
25 | if (typeof context === 'object') {
26 | return ;
27 | }
28 | if (typeof context === 'string') {
29 | return (
30 |
31 |
32 |
33 |
34 | );
35 | }
36 | return null;
37 | }
38 | });
39 |
40 |
41 | globals.blocks.register({
42 | label: 'item block',
43 | icon: 'icon icon-paperclip',
44 | schema: noarg_memoize(function() {
45 | var ReactForms = require('react-forms');
46 | return ReactForms.schema.Mapping({}, {
47 | item: ReactForms.schema.Scalar({label: 'Item', input: }),
48 | className: ReactForms.schema.Scalar({label: 'CSS Class'}),
49 | });
50 | }),
51 | view: FetchedItemBlockView
52 | }, 'itemblock');
53 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/react-middleware.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var doctype = '\n';
4 | var transformResponse = require('subprocess-middleware').transformResponse;
5 | var fs = require('fs');
6 | var inline = fs.readFileSync(__dirname + '/../build/inline.js').toString();
7 |
8 | var render = function (Component, body, res) {
9 | //var start = process.hrtime();
10 | var context = JSON.parse(body);
11 | var props = {
12 | context: context,
13 | href: res.getHeader('X-Request-URL') || context['@id'],
14 | inline: inline
15 | };
16 | var markup;
17 | try {
18 | markup = React.renderToString( );
19 | } catch (err) {
20 | props.context = {
21 | '@type': ['RenderingError', 'error'],
22 | status: 'error',
23 | code: 500,
24 | title: 'Server Rendering Error',
25 | description: 'The server erred while rendering the page.',
26 | detail: err.stack,
27 | log: console._stdout.toString(),
28 | warn: console._stderr.toString(),
29 | context: context
30 | };
31 | // To debug in browser, pause on caught exceptions:
32 | // app.setProps({context: app.props.context.context})
33 | res.statusCode = 500;
34 | markup = React.renderToString( );
35 | }
36 | res.setHeader('Content-Type', 'text/html; charset=utf-8');
37 | //var duration = process.hrtime(start);
38 | //res.setHeader('X-React-duration', duration[0] * 1e6 + (duration[1] / 1000 | 0));
39 | return new Buffer(doctype + markup);
40 | };
41 |
42 |
43 | module.exports.build = function (Component) {
44 | return transformResponse(render.bind(null, Component));
45 | };
46 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/_state.scss:
--------------------------------------------------------------------------------
1 | /* top padding for small screens */
2 | #content {
3 | padding-top: 0;
4 |
5 | @media (min-width: $screen-md-min) {
6 | padding-top: 75px;
7 | }
8 | }
9 |
10 | .app-version {
11 | height: $appVersionHeight;
12 | line-height: $appVersionHeight;
13 | text-align: center;
14 | @include font-size(0.9);
15 | color: #a0a0a0;
16 | }
17 |
18 | // make footer look good on larger screens
19 | // and activate sticky footer
20 | @media (min-width: $screen-sm-min) {
21 | .footer-row { margin-left: 0; margin-right: 0; }
22 |
23 | #page-footer {
24 | height: $footerHeight;
25 | }
26 |
27 | .page-footer {
28 | height: $footerHeight - $appVersionHeight;
29 | }
30 |
31 | .app-version {
32 | text-align: left;
33 | }
34 |
35 | @include sticky-footer($footerHeight, $footerTopBorderHeight, "#layout", "#layout-footer", "#page-footer");
36 |
37 | .footer-links {
38 | list-style: none outside none;
39 | margin-left: -5px;
40 | padding-left: 0;
41 | li {
42 | display: inline-block;
43 | padding-left: 10px;
44 | padding-right: 10px;
45 | }
46 | a {
47 | display: inline;
48 | margin: 0;
49 | padding: 0;
50 | background-color: transparent;
51 | }
52 | a:hover {
53 | text-decoration: underline;
54 | background-color: transparent;
55 | }
56 | a:active { background-color: transparent; }
57 | }
58 |
59 | .footer-logos {
60 | margin-top: 16px;
61 | li {
62 | display: inline-block;
63 | padding-left: 10px;
64 | padding-right: 10px;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/snowflakes/memlimit.py:
--------------------------------------------------------------------------------
1 | # https://code.google.com/p/modwsgi/wiki/RegisteringCleanupCode
2 |
3 |
4 | class Generator2:
5 | def __init__(self, iterable, callback, environ):
6 | self.__iterable = iterable
7 | self.__callback = callback
8 | self.__environ = environ
9 |
10 | def __iter__(self):
11 | for item in self.__iterable:
12 | yield item
13 |
14 | def close(self):
15 | try:
16 | if hasattr(self.__iterable, 'close'):
17 | self.__iterable.close()
18 | finally:
19 | self.__callback(self.__environ)
20 |
21 |
22 | class ExecuteOnCompletion2:
23 | def __init__(self, application, callback):
24 | self.__application = application
25 | self.__callback = callback
26 |
27 | def __call__(self, environ, start_response):
28 | try:
29 | result = self.__application(environ, start_response)
30 | except:
31 | self.__callback(environ)
32 | raise
33 | return Generator2(result, self.__callback, environ)
34 |
35 |
36 | import logging
37 | import psutil
38 | import humanfriendly
39 |
40 |
41 | def rss_checker(rss_limit=None):
42 | log = logging.getLogger(__name__)
43 | process = psutil.Process()
44 |
45 | def callback(environ):
46 | rss = process.memory_info().rss
47 | if rss_limit and rss > rss_limit:
48 | msg = "Restarting process. Memory usage exceeds limit of %d: %d"
49 | log.error(msg, rss_limit, rss)
50 | process.kill()
51 |
52 | return callback
53 |
54 |
55 | def filter_app(app, global_conf, rss_limit=None):
56 | if rss_limit is not None:
57 | rss_limit = humanfriendly.parse_size(rss_limit)
58 |
59 | callback = rss_checker(rss_limit)
60 | return ExecuteOnCompletion2(app, callback)
61 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_grid.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Grid system
3 | // --------------------------------------------------
4 |
5 |
6 | // Container widths
7 | //
8 | // Set the container width, and override it for fixed navbars in media queries.
9 |
10 | .container {
11 | @include container-fixed();
12 |
13 | @media (min-width: $screen-sm-min) {
14 | width: $container-sm;
15 | }
16 | @media (min-width: $screen-md-min) {
17 | width: $container-md;
18 | }
19 | @media (min-width: $screen-lg-min) {
20 | width: $container-lg;
21 | }
22 | }
23 |
24 |
25 | // Fluid container
26 | //
27 | // Utilizes the mixin meant for fixed width containers, but without any defined
28 | // width for fluid, full width layouts.
29 |
30 | .container-fluid {
31 | @include container-fixed();
32 | }
33 |
34 |
35 | // Row
36 | //
37 | // Rows contain and clear the floats of your columns.
38 |
39 | .row {
40 | @include make-row();
41 | }
42 |
43 |
44 | // Columns
45 | //
46 | // Common styles for small and large grid columns
47 |
48 | @include make-grid-columns();
49 |
50 |
51 | // Extra small grid
52 | //
53 | // Columns, offsets, pushes, and pulls for extra small devices like
54 | // smartphones.
55 |
56 | @include make-grid(xs);
57 |
58 |
59 | // Small grid
60 | //
61 | // Columns, offsets, pushes, and pulls for the small device range, from phones
62 | // to tablets.
63 |
64 | @media (min-width: $screen-sm-min) {
65 | @include make-grid(sm);
66 | }
67 |
68 |
69 | // Medium grid
70 | //
71 | // Columns, offsets, pushes, and pulls for the desktop device range.
72 |
73 | @media (min-width: $screen-md-min) {
74 | @include make-grid(md);
75 | }
76 |
77 |
78 | // Large grid
79 | //
80 | // Columns, offsets, pushes, and pulls for the large desktop device range.
81 |
82 | @media (min-width: $screen-lg-min) {
83 | @include make-grid(lg);
84 | }
85 |
--------------------------------------------------------------------------------
/src/snowflakes/commands/jsonld_rdf.py:
--------------------------------------------------------------------------------
1 | """\
2 | Available formats: xml, n3, turtle, nt, pretty-xml, trix.
3 | Example.
4 |
5 | %(prog)s "$SITE_URL/search/?type=Item&frame=object"
6 | """
7 | EPILOG = __doc__
8 |
9 | import rdflib
10 |
11 |
12 | def run(sources, output, parser='json-ld', serializer='xml', base=None):
13 | g = rdflib.ConjunctiveGraph()
14 | for url in sources:
15 | g.parse(url, format=parser)
16 | g.serialize(output, format=serializer, base=base)
17 |
18 |
19 | def main():
20 | import argparse
21 | import sys
22 | stdout = sys.stdout
23 | if sys.version_info.major > 2:
24 | stdout = stdout.buffer
25 |
26 | rdflib_parsers = sorted(
27 | p.name for p in rdflib.plugin.plugins(kind=rdflib.parser.Parser)
28 | if '/' not in p.name)
29 | rdflib_serializers = sorted(
30 | p.name for p in rdflib.plugin.plugins(kind=rdflib.serializer.Serializer)
31 | if '/' not in p.name)
32 | parser = argparse.ArgumentParser(
33 | description="Convert JSON-LD from source URLs to RDF", epilog=EPILOG,
34 | formatter_class=argparse.RawDescriptionHelpFormatter,
35 | )
36 | parser.add_argument('sources', metavar='URL', nargs='+', help="URLs to convert")
37 | parser.add_argument(
38 | '-p', '--parser', default='json-ld', help=', '.join(rdflib_parsers))
39 | parser.add_argument(
40 | '-s', '--serializer', default='xml', help=', '.join(rdflib_serializers))
41 | parser.add_argument(
42 | '-b', '--base', default=None, help='Base URL')
43 | parser.add_argument(
44 | '-o', '--output', type=argparse.FileType('wb'), default=stdout,
45 | help="Output file.")
46 | args = parser.parse_args()
47 | run(args.sources, args.output, args.parser, args.serializer, args.base)
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/StickyHeader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const React = require('react');
3 | const offset = require('../libs/offset');
4 | const cloneWithProps = require('react/lib/cloneWithProps');
5 |
6 | module.exports = React.createClass({
7 | render() {
8 | return React.Children.only(this.props.children);
9 | },
10 |
11 | componentDidMount() {
12 | // Avoid shimming as ie8 does not support css transform
13 | if (window.getComputedStyle === undefined) return;
14 | this.stickyHeader();
15 | window.addEventListener('scroll', this.stickyHeader);
16 | window.addEventListener('resize', this.stickyHeader);
17 | },
18 |
19 | componentWillUnmount() {
20 | if (window.getComputedStyle === undefined) return;
21 | window.removeEventListener('scroll', this.stickyHeader);
22 | window.removeEventListener('resize', this.stickyHeader);
23 | },
24 |
25 | stickyHeader() {
26 | // http://stackoverflow.com/a/6625189/199100
27 | // http://css-tricks.com/persistent-headers/
28 | const header = this.getDOMNode();
29 | const table = header.parentElement;
30 | const offsetTop = offset(table).top;
31 | const nb = document.querySelector('.navbar-fixed-top');
32 | let nb_height = 0;
33 |
34 | if (window.getComputedStyle(nb).getPropertyValue('position') === 'fixed') {
35 | nb_height = nb.clientHeight;
36 | }
37 | const scrollTop = document.body.scrollTop + nb_height;
38 | let y = 0;
39 |
40 | if((scrollTop > offsetTop) && (scrollTop < (offsetTop + table.clientHeight))) {
41 | y = scrollTop - offsetTop - 3; // correction for borders
42 | }
43 | const transform = 'translate(0px,' + y + 'px)';
44 | header.style.transform = transform;
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/src/snovault/commands/jsonld_rdf.py:
--------------------------------------------------------------------------------
1 | """\
2 | Available formats: xml, n3, turtle, nt, pretty-xml, trix.
3 | Example.
4 |
5 | %(prog)s "https://YOUR.SNOWVAULT.RUL/search/?type=Item&frame=object"
6 | """
7 | EPILOG = __doc__
8 |
9 | import rdflib
10 |
11 |
12 | def run(sources, output, parser='json-ld', serializer='xml', base=None):
13 | g = rdflib.ConjunctiveGraph()
14 | for url in sources:
15 | g.parse(url, format=parser)
16 | g.serialize(output, format=serializer, base=base)
17 |
18 |
19 | def main():
20 | import argparse
21 | import sys
22 | stdout = sys.stdout
23 | if sys.version_info.major > 2:
24 | stdout = stdout.buffer
25 |
26 | rdflib_parsers = sorted(
27 | p.name for p in rdflib.plugin.plugins(kind=rdflib.parser.Parser)
28 | if '/' not in p.name)
29 | rdflib_serializers = sorted(
30 | p.name for p in rdflib.plugin.plugins(kind=rdflib.serializer.Serializer)
31 | if '/' not in p.name)
32 | parser = argparse.ArgumentParser(
33 | description="Convert JSON-LD from source URLs to RDF", epilog=EPILOG,
34 | formatter_class=argparse.RawDescriptionHelpFormatter,
35 | )
36 | parser.add_argument('sources', metavar='URL', nargs='+', help="URLs to convert")
37 | parser.add_argument(
38 | '-p', '--parser', default='json-ld', help=', '.join(rdflib_parsers))
39 | parser.add_argument(
40 | '-s', '--serializer', default='xml', help=', '.join(rdflib_serializers))
41 | parser.add_argument(
42 | '-b', '--base', default=None, help='Base URL')
43 | parser.add_argument(
44 | '-o', '--output', type=argparse.FileType('wb'), default=stdout,
45 | help="Output file.")
46 | args = parser.parse_args()
47 | run(args.sources, args.output, args.parser, args.serializer, args.base)
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/examples/s3cp.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: latin-1 -*-
3 | import requests, subprocess, shlex, urlparse, os, sys
4 |
5 | AUTHID='user'; AUTHPW='secret'; HEADERS = {'content-type': 'application/json'}; SERVER = 'https://www.encodeproject.org/'
6 | S3_SERVER='s3://encode-files/'
7 |
8 | #get all the file objects
9 | files = requests.get(
10 | 'https://www.encodeproject.org/search/?type=file&frame=embedded&limit=all',
11 | auth=(AUTHID,AUTHPW), headers=HEADERS).json()['@graph']
12 |
13 | #select your file
14 | f_obj = files[123]
15 |
16 | #make the URL that will get redirected - get it from the file object's href property
17 | encode_url = urlparse.urljoin(SERVER,f_obj.get('href'))
18 |
19 | #stream=True avoids actually downloading the file, but it evaluates the redirection
20 | r = requests.get(encode_url, auth=(AUTHID,AUTHPW), headers=HEADERS, allow_redirects=True, stream=True)
21 | try:
22 | r.raise_for_status
23 | except:
24 | print '%s href does not resolve' %(f_obj.get('accession'))
25 | sys.exit()
26 |
27 | #this is the actual S3 https URL after redirection
28 | s3_url = r.url
29 |
30 | #release the connection
31 | r.close()
32 |
33 | #split up the url into components
34 | o = urlparse.urlparse(s3_url)
35 |
36 | #pull out the filename
37 | filename = os.path.basename(o.path)
38 |
39 | #hack together the s3 cp url (with the s3 method instead of https)
40 | bucket_url = S3_SERVER.rstrip('/') + o.path
41 | #print bucket_url
42 |
43 | #ls the file from the bucket
44 | s3ls_string = subprocess.check_output(shlex.split('aws s3 ls %s' %(bucket_url)))
45 | if s3ls_string.rstrip() == "":
46 | print >> sys.stderr, "%s not in bucket" %(bucket_url)
47 | else:
48 | print "%s %s" %(f_obj.get('accession'), s3ls_string.rstrip())
49 |
50 | #do the actual s3 cp
51 | #return_value = subprocess.check_call(shlex.split('aws s3 cp %s %s' %(bucket_url, filename)))
52 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_searches_fields.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture()
5 | def dummy_parent(dummy_request):
6 | from pyramid.testing import DummyResource
7 | from pyramid.security import Allow
8 | from snosearch.parsers import ParamsParser
9 | from snosearch.queries import AbstractQueryFactory
10 | from snovault.elasticsearch.interfaces import ELASTIC_SEARCH
11 | from elasticsearch import Elasticsearch
12 | dummy_request.registry[ELASTIC_SEARCH] = Elasticsearch()
13 | dummy_request.context = DummyResource()
14 | dummy_request.context.__acl__ = lambda: [(Allow, 'group.submitter', 'search_audit')]
15 | class DummyParent():
16 | def __init__(self):
17 | self._meta = {}
18 | self.response = {}
19 | dp = DummyParent()
20 | pp = ParamsParser(dummy_request)
21 | dp._meta = {
22 | 'params_parser': pp,
23 | 'query_builder': AbstractQueryFactory(pp)
24 | }
25 | return dp
26 |
27 |
28 | def test_searches_fields_non_sortable_response_field(dummy_parent):
29 | dummy_parent._meta['params_parser']._request.environ['QUERY_STRING'] = (
30 | 'type=TestingSearchSchema'
31 | )
32 | from snovault.elasticsearch.searches.fields import NonSortableResponseField
33 | nrf = NonSortableResponseField()
34 | r = nrf.render(parent=dummy_parent)
35 | assert r['non_sortable'] == ['pipeline_error_detail', 'description', 'notes']
36 |
37 |
38 | def test_searches_fields_pass_through_response_field():
39 | from snovault.elasticsearch.searches.fields import PassThroughResponseField
40 | ptrf = PassThroughResponseField(
41 | values_to_pass_through={
42 | 'some': 'values',
43 | 'in': 'a',
44 | 'dictionary': [],
45 | }
46 | )
47 | result = ptrf.render()
48 | assert result == {
49 | 'some': 'values',
50 | 'in': 'a',
51 | 'dictionary': []
52 | }
53 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_upgrade_award.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture
5 | def award():
6 | return{
7 | 'name': 'ENCODE2',
8 | }
9 |
10 |
11 | @pytest.fixture
12 | def award_1(award):
13 | item = award.copy()
14 | item.update({
15 | 'schema_version': '1',
16 | 'rfa': "ENCODE2"
17 | })
18 | return item
19 |
20 |
21 | @pytest.fixture
22 | def award_bad(award):
23 | item = award.copy()
24 | item.update({
25 | 'schema_version': '1',
26 | 'rfa': "ENCODE2",
27 | 'status': "Not a status"
28 | })
29 | return item
30 |
31 |
32 | def test_award_upgrade(upgrader, award_1):
33 | value = upgrader.upgrade('award', award_1, target_version='2')
34 | assert value['schema_version'] == '2'
35 | assert value['status'] == 'disabled'
36 |
37 |
38 | def test_award_upgrade_encode3(upgrader, award_1):
39 | award_1['rfa'] = 'ENCODE3'
40 | value = upgrader.upgrade('award', award_1, target_version='2')
41 | assert value['schema_version'] == '2'
42 | assert value['status'] == 'current'
43 |
44 |
45 | def test_award_upgrade_url(upgrader, award_1):
46 | award_1['url'] = ''
47 | value = upgrader.upgrade('award', award_1, target_version='2')
48 | assert value['schema_version'] == '2'
49 | assert 'url' not in value
50 |
51 |
52 | @pytest.fixture
53 | def award_2(award_1):
54 | item = award_1.copy()
55 | item.update({
56 | 'schema_version': '3',
57 | 'viewing_group': 'ENCODE',
58 | })
59 | return item
60 |
61 |
62 | def test_award_upgrade_viewing_group(upgrader, award_2):
63 | value = upgrader.upgrade('award', award_2, target_version='3')
64 | assert value['schema_version'] == '3'
65 | assert value['viewing_group'] == 'ENCODE'
66 |
67 |
68 | def test_award_batch_upgrade(award_bad, testapp):
69 | # cannot post an invalid object
70 | res = testapp.post_json('/awards/', award_bad, status=422)
71 |
--------------------------------------------------------------------------------
/src/snovault/jsongraph.py:
--------------------------------------------------------------------------------
1 | from snovault import Item, Root, CONNECTION
2 | from snovault.elasticsearch.indexer import all_uuids
3 | from past.builtins import basestring
4 | from pyramid.view import view_config
5 |
6 |
7 | def includeme(config):
8 | config.scan(__name__)
9 |
10 |
11 | def uuid_to_ref(obj, path):
12 | if isinstance(path, basestring):
13 | path = path.split('.')
14 | if not path:
15 | return
16 | name = path[0]
17 | remaining = path[1:]
18 | value = obj.get(name, None)
19 | if value is None:
20 | return
21 | if remaining:
22 | if isinstance(value, list):
23 | for v in value:
24 | uuid_to_ref(v, remaining)
25 | else:
26 | uuid_to_ref(value, remaining)
27 | return
28 | if isinstance(value, list):
29 | obj[name] = [
30 | {'$type': 'ref', 'value': ['uuid', v]}
31 | for v in value
32 | ]
33 | else:
34 | obj[name] = {'$type': 'ref', 'value': ['uuid', value]}
35 |
36 |
37 | def item_jsongraph(context, properties):
38 | properties = properties.copy()
39 | for path in context.type_info.schema_links:
40 | uuid_to_ref(properties, path)
41 | properties['uuid'] = str(context.uuid)
42 | properties['@type'] = context.type_info.name
43 | return properties
44 |
45 |
46 | @view_config(context=Root, request_method='GET', name='jsongraph')
47 | def jsongraph(context, request):
48 | conn = request.registry[CONNECTION]
49 | cache = {
50 | 'uuid': {},
51 | }
52 | for uuid in all_uuids(request.registry):
53 | item = conn[uuid]
54 | properties = item.__json__(request)
55 | cache['uuid'][uuid] = item_jsongraph(item, properties)
56 | for k, values in item.unique_keys(properties).items():
57 | for v in values:
58 | cache.setdefault(k, {})[v] = {'$type': 'ref', 'value': ['uuid', str(item.uuid)]}
59 | return cache
60 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_webuser_auth.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | @pytest.fixture(scope='session')
4 | def test_user():
5 | return {'username': 'user@test.com', 'password': 'test'}
6 |
7 |
8 | def test_login_unknown_user(anontestapp, test_user):
9 | res = anontestapp.post_json('/login', test_user, status=403)
10 |
11 | def test_invalid_create_user(testapp):
12 | url = '/users/'
13 | item = {
14 | 'emailllll': 'blah@blah.com',
15 | 'first_name': 'Persona',
16 | 'last_name': 'Test User',
17 | 'password' : 'password'
18 | }
19 | resp = testapp.post_json(url, item, status=422)
20 | print(resp)
21 | assert resp.json['errors'] is not None
22 |
23 |
24 | def test_login_logout(testapp, anontestapp, app_settings, test_user):
25 | # Create a user with the test_users email
26 | url = '/users/'
27 | item = {
28 | 'email': test_user['username'],
29 | 'first_name': 'Persona',
30 | 'last_name': 'Test User',
31 | 'password' : test_user['password']
32 | }
33 | testapp.post_json(url, item, status=201)
34 |
35 | # Log in
36 | login = {'username' : item['email'], 'password': item['password']}
37 | res = anontestapp.post_json('/login', login, status=200)
38 | assert 'Set-Cookie' in res.headers
39 | assert res.json['auth.userid'] == login['username']
40 |
41 | # Log out
42 | res = anontestapp.get('/logout?redirect=false', status=200)
43 | assert 'Set-Cookie' in res.headers
44 | assert 'auth.userid' not in res.json
45 |
46 | def test_impersonate_user(anontestapp, admin, submitter):
47 | res = anontestapp.post_json(
48 | '/impersonate-user', {'userid': submitter['email']},
49 | extra_environ={'REMOTE_USER': str(admin['email'])}, status=200)
50 | assert 'Set-Cookie' in res.headers
51 | assert res.json['auth.userid'] == submitter['email']
52 | res = anontestapp.get('/session-properties')
53 | assert res.json['auth.userid'] == submitter['email']
54 |
--------------------------------------------------------------------------------
/src/snovault/elasticsearch/cached_views.py:
--------------------------------------------------------------------------------
1 | """ Cached views used when model was pulled from elasticsearch.
2 | """
3 |
4 | from itertools import chain
5 | from pyramid.httpexceptions import HTTPForbidden
6 | from pyramid.view import view_config
7 | from .interfaces import ICachedItem
8 |
9 |
10 | def includeme(config):
11 | config.scan(__name__)
12 |
13 |
14 | @view_config(context=ICachedItem, request_method='GET', name='embedded')
15 | def cached_view_embedded(context, request):
16 | source = context.model.source
17 | allowed = set(source['principals_allowed']['view'])
18 | if allowed.isdisjoint(request.effective_principals):
19 | raise HTTPForbidden()
20 | return source['embedded']
21 |
22 |
23 | @view_config(context=ICachedItem, request_method='GET', name='object')
24 | def cached_view_object(context, request):
25 | source = context.model.source
26 | allowed = set(source['principals_allowed']['view'])
27 | if allowed.isdisjoint(request.effective_principals):
28 | raise HTTPForbidden()
29 | return source['object']
30 |
31 |
32 | @view_config(context=ICachedItem, request_method='GET', name='audit')
33 | def cached_view_audit(context, request):
34 | source = context.model.source
35 | allowed = set(source['principals_allowed']['audit'])
36 | if allowed.isdisjoint(request.effective_principals):
37 | raise HTTPForbidden()
38 | return {
39 | '@id': source['object']['@id'],
40 | 'audit': source['audit'],
41 | }
42 |
43 |
44 | @view_config(context=ICachedItem, request_method='GET', name='audit-self')
45 | def cached_view_audit_self(context, request):
46 | source = context.model.source
47 | allowed = set(source['principals_allowed']['audit'])
48 | if allowed.isdisjoint(request.effective_principals):
49 | raise HTTPForbidden()
50 | path = source['object']['@id']
51 | return {
52 | '@id': path,
53 | 'audit': [a for a in chain(*source['audit'].values()) if a['path'] == path],
54 | }
55 |
--------------------------------------------------------------------------------
/src/snovault/cache.py:
--------------------------------------------------------------------------------
1 | from pyramid.threadlocal import manager
2 | from sqlalchemy.util import LRUCache
3 | import transaction.interfaces
4 | from zope.interface import implementer
5 |
6 |
7 | @implementer(transaction.interfaces.ISynchronizer)
8 | class ManagerLRUCache(object):
9 | """ Override capacity in settings.
10 | """
11 | def __init__(self, name, default_capacity=100, threshold=.5):
12 | self.name = name
13 | self.default_capacity = default_capacity
14 | self.threshold = threshold
15 | transaction.manager.registerSynch(self)
16 |
17 | @property
18 | def cache(self):
19 | if not manager.stack:
20 | return None
21 | threadlocals = manager.stack[0]
22 | if self.name not in threadlocals:
23 | registry = threadlocals['registry']
24 | capacity = int(registry.settings.get(self.name + '.capacity', self.default_capacity))
25 | threadlocals[self.name] = LRUCache(capacity, self.threshold)
26 | return threadlocals[self.name]
27 |
28 | def get(self, key, default=None):
29 | cache = self.cache
30 | if cache is None:
31 | return default
32 | try:
33 | return cache[key]
34 | except KeyError:
35 | return default
36 |
37 | def __contains__(self, key):
38 | cache = self.cache
39 | if cache is None:
40 | return False
41 | return key in cache
42 |
43 | def __setitem__(self, key, value):
44 | cache = self.cache
45 | if cache is None:
46 | return
47 | self.cache[key] = value
48 |
49 | # ISynchronizer
50 |
51 | def beforeCompletion(self, transaction):
52 | pass
53 |
54 | def afterCompletion(self, transaction):
55 | # Ensure cache is cleared for retried transactions
56 | if manager.stack:
57 | threadlocals = manager.stack[0]
58 | threadlocals.pop(self.name, None)
59 |
60 | def newTransaction(self, transaction):
61 | pass
62 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_progress-bars.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Progress bars
3 | // --------------------------------------------------
4 |
5 |
6 | // Bar animations
7 | // -------------------------
8 |
9 | // WebKit
10 | @-webkit-keyframes progress-bar-stripes {
11 | from { background-position: 40px 0; }
12 | to { background-position: 0 0; }
13 | }
14 |
15 | // Spec and IE10+
16 | @keyframes progress-bar-stripes {
17 | from { background-position: 40px 0; }
18 | to { background-position: 0 0; }
19 | }
20 |
21 |
22 |
23 | // Bar itself
24 | // -------------------------
25 |
26 | // Outer container
27 | .progress {
28 | overflow: hidden;
29 | height: $line-height-computed;
30 | margin-bottom: $line-height-computed;
31 | background-color: $progress-bg;
32 | border-radius: $border-radius-base;
33 | @include box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
34 | }
35 |
36 | // Bar of progress
37 | .progress-bar {
38 | float: left;
39 | width: 0%;
40 | height: 100%;
41 | font-size: $font-size-small;
42 | line-height: $line-height-computed;
43 | color: $progress-bar-color;
44 | text-align: center;
45 | background-color: $progress-bar-bg;
46 | @include box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
47 | @include transition(width .6s ease);
48 | }
49 |
50 | // Striped bars
51 | .progress-striped .progress-bar {
52 | @include gradient-striped();
53 | background-size: 40px 40px;
54 | }
55 |
56 | // Call animation for the active one
57 | .progress.active .progress-bar {
58 | @include animation(progress-bar-stripes 2s linear infinite);
59 | }
60 |
61 |
62 |
63 | // Variations
64 | // -------------------------
65 |
66 | .progress-bar-success {
67 | @include progress-bar-variant($progress-bar-success-bg);
68 | }
69 |
70 | .progress-bar-info {
71 | @include progress-bar-variant($progress-bar-info-bg);
72 | }
73 |
74 | .progress-bar-warning {
75 | @include progress-bar-variant($progress-bar-warning-bg);
76 | }
77 |
78 | .progress-bar-danger {
79 | @include progress-bar-variant($progress-bar-danger-bg);
80 | }
81 |
--------------------------------------------------------------------------------
/src/snovault/commands/spreadsheet_to_json.py:
--------------------------------------------------------------------------------
1 | """
2 | Example:
3 |
4 | %(prog)s *.tsv
5 |
6 | """
7 |
8 | from .. import loadxl
9 | import json
10 | import os.path
11 |
12 | EPILOG = __doc__
13 |
14 |
15 | def rename_test_with_underscore(rows):
16 | for row in rows:
17 | if 'test' in row:
18 | if row['test'] != 'test':
19 | row['_test'] = row['test']
20 | del row['test']
21 | yield row
22 |
23 |
24 | def remove_empty(rows):
25 | for row in rows:
26 | if row:
27 | yield row
28 |
29 |
30 | def convert(filename, sheetname=None, outputdir=None, skip_blanks=False):
31 | if outputdir is None:
32 | outputdir = os.path.dirname(filename)
33 | source = loadxl.read_single_sheet(filename, sheetname)
34 | pipeline = [
35 | loadxl.remove_keys_with_empty_value if skip_blanks else loadxl.noop,
36 | rename_test_with_underscore,
37 | remove_empty,
38 | ]
39 | data = list(loadxl.combine(source, pipeline))
40 | if sheetname is None:
41 | sheetname, ext = os.path.splitext(os.path.basename(filename))
42 | out = open(os.path.join(outputdir, sheetname + '.json'), 'w')
43 | json.dump(data, out, sort_keys=True, indent=4, separators=(',', ': '))
44 |
45 |
46 |
47 | def main():
48 | import argparse
49 | parser = argparse.ArgumentParser(
50 | description="Convert spreadsheet to json list", epilog=EPILOG,
51 | formatter_class=argparse.RawDescriptionHelpFormatter,
52 | )
53 | parser.add_argument('filenames', metavar='FILE', nargs='+', help="Files to convert")
54 | parser.add_argument('--outputdir', help="Directory to write converted output")
55 | parser.add_argument('--sheetname', help="xlsx sheet name")
56 | parser.add_argument('--skip-blanks', help="Skip blank columns")
57 | args = parser.parse_args()
58 |
59 | for filename in args.filenames:
60 | convert(filename, args.sheetname, args.outputdir, args.skip_blanks)
61 |
62 |
63 | if __name__ == '__main__':
64 | main()
65 |
--------------------------------------------------------------------------------
/src/snovault/snowflake_hash.py:
--------------------------------------------------------------------------------
1 | from base64 import (
2 | b64decode,
3 | b64encode,
4 | )
5 | from hashlib import sha384
6 | from passlib.registry import register_crypt_handler
7 | from passlib.utils import handlers as uh
8 |
9 |
10 | def includeme(config):
11 | register_crypt_handler(SNOWHash)
12 |
13 |
14 | class SNOWHash(uh.StaticHandler):
15 | """ a special snowflake of a password hashing scheme
16 |
17 | Cryptographic strength of the hashing function is less of a concern for
18 | randomly generated passwords.
19 | """
20 | name = 'snowflake_hash'
21 | checksum_chars = uh.PADDED_BASE64_CHARS
22 | checksum_size = 64
23 |
24 | setting_kwds = ('salt_before', 'salt_after', 'salt_base')
25 | salt_before = b"186ED79BAEXzeusdioIsdklnw88e86cd73"
26 | salt_after = b"<*#$*(#)!DSDFOUIHLjksdf"
27 | salt_base = b64decode(b"""\
28 | Kf8r/S37L/kh9yP1JfMn8TnvO+096z/pMecz5TXjN+EJ3wvdDdsP2QHXA9UF0wfRGc8bzR3LH8kR
29 | xxPFFcMXwWm/a71tu2+5YbdjtWWzZ7F5r3utfat/qXGnc6V1o3ehSZ9LnU2bT5lBl0OVRZNHkVmP
30 | W41di1+JUYdThVWDV4Gpf6t9rXuveaF3o3Wlc6dxuW+7bb1rv2mxZ7NltWO3YYlfi12NW49ZgVeD
31 | VYVTh1GZT5tNnUufSZFHk0WVQ5dB6T/rPe077znhN+M15TPnMfkv+y39K/8p8SfzJfUj9yHJH8sd
32 | zRvPGcEXwxXFE8cR2Q/bDd0L3wnRB9MF1QPXASn/K/0t+y/5Ifcj9SXzJ/E57zvtPes/6THnM+U1
33 | 4zfhCd8L3Q3bD9kB1wPVBdMH0RnPG80dyx/JEccTxRXDF8Fpv2u9bbtvuWG3Y7Vls2exea97rX2r
34 | f6lxp3OldaN3oUmfS51Nm0+ZQZdDlUWTR5FZj1uNXYtfiVGHU4VVg1eBqX+rfa17r3mhd6N1pXOn
35 | cblvu229a79psWezZbVjt2GJX4tdjVuPWYFXg1WFU4dRmU+bTZ1Ln0mRR5NFlUOXQek/6z3tO+85
36 | 4TfjNeUz5zH5L/st/Sv/KfEn8yX1I/chyR/LHc0bzxnBF8MVxRPHEdkP2w3dC98J0QfTBdUD1wE=
37 | """)
38 |
39 | def _calc_checksum(self, secret):
40 | if not isinstance(secret, bytes):
41 | secret = secret.encode('utf-8')
42 | salted = self.salt_before + secret + self.salt_after + b'\0'
43 | if len(salted) > len(self.salt_base):
44 | raise ValueError("Password too long")
45 | salted += self.salt_base[len(salted):]
46 | chk = sha384(salted).digest()
47 | return b64encode(chk).decode("ascii")
48 |
--------------------------------------------------------------------------------
/src/snowflakes/commands/spreadsheet_to_json.py:
--------------------------------------------------------------------------------
1 | """
2 | Example:
3 |
4 | %(prog)s *.tsv
5 |
6 | """
7 |
8 | from .. import loadxl
9 | import json
10 | import os.path
11 |
12 | EPILOG = __doc__
13 |
14 |
15 | def rename_test_with_underscore(rows):
16 | for row in rows:
17 | if 'test' in row:
18 | if row['test'] != 'test':
19 | row['_test'] = row['test']
20 | del row['test']
21 | yield row
22 |
23 |
24 | def remove_empty(rows):
25 | for row in rows:
26 | if row:
27 | yield row
28 |
29 |
30 | def convert(filename, sheetname=None, outputdir=None, skip_blanks=False):
31 | if outputdir is None:
32 | outputdir = os.path.dirname(filename)
33 | source = loadxl.read_single_sheet(filename, sheetname)
34 | pipeline = [
35 | loadxl.remove_keys_with_empty_value if skip_blanks else loadxl.noop,
36 | rename_test_with_underscore,
37 | remove_empty,
38 | ]
39 | data = list(loadxl.combine(source, pipeline))
40 | if sheetname is None:
41 | sheetname, ext = os.path.splitext(os.path.basename(filename))
42 | out = open(os.path.join(outputdir, sheetname + '.json'), 'w')
43 | json.dump(data, out, sort_keys=True, indent=4, separators=(',', ': '))
44 |
45 |
46 |
47 | def main():
48 | import argparse
49 | parser = argparse.ArgumentParser(
50 | description="Convert spreadsheet to json list", epilog=EPILOG,
51 | formatter_class=argparse.RawDescriptionHelpFormatter,
52 | )
53 | parser.add_argument('filenames', metavar='FILE', nargs='+', help="Files to convert")
54 | parser.add_argument('--outputdir', help="Directory to write converted output")
55 | parser.add_argument('--sheetname', help="xlsx sheet name")
56 | parser.add_argument('--skip-blanks', help="Skip blank columns")
57 | args = parser.parse_args()
58 |
59 | for filename in args.filenames:
60 | convert(filename, args.sheetname, args.outputdir, args.skip_blanks)
61 |
62 |
63 | if __name__ == '__main__':
64 | main()
65 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/features/forms.feature:
--------------------------------------------------------------------------------
1 | @forms
2 | Feature: Edit forms
3 |
4 | Scenario: Save a change to a snowflake
5 | When I visit "/snowflakes/SNOFL000LSP/"
6 | And I wait for the content to load
7 | And I click the element with the css selector ".icon-gear"
8 | And I click the link to "/snowflakes/SNOFL000LSP/#!edit"
9 | And I wait for an element with the css selector "form.rf-Form" to load
10 | And I select "slushy" from "type"
11 | And I press "Save"
12 | And I wait for the form to close
13 | And I wait for an element with the css selector ".view-item.type-Snowflake" to load
14 | Then I should see "slushy"
15 |
16 | Scenario: Leaving a dirty form without saving asks for confirmation
17 | When I visit "/snowflakes/SNOFL001MXE/#!edit"
18 | And I wait for an element with the css selector "form.rf-Form" to load
19 | And I select "slushy" from "type"
20 | And I click the link with text "SNOWFLAKES"
21 | And I dismiss the alert
22 | Then field "type" should have the value "slushy"
23 | # Make sure we don't leave a dirty form that will interfere with subsequent tests
24 | When I click the link with text "SNOWFLAKES"
25 | And I accept the alert
26 |
27 | # Scenario: Validation errors are shown in context
28 | # When I visit "/snowflakes/SNOFL001MYM/#!edit"
29 | # And I wait for an element with the css selector "form.rf-Form" to load
30 | # And I fill in "date_created" with "bogus"
31 | # And I press "Save"
32 | # And I wait for an element with the css selector "input[name=date_created] + .rf-Message" to load
33 | # Then I should see "'bogus' is not valid under any of the given schemas" within 2 seconds
34 | # # Make sure we don't leave a dirty form that will interfere with subsequent tests
35 | # When I click the link with text "SNOWFLAKES"
36 | # And I accept the alert
37 |
38 | # To add:
39 | # - interacting with the object picker
40 | # - clicking links in the form opens in new window
41 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/schema.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var globals = require('./globals');
4 | var fetched = require('./fetched');
5 |
6 |
7 | var Markdown = module.exports.Markdown = React.createClass({
8 | render: function() {
9 | var marked = require('marked');
10 | var html = marked(this.props.source, {sanitize: true});
11 | return
;
12 | }
13 | });
14 |
15 |
16 | var ChangeLog = module.exports.ChangeLog = React.createClass({
17 | render: function() {
18 | return (
19 |
24 | );
25 | }
26 | });
27 |
28 | var SchemaPage = module.exports.SchemaPage = React.createClass({
29 | render: function() {
30 | var context = this.props.context;
31 | var itemClass = globals.itemClass(context);
32 | var title = context['title'];
33 | var changelog = context['changelog'];
34 | return (
35 |
36 |
37 |
38 |
{title}
39 |
40 |
41 | {typeof context.description == "string" ?
{context.description}
: null}
42 |
43 |
44 |
{JSON.stringify(context, null, 4)}
45 |
46 |
47 | {changelog &&
48 |
49 |
50 | }
51 |
52 | );
53 | }
54 | });
55 | globals.content_views.register(SchemaPage, 'JSONSchema');
56 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_tooltip.scss:
--------------------------------------------------------------------------------
1 | $tooltip-width: 300px;
2 | $tooltip-color: desaturate(lighten(#0a253d,30%),30%);
3 |
4 |
5 | // Generic tooltips with clickable triggers
6 | .tooltip-trigger {
7 | display: inline-block;
8 | position: relative;
9 | cursor: default;
10 | }
11 |
12 | .tooltip {
13 | position: absolute;
14 | left: 50%;
15 | margin-right: -$tooltip-width;
16 | visibility: hidden;
17 | transform: translateX(-50%);
18 | white-space: normal;
19 | text-align: center;
20 | opacity: 1;
21 | z-index: 100;
22 |
23 | .no-csstransforms & {
24 | left: 0;
25 | padding: 0;
26 | }
27 |
28 | &.tooltip-open {
29 | visibility: visible;
30 | }
31 | }
32 |
33 | .tooltip-inner {
34 | padding: 2px 5px;
35 | border-right: 1px solid #fff;
36 | border-bottom: 1px solid #fff;
37 | border-left: 1px solid #fff;
38 | background-color: $tooltip-color;
39 | @include box-shadow(0 2px 4px 0 rgba(0,0,0,0.3));
40 | max-width: $tooltip-width;
41 | cursor: auto;
42 | font-family: $font-family-sans-serif;
43 | }
44 |
45 | .tooltip.bottom .tooltip-arrow {
46 | border-bottom-color: $tooltip-color;
47 | }
48 |
49 |
50 | // Tooltips specifically for antibody statuses
51 | .tooltip-status {
52 | position: absolute;
53 | visibility: hidden;
54 | top: 20px;
55 | left: 15px;
56 | padding: 2px 5px;
57 | z-index: 100;
58 | border: 1px solid #fff;
59 | background-color: $tooltip-color;
60 | @include box-shadow(0 2px 4px 0 rgba(0,0,0,0.3));
61 | color: #fff;
62 | text-align: center;
63 | white-space: nowrap;
64 | @include font-size(0.9);
65 | @include border-radius(4px);
66 |
67 | span:last-child {
68 | font-weight: normal;
69 | }
70 |
71 | &.tooltipopen {
72 | visibility: visible;
73 | }
74 | }
75 |
76 | .tooltip-status-trigger {
77 | position: relative;
78 | margin-left: 5px;
79 | @include font-size(1.2);
80 | cursor: default;
81 | }
82 |
83 | .no-csstransforms .tooltip-arrow {
84 | display: none;
85 | }
86 |
--------------------------------------------------------------------------------
/src/snovault/tests/test_link.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture(autouse=True)
5 | def autouse_external_tx(external_tx):
6 | pass
7 |
8 |
9 | def test_links_add(targets, sources, posted_targets_and_sources, session):
10 | from snovault.storage import Link
11 | links = sorted([
12 | (str(link.source_rid), link.rel, str(link.target_rid))
13 | for link in session.query(Link).all()
14 | ])
15 | expected = sorted([
16 | (sources[0]['uuid'], u'target', targets[0]['uuid']),
17 | (sources[1]['uuid'], u'target', targets[1]['uuid']),
18 | ])
19 | assert links == expected
20 |
21 |
22 | def test_links_update(targets, sources, posted_targets_and_sources, testapp, session):
23 | from snovault.storage import Link
24 |
25 | url = '/testing-link-sources/' + sources[1]['uuid']
26 | new_item = {'name': 'B updated', 'target': targets[0]['name']}
27 | testapp.put_json(url, new_item, status=200)
28 |
29 | links = sorted([
30 | (str(link.source_rid), link.rel, str(link.target_rid))
31 | for link in session.query(Link).all()
32 | ])
33 | expected = sorted([
34 | (sources[0]['uuid'], u'target', targets[0]['uuid']),
35 | (sources[1]['uuid'], u'target', targets[0]['uuid']),
36 | ])
37 | assert links == expected
38 |
39 |
40 | def test_links_reverse(targets, sources, posted_targets_and_sources, testapp, session):
41 | target = targets[0]
42 | res = testapp.get('/testing-link-targets/%s/?frame=object' % target['name'])
43 | assert res.json['reverse'] == ['/testing-link-sources/%s/' % sources[0]['uuid']]
44 |
45 | # DELETED sources are hidden from the list.
46 | target = targets[1]
47 | res = testapp.get('/testing-link-targets/%s/' % target['name'])
48 | assert res.json['reverse'] == []
49 |
50 |
51 | def test_links_quoted_ids(posted_targets_and_sources, testapp, session):
52 | res = testapp.get('/testing-link-targets/quote:name/?frame=object')
53 | target = res.json
54 | source = {'name': 'C', 'target': target['@id']}
55 | testapp.post_json('/testing-link-sources/', source, status=201)
56 |
--------------------------------------------------------------------------------
/src/snowflakes/typedsheets.py:
--------------------------------------------------------------------------------
1 | from pyramid.settings import asbool
2 |
3 |
4 | def parse_array(types, value):
5 | return [cast(types, v) for v in value.split(';') if v.strip()]
6 |
7 |
8 | def parse_object(types, value):
9 | items = (part.split(':', 1) for part in value.split(',') if value.strip())
10 | return {k.strip(): cast(types, v) for k, v in items}
11 |
12 |
13 | def parse_string(types, value):
14 | assert not types
15 | return value
16 |
17 |
18 | def parse_ignore(types, value):
19 | return None
20 |
21 |
22 | def parse_number(types, value):
23 | assert not types
24 | try:
25 | return int(value)
26 | except ValueError:
27 | return float(value)
28 |
29 |
30 | def parse_integer(types, value):
31 | assert not types
32 | return int(value)
33 |
34 |
35 | def parse_boolean(types, value):
36 | assert not types
37 | return asbool(value)
38 |
39 |
40 | TYPE_BY_NAME = {
41 | 'string': parse_string,
42 | 'number': parse_number,
43 | 'boolean': parse_boolean,
44 | 'integer': parse_integer,
45 | 'ignore': parse_ignore,
46 | 'array': parse_array,
47 | 'object': parse_object,
48 | }
49 |
50 |
51 | def cast(types, value):
52 | types = list(types) or ['string']
53 | type_name = types.pop()
54 | value = value.strip()
55 | if value.lower() == 'null':
56 | return None
57 | if value == '' and type_name != 'string':
58 | return None
59 | parse = TYPE_BY_NAME[type_name]
60 | return parse(types, value)
61 |
62 |
63 | def convert(name, value):
64 | """ fieldname:[:...], value -> fieldname, cast(value)
65 | """
66 | parts = name.split(':')
67 | return parts[0], cast(parts[1:], value)
68 |
69 |
70 | def cast_row_values(dictrows):
71 | """ Wrapper generator for typing csv.DictReader rows
72 | """
73 | for row in dictrows:
74 | yield dict(convert(name, value or '') for name, value in row.items())
75 |
76 |
77 | def remove_nulls(dictrows):
78 | for row in dictrows:
79 | yield {
80 | name: value for name, value in row.items()
81 | if value is not None and name
82 | }
83 |
--------------------------------------------------------------------------------
/src/snovault/local_storage.py:
--------------------------------------------------------------------------------
1 | import binascii
2 |
3 | from os import urandom
4 | from datetime import datetime
5 | from pytz import timezone
6 |
7 | from redis import StrictRedis
8 |
9 |
10 | def base_result(local_store):
11 | local_dt = datetime.now(timezone(local_store.local_tz))
12 | return {
13 | '@type': ['result'],
14 | 'utc_now': str(datetime.utcnow()),
15 | 'lcl_now': f"{local_store.local_tz}: {local_dt}",
16 | }
17 |
18 |
19 | class LocalStoreClient():
20 | '''
21 | Light redis wrapper and redis examples
22 | - get_tag function was added to return hex str
23 | - Can access client directly for full functionality
24 | '''
25 | def __init__(self, **kwargs):
26 | self.local_tz = kwargs.get('local_tz', 'GMT')
27 | self.client = StrictRedis(
28 | encoding='utf-8',
29 | decode_responses=True,
30 | db=kwargs['db_index'],
31 | host=kwargs['host'],
32 | port=kwargs['port'],
33 | socket_timeout=kwargs['socket_timeout'],
34 | )
35 |
36 | @staticmethod
37 | def get_tag(tag, num_bytes=2):
38 | '''
39 | Tags are the tag plus a random hex bytes string
40 | - Bytes string length is 2 * num bytes
41 | '''
42 | rand_hex_str = binascii.b2a_hex(urandom(num_bytes)).decode('utf-8')
43 | return f"{tag}:{rand_hex_str}"
44 |
45 | def ping(self):
46 | return self.client.ping()
47 |
48 | def dict_get(self, key):
49 | return self.client.hgetall(key)
50 |
51 | def dict_set(self, key, hash_dict):
52 | for k, v in hash_dict.items():
53 | self.client.hset(name=key, key=k, value=v)
54 |
55 | def get_tag_keys(self, tag):
56 | return self.client.keys(f"{tag}:*")
57 |
58 | def item_get(self, key):
59 | return self.client.get(key)
60 |
61 | def item_set(self, key, item):
62 | return self.client.set(key, item)
63 |
64 | def list_add(self, key, item):
65 | return self.client.lpush(key, item)
66 |
67 | def list_get(self, key, start=0, stop=-1):
68 | return self.client.lrange(key, start, stop)
69 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_print.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Basic print styles
3 | // --------------------------------------------------
4 | // Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css
5 |
6 | @media print {
7 |
8 | * {
9 | text-shadow: none !important;
10 | color: #000 !important; // Black prints faster: h5bp.com/s
11 | background: transparent !important;
12 | box-shadow: none !important;
13 | }
14 |
15 | a,
16 | a:visited {
17 | text-decoration: underline;
18 | }
19 |
20 | a[href]:after {
21 | content: " (" attr(href) ")";
22 | }
23 |
24 | abbr[title]:after {
25 | content: " (" attr(title) ")";
26 | }
27 |
28 | // Don't show links for images, or javascript/internal links
29 | a[href^="javascript:"]:after,
30 | a[href^="#"]:after {
31 | content: "";
32 | }
33 |
34 | pre,
35 | blockquote {
36 | border: 1px solid #999;
37 | page-break-inside: avoid;
38 | }
39 |
40 | thead {
41 | display: table-header-group; // h5bp.com/t
42 | }
43 |
44 | tr,
45 | img {
46 | page-break-inside: avoid;
47 | }
48 |
49 | img {
50 | max-width: 100% !important;
51 | }
52 |
53 | p,
54 | h2,
55 | h3 {
56 | orphans: 3;
57 | widows: 3;
58 | }
59 |
60 | h2,
61 | h3 {
62 | page-break-after: avoid;
63 | }
64 |
65 | // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245
66 | // Once fixed, we can just straight up remove this.
67 | select {
68 | background: #fff !important;
69 | }
70 |
71 | // Bootstrap components
72 | .navbar {
73 | display: none;
74 | }
75 | .table {
76 | td,
77 | th {
78 | background-color: #fff !important;
79 | }
80 | }
81 | .btn,
82 | .dropup > .btn {
83 | > .caret {
84 | border-top-color: #000 !important;
85 | }
86 | }
87 | .label {
88 | border: 1px solid #000;
89 | }
90 |
91 | .table {
92 | border-collapse: collapse !important;
93 | }
94 | .table-bordered {
95 | th,
96 | td {
97 | border: 1px solid #ddd !important;
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/snowflakes/static/libs/svg-icons.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 |
4 | var icons = {
5 | table: table-tab-icon ,
6 | matrix: matrix-icon ,
7 | search: search-icon
8 | };
9 |
10 |
11 | var SvgIcon = function(icon) {
12 | return icons[icon];
13 | };
14 |
15 | module.exports = SvgIcon;
16 |
--------------------------------------------------------------------------------
/development.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
4 | ###
5 |
6 | [app:app]
7 | use = config:base.ini#app
8 | sqlalchemy.url = postgresql://postgres@:5432/postgres?host=/tmp/snovault/pgdata
9 | snp_search.server = localhost:9200
10 | load_test_only = true
11 | local_tz = US/Pacific
12 | create_tables = true
13 | testing = true
14 | postgresql.statement_timeout = 20
15 | indexer.processes =
16 |
17 | pyramid.reload_templates = true
18 | pyramid.debug_authorization = false
19 | pyramid.debug_notfound = true
20 | pyramid.debug_routematch = false
21 | pyramid.default_locale_name = en
22 |
23 | snovault.load_test_data = snowflakes.loadxl:load_test_data
24 | # Local Storage: Settings must exist in...
25 | # snovault/tests/[testappsettings.py, test_key.py]
26 | # snowflakes/tests/conftest.py
27 | local_storage_host = localhost
28 | local_storage_port = 6378
29 | local_storage_redis_index = 1
30 | local_storage_timeout = 5
31 |
32 | [pipeline:debug]
33 | pipeline =
34 | egg:PasteDeploy#prefix
35 | egg:repoze.debug#pdbpm
36 | app
37 | set pyramid.includes =
38 | pyramid_translogger
39 |
40 | [composite:main]
41 | use = egg:rutter#urlmap
42 | / = debug
43 | /_indexer = indexer
44 |
45 | [composite:indexer]
46 | use = config:base.ini#indexer
47 |
48 | [composite:regionindexer]
49 | use = config:base.ini#regionindexer
50 |
51 | ###
52 | # wsgi server configuration
53 | ###
54 |
55 | [server:main]
56 | use = egg:waitress#main
57 | host = 0.0.0.0
58 | port = 6543
59 | threads = 1
60 |
61 | ###
62 | # logging configuration
63 | # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
64 | ###
65 |
66 | [loggers]
67 | keys = root, wsgi, snovault
68 |
69 | [handlers]
70 | keys = console
71 |
72 | [formatters]
73 | keys = generic
74 |
75 | [logger_root]
76 | level = INFO
77 | handlers = console
78 |
79 | [logger_wsgi]
80 | level = DEBUG
81 | handlers =
82 | qualname = wsgi
83 |
84 | [logger_snovault]
85 | level = DEBUG
86 | handlers =
87 | qualname = snovault
88 |
89 | [handler_console]
90 | class = StreamHandler
91 | args = (sys.stderr,)
92 | level = NOTSET
93 | formatter = generic
94 |
95 | [formatter_generic]
96 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
97 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/snowflakes/modules/_layout-editor.scss:
--------------------------------------------------------------------------------
1 | .block {
2 | padding: .5em;
3 | }
4 |
5 | .layout.editable {
6 | position: relative;
7 | .row {
8 | position: relative;
9 | margin-top: -5px;
10 | margin-bottom: -5px;
11 | padding-top: 5px;
12 | padding-bottom: 5px;
13 | outline: dashed 1px red;
14 | }
15 | [class^="col"] {
16 | outline: dashed 1px blue;
17 | }
18 | .block {
19 | position: relative;
20 | min-height: 20px;
21 |
22 | &.dragging {
23 | opacity: .5;
24 | }
25 |
26 | &.hover {
27 | outline: dashed 1px $brand-primary;
28 | &[draggable="true"] {
29 | cursor: move;
30 | }
31 | .block-toolbar {
32 | display: block;
33 | }
34 | }
35 |
36 | .block-toolbar {
37 | position: absolute;
38 | z-index: 1;
39 | top: 3px;
40 | right: 3px;
41 | text-align: right;
42 | display: none;
43 | a {
44 | text-decoration: none;
45 | cursor: pointer;
46 | }
47 | }
48 |
49 | }
50 |
51 | }
52 |
53 | .drop-top:before {
54 | content: " ";
55 | display: block !important;
56 | width: 100%;
57 | border-top: solid 3px $brand-primary;
58 | margin-top: -3px;
59 | position: absolute; top: 0; left: 0;
60 | }
61 | .drop-bottom:after {
62 | content: " ";
63 | display: block !important;
64 | width: 100%;
65 | border-bottom: solid 3px $brand-primary;
66 | margin-bottom: -3px;
67 | position: absolute; bottom: 0; left: 0;
68 | }
69 | .row.drop-bottom:after {
70 | display: table !important;
71 | position: static;
72 | }
73 | .drop-left:before {
74 | content: " ";
75 | display: block !important;
76 | position: absolute;
77 | top: 0;
78 | left: 0;
79 | height: 100%;
80 | border-left: solid 3px $brand-primary;
81 | }
82 | .drop-right:before {
83 | content: " ";
84 | display: block !important;
85 | position: absolute;
86 | top: 0;
87 | right: 0;
88 | height: 100%;
89 | border-right: solid 3px $brand-primary;
90 | }
91 |
92 | .layout-toolbar {
93 | z-index: 1030;
94 | .navbar-left .btn {
95 | cursor: move;
96 | }
97 | }
98 | .layout-toolbar.navbar-fixed-top {
99 | z-index: 1031;
100 | .col-md-12 {
101 | outline: 0;
102 | }
103 | }
104 |
105 | .modal-dialog {
106 | width: $modal-lg;
107 | }
108 |
--------------------------------------------------------------------------------
/src/snowflakes/authorization.py:
--------------------------------------------------------------------------------
1 | from snovault import COLLECTIONS
2 |
3 |
4 | def groupfinder(login, request):
5 | if '.' not in login:
6 | return None
7 | namespace, localname = login.split('.', 1)
8 | user = None
9 |
10 | collections = request.registry[COLLECTIONS]
11 |
12 | if namespace == 'remoteuser':
13 | if localname in ['EMBED', 'INDEXER']:
14 | return []
15 | elif localname in ['TEST', 'IMPORT', 'UPGRADE']:
16 | return ['group.admin']
17 | elif localname in ['TEST_SUBMITTER']:
18 | return ['group.submitter']
19 | elif localname in ['TEST_AUTHENTICATED']:
20 | return ['viewing_group.ENCODE']
21 |
22 | if namespace in ('mailto', 'remoteuser', 'webuser'):
23 | users = collections.by_item_type['user']
24 | try:
25 | user = users[localname]
26 | except KeyError:
27 | return None
28 |
29 | elif namespace == 'accesskey':
30 | access_keys = collections.by_item_type['access_key']
31 | try:
32 | access_key = access_keys[localname]
33 | except KeyError:
34 | return None
35 |
36 | if access_key.properties.get('status') in ('deleted', 'disabled'):
37 | return None
38 |
39 | userid = access_key.properties['user']
40 | user = collections.by_item_type['user'][userid]
41 |
42 | if user is None:
43 | return None
44 |
45 | user_properties = user.properties
46 |
47 | if user_properties.get('status') in ('deleted', 'disabled'):
48 | return None
49 |
50 | principals = ['userid.%s' % user.uuid]
51 | lab = user_properties.get('lab')
52 | if lab:
53 | principals.append('lab.%s' % lab)
54 | submits_for = user_properties.get('submits_for', [])
55 | principals.extend('lab.%s' % lab_uuid for lab_uuid in submits_for)
56 | principals.extend('submits_for.%s' % lab_uuid for lab_uuid in submits_for)
57 | if submits_for:
58 | principals.append('group.submitter')
59 | groups = user_properties.get('groups', [])
60 | principals.extend('group.%s' % group for group in groups)
61 | viewing_groups = user_properties.get('viewing_groups', [])
62 | principals.extend('viewing_group.%s' % group for group in viewing_groups)
63 | return principals
64 |
--------------------------------------------------------------------------------
/src/snowflakes/static/components/inputs/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 |
4 |
5 | var FileInput = module.exports.FileInput = React.createClass({
6 |
7 | getInitialState: function() {
8 | return {
9 | value: this.props.value || {},
10 | };
11 | },
12 |
13 | componentWillReceiveProps: function(nextProps) {
14 | this.setState({value: nextProps.value});
15 | },
16 |
17 | render: function() {
18 | var mimetype = this.state.value.type;
19 | var preview = (mimetype && mimetype.indexOf('image/') === 0) ? : '';
20 | var filename = this.state.value.download;
21 | return (
22 |
23 |
24 | {filename ?
: ''}
27 |
{preview}
28 |
Drop a {filename ? 'replacement' : ''} file here.
29 | Or
30 |
31 |
32 |
33 | );
34 | },
35 |
36 | onDragOver: function(e) {
37 | e.dataTransfer.dropEffect = 'copy';
38 | e.preventDefault(); // indicate we are going to handle the drop
39 | },
40 |
41 | onDrop: function(e) {
42 | var file = e.dataTransfer.files[0];
43 | this.onChange(null, file);
44 | e.preventDefault();
45 | },
46 |
47 | onChange: function(e, file) {
48 | if (file === undefined) {
49 | var input = this.refs.input.getDOMNode();
50 | file = input.files[0];
51 | }
52 | var reader = new FileReader();
53 | reader.onloadend = function() {
54 | var value = {
55 | download: file.name,
56 | type: file.type || undefined,
57 | href: reader.result
58 | };
59 | this.props.onChange(value);
60 | }.bind(this);
61 | if (file) {
62 | reader.readAsDataURL(file);
63 | }
64 | }
65 | });
66 |
67 | module.exports = FileInput;
68 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_responsive-utilities.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // IE10 in Windows (Phone) 8
7 | //
8 | // Support for responsive views via media queries is kind of borked in IE10, for
9 | // Surface/desktop in split view and for Windows Phone 8. This particular fix
10 | // must be accompanied by a snippet of JavaScript to sniff the user agent and
11 | // apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at
12 | // our Getting Started page for more information on this bug.
13 | //
14 | // For more information, see the following:
15 | //
16 | // Issue: https://github.com/twbs/bootstrap/issues/10497
17 | // Docs: http://getbootstrap.com/getting-started/#browsers
18 | // Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
19 |
20 | @-ms-viewport {
21 | width: device-width;
22 | }
23 |
24 |
25 | // Visibility utilities
26 |
27 | @include responsive-invisibility('.visible-xs, .visible-sm, .visible-md, .visible-lg');
28 |
29 | @media (max-width: $screen-xs-max) {
30 | @include responsive-visibility('.visible-xs');
31 | }
32 |
33 | @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
34 | @include responsive-visibility('.visible-sm');
35 | }
36 |
37 | @media (min-width: $screen-md-min) and (max-width: $screen-md-max) {
38 | @include responsive-visibility('.visible-md');
39 | }
40 |
41 | @media (min-width: $screen-lg-min) {
42 | @include responsive-visibility('.visible-lg');
43 | }
44 |
45 | @media (max-width: $screen-xs-max) {
46 | @include responsive-invisibility('.hidden-xs');
47 | }
48 |
49 | @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
50 | @include responsive-invisibility('.hidden-sm');
51 | }
52 |
53 | @media (min-width: $screen-md-min) and (max-width: $screen-md-max) {
54 | @include responsive-invisibility('.hidden-md');
55 | }
56 |
57 | @media (min-width: $screen-lg-min) {
58 | @include responsive-invisibility('.hidden-lg');
59 | }
60 |
61 |
62 | // Print utilities
63 | //
64 | // Media queries are placed on the inside to be mixin-friendly.
65 |
66 | @include responsive-invisibility('.visible-print');
67 |
68 | @media print {
69 | @include responsive-visibility('.visible-print');
70 | }
71 |
72 | @media print {
73 | @include responsive-invisibility('.hidden-print');
74 | }
75 |
--------------------------------------------------------------------------------
/src/snowflakes/static/scss/bootstrap/_pagination.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Pagination (multiple pages)
3 | // --------------------------------------------------
4 | .pagination {
5 | display: inline-block;
6 | padding-left: 0;
7 | margin: $line-height-computed 0;
8 | border-radius: $border-radius-base;
9 |
10 | > li {
11 | display: inline; // Remove list-style and block-level defaults
12 | > a,
13 | > span {
14 | position: relative;
15 | float: left; // Collapse white-space
16 | padding: $padding-base-vertical $padding-base-horizontal;
17 | line-height: $line-height-base;
18 | text-decoration: none;
19 | color: $pagination-color;
20 | background-color: $pagination-bg;
21 | border: 1px solid $pagination-border;
22 | margin-left: -1px;
23 | }
24 | &:first-child {
25 | > a,
26 | > span {
27 | margin-left: 0;
28 | @include border-left-radius($border-radius-base);
29 | }
30 | }
31 | &:last-child {
32 | > a,
33 | > span {
34 | @include border-right-radius($border-radius-base);
35 | }
36 | }
37 | }
38 |
39 | > li > a,
40 | > li > span {
41 | &:hover,
42 | &:focus {
43 | color: $pagination-hover-color;
44 | background-color: $pagination-hover-bg;
45 | border-color: $pagination-hover-border;
46 | }
47 | }
48 |
49 | > .active > a,
50 | > .active > span {
51 | &,
52 | &:hover,
53 | &:focus {
54 | z-index: 2;
55 | color: $pagination-active-color;
56 | background-color: $pagination-active-bg;
57 | border-color: $pagination-active-border;
58 | cursor: default;
59 | }
60 | }
61 |
62 | > .disabled {
63 | > span,
64 | > span:hover,
65 | > span:focus,
66 | > a,
67 | > a:hover,
68 | > a:focus {
69 | color: $pagination-disabled-color;
70 | background-color: $pagination-disabled-bg;
71 | border-color: $pagination-disabled-border;
72 | cursor: not-allowed;
73 | }
74 | }
75 | }
76 |
77 | // Sizing
78 | // --------------------------------------------------
79 |
80 | // Large
81 | .pagination-lg {
82 | @include pagination-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $border-radius-large);
83 | }
84 |
85 | // Small
86 | .pagination-sm {
87 | @include pagination-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $border-radius-small);
88 | }
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "snowflakes",
3 | "version": "0.0.0",
4 | "description": "domready held back.",
5 | "scripts": {
6 | "test": "jest",
7 | "build": "gulp build",
8 | "dev": "gulp dev"
9 | },
10 | "author": "",
11 | "license": "MIT",
12 | "files": [],
13 | "repository": "ENCODE-DCC/encoded",
14 | "jest": {
15 | "rootDir": "src/snowflakes/static",
16 | "setupFiles": [
17 | "../../../jest/environment.js"
18 | ],
19 | "unmockedModulePathPatterns": [
20 | "node_modules/react",
21 | "node_modules/underscore",
22 | "libs/react-patches",
23 | "jsdom"
24 | ]
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.6.0",
28 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
29 | "@babel/plugin-transform-modules-commonjs": "^7.6.0",
30 | "@babel/plugin-transform-react-display-name": "^7.0.0",
31 | "@babel/plugin-transform-react-jsx": "^7.0.0",
32 | "@babel/polyfill": "^7.6.0",
33 | "@babel/preset-env": "^7.6.0",
34 | "@babel/preset-react": "^7.0.0",
35 | "@babel/register": "^7.6.0",
36 | "babel-jest": "^24.9.0",
37 | "babel-loader": "^8.0.6",
38 | "fancy-log": "^1.3.3",
39 | "gulp": "^4.0.2",
40 | "jest-cli": "^24.9.0",
41 | "jsdom": "^15.1.1",
42 | "json-loader": "^0.5.4",
43 | "string-replace-loader": "^2.2.0",
44 | "webpack": "^4.39.3"
45 | },
46 | "dependencies": {
47 | "@babel/preset-flow": "^7.0.0",
48 | "babel-polyfill": "^6.7.4",
49 | "brace": "^0.3.0",
50 | "ckeditor": "file:node_shims/ckeditor",
51 | "color": "^0.10.1",
52 | "d3": "^3.4.6",
53 | "dagre-d3": "git+https://github.com/ENCODE-DCC/dagre-d3.git",
54 | "domready": "^1.0.8",
55 | "form-serialize": "^0.7.2",
56 | "google-analytics": "file:node_shims/google-analytics",
57 | "immutable": "^3.7.5",
58 | "js-cookie": "^2.2.1",
59 | "marked": "^0.7.0",
60 | "moment": "^2.8.2",
61 | "query-string": "^4.1.0",
62 | "react": "^0.12.2",
63 | "react-bootstrap": "^0.15.1",
64 | "react-forms": "git+https://github.com/lrowe/react-forms.git#3953a633b1f77640dffb5e3f1d5bbe78a98c3dfe",
65 | "scriptjs": "^2.5.7",
66 | "source-map-support": "^0.5.13",
67 | "subprocess-middleware": "^0.1.0",
68 | "terser-webpack-plugin": "^2.0.1",
69 | "underscore": "^1.8.3",
70 | "whatwg-fetch": "git+https://github.com/lrowe/fetch.git#bf5d58b738fb2ed6d60791b944d36075fee8604a"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/snovault/validators.py:
--------------------------------------------------------------------------------
1 | from uuid import UUID
2 | from .schema_utils import validate_request
3 | from .validation import ValidationFailure
4 |
5 |
6 | # No-validation validators
7 |
8 |
9 | def no_validate_item_content_post(context, request):
10 | data = request.json
11 | request.validated.update(data)
12 |
13 |
14 | def no_validate_item_content_put(context, request):
15 | data = request.json
16 | if 'uuid' in data:
17 | if UUID(data['uuid']) != context.uuid:
18 | msg = 'uuid may not be changed'
19 | raise ValidationFailure('body', ['uuid'], msg)
20 | request.validated.update(data)
21 |
22 |
23 | def no_validate_item_content_patch(context, request):
24 | data = context.properties.copy()
25 | data.update(request.json)
26 | if 'uuid' in data:
27 | if UUID(data['uuid']) != context.uuid:
28 | msg = 'uuid may not be changed'
29 | raise ValidationFailure('body', ['uuid'], msg)
30 | request.validated.update(data)
31 |
32 |
33 | # Schema checking validators
34 |
35 |
36 | def validate_item_content_post(context, request):
37 | data = request.json
38 | validate_request(context.type_info.schema, request, data)
39 |
40 |
41 | def validate_item_content_put(context, request):
42 | data = request.json
43 | schema = context.type_info.schema
44 | if 'uuid' in data and UUID(data['uuid']) != context.uuid:
45 | msg = 'uuid may not be changed'
46 | raise ValidationFailure('body', ['uuid'], msg)
47 | accession = context.properties.get('accession')
48 | if accession and accession != data.get('accession'):
49 | msg = 'must specify original accession'
50 | raise ValidationFailure('body', ['accession'], msg)
51 | current = context.upgrade_properties().copy()
52 | current['uuid'] = str(context.uuid)
53 | validate_request(schema, request, data, current)
54 |
55 |
56 | def validate_item_content_patch(context, request):
57 | data = context.upgrade_properties().copy()
58 | if 'schema_version' in data:
59 | del data['schema_version']
60 | data.update(request.json)
61 | schema = context.type_info.schema
62 | if 'uuid' in data and UUID(data['uuid']) != context.uuid:
63 | msg = 'uuid may not be changed'
64 | raise ValidationFailure('body', ['uuid'], msg)
65 | current = context.upgrade_properties().copy()
66 | current['uuid'] = str(context.uuid)
67 | validate_request(schema, request, data, current)
68 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_schemas.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pkg_resources import resource_listdir
3 |
4 | SCHEMA_FILES = [
5 | f for f in resource_listdir('snowflakes', 'schemas')
6 | if f.endswith('.json')
7 | ]
8 |
9 |
10 | @pytest.mark.parametrize('schema', SCHEMA_FILES)
11 | def test_load_schema(schema):
12 | from snovault.schema_utils import load_schema
13 | assert load_schema('snowflakes:schemas/%s' % schema)
14 |
15 |
16 | def test_linkTo_saves_uuid(root, submitter, lab):
17 | item = root['users'][submitter['uuid']]
18 | assert item.properties['submits_for'] == [lab['uuid']]
19 |
20 |
21 | def test_mixinProperties():
22 | from snovault.schema_utils import load_schema
23 | schema = load_schema('snowflakes:schemas/access_key.json')
24 | assert schema['properties']['uuid']['type'] == 'string'
25 |
26 |
27 | def test_dependencies(testapp):
28 | collection_url = '/testing-dependencies/'
29 | testapp.post_json(collection_url, {'dep1': 'dep1', 'dep2': 'dep2'}, status=201)
30 | testapp.post_json(collection_url, {'dep1': 'dep1'}, status=422)
31 | testapp.post_json(collection_url, {'dep2': 'dep2'}, status=422)
32 | testapp.post_json(collection_url, {'dep1': 'dep1', 'dep2': 'disallowed'}, status=422)
33 |
34 |
35 | def test_page_schema_validates_parent_is_not_collection_default_page(testapp):
36 | res = testapp.post_json('/pages/', {'name': 'users', 'title': 'Users'})
37 | uuid = res.json['@graph'][0]['@id']
38 | testapp.post_json('/pages/', {'parent': uuid, 'name': 'test', 'title': 'Test'}, status=422)
39 |
40 |
41 | def test_changelogs(testapp, registry):
42 | from snovault import TYPES
43 | for typeinfo in registry[TYPES].by_item_type.values():
44 | changelog = typeinfo.schema.get('changelog')
45 | if changelog is not None:
46 | res = testapp.get(changelog)
47 | assert res.status_int == 200, changelog
48 | assert res.content_type == 'text/markdown'
49 |
50 |
51 | def test_schemas_etag(testapp):
52 | etag = testapp.get('/profiles/', status=200).etag
53 | assert etag
54 | testapp.get('/profiles/', headers={'If-None-Match': etag}, status=304)
55 |
56 |
57 | def test_etag_if_match_tid(testapp, award):
58 | res = testapp.get(award['@id'] + '?frame=edit', status=200)
59 | etag = res.etag
60 | testapp.patch_json(award['@id'], {}, headers={'If-Match': etag}, status=200)
61 | testapp.patch_json(award['@id'], {}, headers={'If-Match': etag}, status=412)
62 |
--------------------------------------------------------------------------------
/src/snowflakes/tests/test_server_defaults.py:
--------------------------------------------------------------------------------
1 | from pytest import fixture
2 |
3 |
4 | COLLECTION_URL = '/testing-server-defaults'
5 |
6 |
7 | @fixture
8 | def extra_environ(admin):
9 | email = admin['email']
10 | return {'REMOTE_USER': str(email)}
11 |
12 |
13 | @fixture
14 | def default_content(testapp, external_tx):
15 | res = testapp.post_json(COLLECTION_URL, {}, status=201)
16 | return {'@id': res.json['@graph'][0]['@id']}
17 |
18 |
19 | def test_server_defaults(admin, anontestapp, extra_environ):
20 | res = anontestapp.post_json(
21 | COLLECTION_URL, {}, status=201,
22 | extra_environ=extra_environ,
23 | )
24 | item = res.json['@graph'][0]
25 | assert item['now'].startswith('2')
26 | assert item['user'] == admin['@id']
27 | assert item['accession'].startswith('SNO')
28 |
29 | anontestapp.patch_json(
30 | res.location, {}, status=200,
31 | extra_environ=extra_environ,
32 | )
33 |
34 |
35 | @fixture(scope='session')
36 | def test_accession_app(request, check_constraints, zsa_savepoints, app_settings):
37 | from snowflakes import main
38 | app_settings = app_settings.copy()
39 | app_settings['accession_factory'] = 'snowflakes.server_defaults.test_accession'
40 | return main({}, **app_settings)
41 |
42 |
43 | @fixture
44 | def test_accession_anontestapp(request, test_accession_app, external_tx, zsa_savepoints):
45 | '''TestApp with JSON accept header.
46 | '''
47 | from webtest import TestApp
48 | environ = {
49 | 'HTTP_ACCEPT': 'application/json',
50 | }
51 | return TestApp(test_accession_app, environ)
52 |
53 |
54 | @fixture
55 | def test_accession_authtestapp(request, test_accession_app, external_tx, zsa_savepoints):
56 | '''TestApp with JSON accept header.
57 | '''
58 | from webtest import TestApp
59 | environ = {
60 | 'HTTP_ACCEPT': 'application/json',
61 | 'REMOTE_USER': 'TEST_AUTHENTICATED',
62 | }
63 | return TestApp(test_accession_app, environ)
64 |
65 |
66 | def test_test_accession_server_defaults(admin, test_accession_anontestapp, extra_environ):
67 | res = test_accession_anontestapp.post_json(
68 | COLLECTION_URL, {}, status=201,
69 | extra_environ=extra_environ,
70 | )
71 | item = res.json['@graph'][0]
72 | assert item['accession'].startswith('TSTSS')
73 |
74 | test_accession_anontestapp.patch_json(
75 | res.location, {}, status=200,
76 | extra_environ=extra_environ,
77 | )
78 |
--------------------------------------------------------------------------------