diff --git a/.vscode/launch.json b/.vscode/launch.json index 443afd0ee37346541306f796114a1cefc0a7414c..ff04f5a491a24b0f9234a3ad300adc3a9f193152 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -44,7 +44,7 @@ "cwd": "${workspaceFolder}", "program": "${workspaceFolder}/.pyenv/bin/pytest", "args": [ - "-sv", "tests/test_parsing.py::TestLocalBackend::test_two_sections" + "-sv", "tests/test_api.py::test_repo_calcs_user" ] }, { diff --git a/.vscode/temp.sql b/.vscode/temp.sql new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docs/components.graffle/data.plist b/docs/components.graffle/data.plist index c2ca48b1353cb7f2b210ed48d90c049eb9a82462..8573b8e2771c2ab3090f048854a0f2d7eeb1becc 100644 Binary files a/docs/components.graffle/data.plist and b/docs/components.graffle/data.plist differ diff --git a/gui/src/api.js b/gui/src/api.js index 94957c9026bda37921b0cb50d47dd0a7642dd61c..dca128fa9baaf805ac58f7abcc0b3c38959a9725 100644 --- a/gui/src/api.js +++ b/gui/src/api.js @@ -2,7 +2,7 @@ import { UploadRequest } from '@navjobs/upload' import { apiBase, appStaticBase } from './config' const auth_headers = { - Authorization: 'Basic ' + btoa('me@gmail.com:nomad') + Authorization: 'Basic ' + btoa('sheldon.cooper@nomad-fairdi.tests.de:password') } const networkError = () => { diff --git a/infrastructure/nomad/docker-compose.override.yml b/infrastructure/nomad/docker-compose.override.yml index 32b59b9678cd18a6122d6bae0362d12bda15a2fa..ce472558e5cca863252b82bf15af68ffbaec3de3 100644 --- a/infrastructure/nomad/docker-compose.override.yml +++ b/infrastructure/nomad/docker-compose.override.yml @@ -15,6 +15,11 @@ version: '3.4' services: + # postgres for NOMAD-coe repository API and GUI + postgres: + ports: + - 5432:5432 + # broker for celery rabbitmq: ports: diff --git a/infrastructure/nomad/docker-compose.yml b/infrastructure/nomad/docker-compose.yml index 2f8972ae32420f0f6d196d8322ef1740cfe80ebb..e6e54ae19cf8c823734706d3f4e1a2b60906d730 100644 --- a/infrastructure/nomad/docker-compose.yml +++ b/infrastructure/nomad/docker-compose.yml @@ -21,6 +21,18 @@ x-common-variables: &nomad_backend_env NOMAD_MONGO_HOST: mongo services: + # postgres for NOMAD-coe repository API and GUI + postgres: + restart: always + image: postgres:9.4 + container_name: nomad_postgres + environment: + POSTGRES_PASSWORD: 'nomad' + POSTGRES_USER: 'postgres' + POSTGRES_DB: 'nomad' + volumes: + - nomad_postgres:/var/lib/postgresql/data + # broker for celery rabbitmq: restart: always @@ -110,6 +122,7 @@ services: command: nginx -g 'daemon off;' volumes: + nomad_postgres: nomad_mongo: nomad_elastic: nomad_rabbitmq: diff --git a/nomad/api.py b/nomad/api.py deleted file mode 100644 index 2eae8570aaae04838161fe04a03c3e3e4b234967..0000000000000000000000000000000000000000 --- a/nomad/api.py +++ /dev/null @@ -1,874 +0,0 @@ -# Copyright 2018 Markus Scheidgen -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an"AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from werkzeug.exceptions import HTTPException -from flask import Flask, request, g, jsonify, send_file, Response -from flask_restful import Resource, Api, abort -from flask_cors import CORS -from flask_httpauth import HTTPBasicAuth -from elasticsearch.exceptions import NotFoundError -from datetime import datetime -import os.path -import zipstream -from zipfile import ZIP_DEFLATED -from contextlib import contextmanager - -from nomad import config, infrastructure -from nomad.files import UploadFile, ArchiveFile, ArchiveLogFile, RepositoryFile -from nomad.utils import get_logger -from nomad.processing import Upload, NotAllowedDuringProcessing -from nomad.repo import RepoCalc -from nomad.user import User - -base_path = config.services.api_base_path - -app = Flask( - __name__, - static_url_path='%s/docs' % base_path, - static_folder=os.path.abspath(os.path.join(os.path.dirname(__file__), '../docs/.build/html'))) -CORS(app) - -app.config['SECRET_KEY'] = config.services.api_secret - -auth = HTTPBasicAuth() -api = Api(app) - - -@app.before_first_request -def setup(): - infrastructure.setup() - - -@auth.verify_password -def verify_password(username_or_token, password): - # first try to authenticate by token - user = User.verify_auth_token(username_or_token) - if not user: - # try to authenticate with username/password - user = User.objects(email=username_or_token).first() - if not user: - g.user = None - return True # anonymous access - - if not user or not user.verify_password(password): - return False - - g.user = user - return True - - -def login_really_required(func): - @auth.login_required - def wrapper(*args, **kwargs): - if g.user is None: - abort(401, message='Anonymous access is forbidden, authorization required') - else: - return func(*args, **kwargs) - wrapper.__name__ = func.__name__ - wrapper.__doc__ = func.__doc__ - return wrapper - - -@app.route('/api/token') -@login_really_required -def get_auth_token(): - token = g.user.generate_auth_token(600) - return jsonify({'token': token.decode('ascii'), 'duration': 600}) - - -class UploadsRes(Resource): - """ Uploads """ - @login_really_required - def get(self): - """ - Get a list of current users uploads. - - .. :quickref: upload; Get a list of current users uploads. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/uploads HTTP/1.1 - Accept: application/json - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: application/json - - [ - { - "name": "examples_vasp_6.zip", - "upload_id": "5b89469e0d80d40008077dbc", - "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", - "create_time": "2018-08-31T13:46:06.781000", - "upload_time": "2018-08-31T13:46:07.531000", - "is_stale": false, - "completed": true, - "status": "SUCCESS", - "current_task": "cleanup", - "tasks": ["uploading", "extracting", "parse_all", "cleanup"] - "errors": [], - "warnings": [] - } - ] - - :resheader Content-Type: application/json - :status 200: uploads successfully provided - :returns: list of :class:`nomad.data.Upload` - """ - return [upload.json_dict for upload in Upload.user_uploads(g.user)], 200 - - @login_really_required - def post(self): - """ - Create a new upload. Creating an upload on its own wont do much, but provide - a *presigned* upload URL. PUT a file to this URL to do the actual upload and - initiate the processing. - - .. :quickref: upload; Create a new upload. - - **Example request**: - - .. sourcecode:: http - - POST /nomad/api/uploads HTTP/1.1 - Accept: application/json - Content-Type: application/json - - { - "name": "vasp_data.zip" - } - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: application/json - - { - "name": "vasp_data.zip", - "upload_id": "5b89469e0d80d40008077dbc", - "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", - "create_time": "2018-08-31T13:46:06.781000", - "upload_time": "2018-08-31T13:46:07.531000", - "is_stale": false, - "completed": true, - "status": "SUCCESS", - "current_task": "cleanup", - "tasks": ["uploading", "extracting", "parse_all", "cleanup"] - "errors": [], - "warnings": [], - "calcs": [ - { - "current_task": "archiving", - "tasks": ["parsing", "normalizing", "archiving"] - "status": "SUCCESS", - "errors": [], - "warnings": [], - "parser": "parsers/vasp", - "mainfile": "Si.xml" - } - ] - } - - :jsonparam string name: An optional name for the upload. - :jsonparem string local_path: An optional path the a file that is already on the server. - In this case, uploading a file won't be possible, the local file is processed - immediatly as if it was uploaded. - :reqheader Content-Type: application/json - :resheader Content-Type: application/json - :status 200: upload successfully created - :returns: a new instance of :class:`nomad.data.Upload` - """ - json_data = request.get_json() - if json_data is None: - json_data = {} - - upload = Upload.create( - user=g.user, - name=json_data.get('name'), - local_path=json_data.get('local_path')) - - if upload.local_path is not None: - logger = get_logger( - __name__, endpoint='uploads', action='post', upload_id=upload.upload_id) - logger.info('file uploaded offline') - upload.upload_time = datetime.now() - upload.process() - logger.info('initiated processing') - - return upload.json_dict, 200 - - -class UploadRes(Resource): - """ Uploads """ - @login_really_required - def get(self, upload_id): - """ - Get an update on an existing upload. Will not only return the upload, but - also its calculations paginated. Use the pagination params to determine - the page. - - .. :quickref: upload; Get an update for an existing upload. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/uploads/5b89469e0d80d40008077dbc HTTP/1.1 - Accept: application/json - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: application/json - - { - "name": "vasp_data.zip", - "upload_id": "5b89469e0d80d40008077dbc", - "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", - "create_time": "2018-08-31T13:46:06.781000", - "upload_time": "2018-08-31T13:46:07.531000", - "is_stale": false, - "completed": true, - "status": "SUCCESS", - "current_task": "cleanup", - "tasks": ["uploading", "extracting", "parse_all", "cleanup"] - "errors": [], - "warnings": [], - "calcs": { - "pagination": { - "total": 1, - "page": 1, - "per_page": 25 - }, - "results": [ - { - "current_task": "archiving", - "tasks": ["parsing", "normalizing", "archiving"] - "status": "SUCCESS", - "errors": [], - "warnings": [], - "parser": "parsers/vasp", - "mainfile": "Si.xml" - } - ] - } - } - - :param string upload_id: the id for the upload - :qparam int page: the page starting with 1 - :qparam int per_page: desired calcs per page - :qparam str order_by: the field to sort the calcs by, use [status,mainfile] - :resheader Content-Type: application/json - :status 200: upload successfully updated and retrieved - :status 404: upload with id does not exist - :returns: the :class:`nomad.data.Upload` instance - """ - try: - upload = Upload.get(upload_id) - except KeyError: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - if upload.user_id != g.user.email: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - try: - page = int(request.args.get('page', 1)) - per_page = int(request.args.get('per_page', 10)) - order_by = str(request.args.get('order_by', 'mainfile')) - order = int(str(request.args.get('order', -1))) - except Exception: - abort(400, message='invalid pagination or ordering') - - try: - assert page >= 1 - assert per_page > 0 - except AssertionError: - abort(400, message='invalid pagination') - - if order_by not in ['mainfile', 'status', 'parser']: - abort(400, message='invalid order_by field %s' % order_by) - - order_by = ('-%s' if order == -1 else '+%s') % order_by - - calcs = upload.all_calcs((page - 1) * per_page, page * per_page, order_by) - failed_calcs = upload.failed_calcs - result = upload.json_dict - result['calcs'] = { - 'pagination': dict( - total=upload.total_calcs, page=page, per_page=per_page, - successes=upload.processed_calcs - failed_calcs, failures=failed_calcs), - 'results': [calc.json_dict for calc in calcs] - } - - return result, 200 - - @login_really_required - def post(self, upload_id): - """ - Move an upload out of the staging area. This changes the visibility of the upload. - Clients can specify, if the calcs should be restricted. - - .. :quickref: upload; Move an upload out of the staging area. - - **Example request**: - - .. sourcecode:: http - - POST /nomad/api/uploads HTTP/1.1 - Accept: application/json - Content-Type: application/json - - { - "operation": "unstage" - } - - - :param string upload_id: the upload id - :resheader Content-Type: application/json - :status 200: upload unstaged successfully - :status 404: upload could not be found - :status 400: if the operation is not supported - :returns: the upload record - """ - try: - upload = Upload.get(upload_id) - except KeyError: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - if upload.user_id != g.user.email: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - json_data = request.get_json() - if json_data is None: - json_data = {} - - operation = json_data.get('operation') - if operation == 'unstage': - upload.unstage() - return upload.json_dict, 200 - - abort(400, message='Unsuported operation %s.' % operation) - - @login_really_required - def delete(self, upload_id): - """ - Deletes an existing upload. Only ``is_ready`` or ``is_stale`` uploads - can be deleted. Deleting an upload in processing is not allowed. - - .. :quickref: upload; Delete an existing upload. - - **Example request**: - - .. sourcecode:: http - - DELETE /nomad/api/uploads/5b89469e0d80d40008077dbc HTTP/1.1 - Accept: application/json - - :param string upload_id: the id for the upload - :resheader Content-Type: application/json - :status 200: upload successfully deleted - :status 400: upload cannot be deleted - :status 404: upload with id does not exist - :returns: the :class:`nomad.data.Upload` instance with the latest processing state - """ - try: - upload = Upload.get(upload_id) - except KeyError: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - if upload.user_id != g.user.email: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - try: - upload.delete() - return upload.json_dict, 200 - except NotAllowedDuringProcessing: - abort(400, message='You must not delete an upload during processing.') - - -class UploadFileRes(Resource): - """ - Upload a file to an existing upload. Can be used to upload files via bowser - or other http clients like curl. This will start the processing of the upload. - - There are two basic ways to upload a file: multipart-formdata or simply streaming - the file data. Both are supported. The later one does not allow to transfer a - filename or other meta-data. If a filename is available, it will become the - name of the upload. - - .. :quickref: upload; Upload a file to an existing upload. - - **Curl examples for both approaches**: - - .. sourcecode:: sh - - curl -X put "/nomad/api/uploads/5b89469e0d80d40008077dbc/file" -F file=@local_file - curl "/nomad/api/uploads/5b89469e0d80d40008077dbc/file" --upload-file local_file - - :param string upload_id: the upload_id of the upload - :resheader Content-Type: application/json - :status 200: upload successfully received. - :status 404: upload with given id does not exist - :status 400: if the fileformat is not supported or the form data is different than expected. - :returns: the upload (see GET /uploads/<upload_id>) - """ - @login_really_required - def put(self, upload_id): - logger = get_logger(__name__, endpoint='upload', action='put', upload_id=upload_id) - - try: - upload = Upload.get(upload_id) - except KeyError: - abort(404, message='Upload with id %s does not exist.' % upload_id) - - if upload.upload_time is not None: - abort(400, message='A file was already uploaded to this uploade before.') - - uploadFile = UploadFile(upload_id) - - if request.mimetype == 'application/multipart-formdata': - # multipart formdata, e.g. with curl -X put "url" -F file=@local_file - # might have performance issues for large files: https://github.com/pallets/flask/issues/2086 - if 'file' in request.files: - abort(400, message='Bad multipart-formdata, there is no file part.') - file = request.files['file'] - if upload.name is '': - upload.name = file.filename - - file.save(uploadFile.os_path) - else: - # simple streaming data in HTTP body, e.g. with curl "url" -T local_file - try: - with uploadFile.open('wb') as f: - while not request.stream.is_exhausted: - f.write(request.stream.read(1024)) - - except Exception as e: - logger.error('Error on streaming upload', exc_info=e) - abort(400, message='Some IO went wrong, download probably aborted/disrupted.') - - if not uploadFile.is_valid: - uploadFile.delete() - abort(400, message='Bad file format, excpected %s.' % ", ".join(UploadFile.formats)) - - logger.info('received uploaded file') - upload.upload_time = datetime.now() - upload.process() - logger.info('initiated processing') - - return upload.json_dict, 200 - - -class RepoCalcRes(Resource): - def get(self, upload_hash, calc_hash): - """ - Get calculation data in repository form, which only entails the quanties shown - in the repository. This is basically the elastic search index entry for the - requested calculations. Calcs are references via *upload_hash*, *calc_hash* - pairs. - - .. :quickref: repo; Get calculation data in repository form. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/repo/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 - Accept: application/json - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: application/json - - { - "calc_hash":"7ddvtfRfZAvc3Crr7jOJ8UH0T34I", - "upload_time":"2018-08-30T08:41:51.771367", - "upload_id":"5b87adb813a441000a70a968", - "upload_hash":"W36aqCzAKxOCfIiMFsBJh3nHPb4a", - "mainfile":"RopD3Mo8oMV_-E5bh8uW5PiiCRkH1/data/BrK_svSi/TFCC010.CAB/vasprun.xml.relax1", - "program_name":"VASP", - "program_version":"4.6.35 3Apr08 complex parallel LinuxIFC", - "chemical_composition_bulk_reduced":"BrKSi2", - "program_basis_set_type":"plane waves", - "atom_species":[ - 35, - 19, - 14, - 14 - ], - "system_type":"Bulk", - "crystal_system":"orthorhombic", - "space_group_number":47, - "configuration_raw_gid":"sq6wTJjRKb2VTajoDLVWDxHCgyN6i", - "XC_functional_name":"GGA_X_PBE" - } - - :param string upload_hash: the hash of the upload (from uploaded file contents) - :param string calc_hash: the hash of the calculation (from mainfile) - :resheader Content-Type: application/json - :status 200: calc successfully retrieved - :status 404: calc with given hashes does not exist - :returns: the repository calculation entry - """ - try: - return RepoCalc.get(id='%s/%s' % (upload_hash, calc_hash)).json_dict, 200 - except NotFoundError: - abort(404, message='There is no calculation for %s/%s' % (upload_hash, calc_hash)) - except Exception as e: - abort(500, message=str(e)) - - -class RepoCalcsRes(Resource): - @auth.login_required - def get(self): - """ - Get *'all'* calculations in repository from, paginated. - - .. :quickref: repo; Get *'all'* calculations in repository from, paginated. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/repo?page=1&per_page=25 HTTP/1.1 - Accept: application/json - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: application/json - - { - "pagination":{ - "total":1, - "page":1, - "per_page":25 - }, - "results":[ - { - "calc_hash":"7ddvtfRfZAvc3Crr7jOJ8UH0T34I", - "upload_time":"2018-08-30T08:41:51.771367", - "upload_id":"5b87adb813a441000a70a968", - "upload_hash":"W36aqCzAKxOCfIiMFsBJh3nHPb4a", - "mainfile":"RopD3Mo8oMV_-E5bh8uW5PiiCRkH1/data/BrK_svSi/TFCC010.CAB/vasprun.xml.relax1", - "program_name":"VASP", - "program_version":"4.6.35 3Apr08 complex parallel LinuxIFC", - "chemical_composition_bulk_reduced":"BrKSi2", - "program_basis_set_type":"plane waves", - "atom_species":[ - 35, - 19, - 14, - 14 - ], - "system_type":"Bulk", - "crystal_system":"orthorhombic", - "space_group_number":47, - "configuration_raw_gid":"sq6wTJjRKb2VTajoDLVWDxHCgyN6i", - "XC_functional_name":"GGA_X_PBE" - } - ] - } - - :qparam int page: the page starting with 1 - :qparam int per_page: desired calcs per page - :qparam string owner: specifies which cals to return: all|user|staging, default is all - :resheader Content-Type: application/json - :status 200: calcs successfully retrieved - :returns: a list of repository entries in ``results`` and pagination info - """ - # TODO use argparse? bad request reponse an bad params, pagination as decorator - page = int(request.args.get('page', 1)) - per_page = int(request.args.get('per_page', 10)) - owner = request.args.get('owner', 'all') - - try: - assert page >= 1 - assert per_page > 0 - except AssertionError: - abort(400, message='invalid pagination') - - if owner == 'all': - search = RepoCalc.search().query('match_all') - elif owner == 'user': - if g.user is None: - abort(401, message='Authentication required for owner value user.') - search = RepoCalc.search().query('match_all') - search = search.filter('term', user_id=g.user.email) - elif owner == 'staging': - if g.user is None: - abort(401, message='Authentication required for owner value user.') - search = RepoCalc.search().query('match_all') - search = search.filter('term', user_id=g.user.email).filter('term', staging=True) - else: - abort(400, message='Invalid owner value. Valid values are all|user|staging, default is all') - - search = search[(page - 1) * per_page: page * per_page] - return { - 'pagination': { - 'total': search.count(), - 'page': page, - 'per_page': per_page - }, - 'results': [result.json_dict for result in search] - } - - -@app.route('%s/logs/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) -def get_calc_proc_log(upload_hash, calc_hash): - """ - Get calculation processing log. Calcs are references via *upload_hash*, *calc_hash* - pairs. - - .. :quickref: archive; Get calculation processing logs. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/logs/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 - Accept: application/json - - :param string upload_hash: the hash of the upload (from uploaded file contents) - :param string calc_hash: the hash of the calculation (from mainfile) - :resheader Content-Type: application/json - :status 200: calc successfully retrieved - :status 404: calc with given hashes does not exist - :returns: the log data, a line by line sequence of structured logs - """ - archive_id = '%s/%s' % (upload_hash, calc_hash) - - try: - archive = ArchiveLogFile(archive_id) - archive_path = archive.os_path - - rv = send_file( - archive_path, - mimetype='application/text', - as_attachment=True, - attachment_filename=os.path.basename(archive_path)) - - return rv - except KeyError: - abort(404, message='Archive/calculation %s does not exist.' % archive_id) - except FileNotFoundError: - abort(404, message='Archive/calculation %s does not exist.' % archive_id) - except Exception as e: - logger = get_logger( - __name__, endpoint='logs', action='get', - upload_hash=upload_hash, calc_hash=calc_hash) - logger.error('Exception on accessing calc proc log', exc_info=e) - abort(500, message='Could not accessing the logs.') - - -@app.route('%s/archive/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) -def get_calc(upload_hash, calc_hash): - """ - Get calculation data in archive form. Calcs are references via *upload_hash*, *calc_hash* - pairs. - - .. :quickref: archive; Get calculation data in archive form. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/archive/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 - Accept: application/json - - :param string upload_hash: the hash of the upload (from uploaded file contents) - :param string calc_hash: the hash of the calculation (from mainfile) - :resheader Content-Type: application/json - :status 200: calc successfully retrieved - :status 404: calc with given hashes does not exist - :returns: the metainfo formated JSON data of the requested calculation - """ - archive_id = '%s/%s' % (upload_hash, calc_hash) - - try: - archive = ArchiveFile(archive_id) - archive_path = archive.os_path - - rv = send_file( - archive_path, - mimetype='application/json', - as_attachment=True, - attachment_filename=os.path.basename(archive_path)) - - if config.files.compress_archive: - rv.headers['Content-Encoding'] = 'gzip' - - return rv - except KeyError: - abort(404, message='Archive %s does not exist.' % archive_id) - except FileNotFoundError: - abort(404, message='Archive %s does not exist.' % archive_id) - except Exception as e: - logger = get_logger( - __name__, endpoint='archive', action='get', - upload_hash=upload_hash, calc_hash=calc_hash) - logger.error('Exception on accessing archive', exc_info=e) - abort(500, message='Could not accessing the archive.') - - -@app.route('%s/raw/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) -def get_raw(upload_hash, calc_hash): - """ - Get calculation mainfile raw data. Calcs are references via *upload_hash*, *calc_hash* - pairs. Returns the mainfile, unless an aux_file is specified. Aux files are stored - in repository entries. See ``/repo`` endpoint. - - .. :quickref: repo; Get calculation raw data. - - **Example request**: - - .. sourcecode:: http - - GET /nomad/api/raw/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 - Accept: application/gz - - :param string upload_hash: the hash of the upload (from uploaded file contents) - :param string calc_hash: the hash of the calculation (from mainfile) - :qparam str auxfile: an optional aux_file to download the respective aux file, default is mainfile - :qparam all: set any value to get a .zip with main and aux files instead of an individual file - :resheader Content-Type: application/json - :status 200: calc raw data successfully retrieved - :status 404: calc with given hashes does not exist or the given aux file does not exist - :returns: the raw data in body - """ - archive_id = '%s/%s' % (upload_hash, calc_hash) - logger = get_logger(__name__, endpoint='raw', action='get', archive_id=archive_id) - - try: - repo = RepoCalc.get(id=archive_id) - except NotFoundError: - abort(404, message='There is no calculation for %s/%s' % (upload_hash, calc_hash)) - except Exception as e: - abort(500, message=str(e)) - - repository_file = RepositoryFile(upload_hash) - - @contextmanager - def raw_file(filename): - try: - the_file = repository_file.get_file(filename) - with the_file.open() as f: - yield f - except KeyError: - abort(404, message='The file %s does not exist.' % filename) - except FileNotFoundError: - abort(404, message='The file %s does not exist.' % filename) - - get_all = request.args.get('all', None) is not None - if get_all: - # retrieve the 'whole' calculation, meaning the mainfile and all aux files as - # a .zip archive - def generator(): - """ Stream a zip file with all files using zipstream. """ - def iterator(): - """ Replace the directory based iter of zipstream with an iter over all raw files. """ - def write(filename): - """ Write a raw file to the zipstream. """ - def iter_content(): - """ Iterate the raw file contents. """ - with raw_file(filename) as file_object: - while True: - data = file_object.read(1024) - if not data: - break - yield data - return dict(arcname=filename, iterable=iter_content()) - - yield write(repo.mainfile) - try: - for auxfile in repo.aux_files: - yield write(os.path.join(os.path.dirname(repo.mainfile), auxfile)) - except Exception as e: - logger.error('Exception while accessing auxfiles.', exc_info=e) - - zip_stream = zipstream.ZipFile(mode='w', compression=ZIP_DEFLATED) - zip_stream.paths_to_write = iterator() - - for chunk in zip_stream: - yield chunk - - response = Response(generator(), mimetype='application/zip') - response.headers['Content-Disposition'] = 'attachment; filename={}'.format('%s.zip' % archive_id) - return response - else: - # retrieve an individual raw file - auxfile = request.args.get('auxfile', None) - if auxfile: - filename = os.path.join(os.path.dirname(repo.mainfile), auxfile) - else: - filename = repo.mainfile - - try: - with raw_file(filename) as f: - rv = send_file( - f, - mimetype='application/octet-stream', - as_attachment=True, - attachment_filename=os.path.basename(filename)) - return rv - except HTTPException as e: - raise e - except Exception as e: - logger = get_logger( - __name__, endpoint='archive', action='get', - upload_hash=upload_hash, calc_hash=calc_hash) - logger.error('Exception on accessing archive', exc_info=e) - abort(500, message='Could not accessing the archive.') - - -@app.route('%s/admin/<string:operation>' % base_path, methods=['POST']) -def call_admin_operation(operation): - if operation == 'repair_uploads': - Upload.repair_all() - if operation == 'reset': - infrastructure.reset() - else: - abort(400, message='Unknown operation %s' % operation) - - return 'done', 200 - - -api.add_resource(UploadsRes, '%s/uploads' % base_path) -api.add_resource(UploadRes, '%s/uploads/<string:upload_id>' % base_path) -api.add_resource(UploadFileRes, '%s/uploads/<string:upload_id>/file' % base_path) -api.add_resource(RepoCalcsRes, '%s/repo' % base_path) -api.add_resource(RepoCalcRes, '%s/repo/<string:upload_hash>/<string:calc_hash>' % base_path) - - -if __name__ == '__main__': - app.run(debug=True, port=8000) diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b837a030c8646b11519403f61e2bc041fd8a578c --- /dev/null +++ b/nomad/api/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2018 Markus Scheidgen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an"AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .app import app +from . import upload, repository, archive + + +if __name__ == '__main__': + app.run(debug=True, port=8000) diff --git a/nomad/api/app.py b/nomad/api/app.py new file mode 100644 index 0000000000000000000000000000000000000000..ff96d31fd69aad5cb8b25fce2b172adad977d658 --- /dev/null +++ b/nomad/api/app.py @@ -0,0 +1,91 @@ +# Copyright 2018 Markus Scheidgen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an"AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from flask import Flask, g +from flask_restful import Api, abort +from flask_cors import CORS +from flask_httpauth import HTTPBasicAuth +import os.path + +from nomad import config, infrastructure +from nomad.user import User +from nomad.processing import Upload + +base_path = config.services.api_base_path + +app = Flask( + __name__, + static_url_path='%s/docs' % base_path, + static_folder=os.path.abspath(os.path.join(os.path.dirname(__file__), '../docs/.build/html'))) +CORS(app) + +app.config['SECRET_KEY'] = config.services.api_secret + +auth = HTTPBasicAuth() +api = Api(app) + + +@app.before_first_request +def setup(): + infrastructure.setup() + + +@auth.verify_password +def verify_password(username_or_token, password): + # first try to authenticate by token + g.user = User.verify_auth_token(username_or_token) + if not g.user: + # try to authenticate with username/password + try: + g.user = User.verify_user_password(username_or_token, password) + except Exception: + return False + + if not g.user: + return True # anonymous access + + return True + + +def login_really_required(func): + @auth.login_required + def wrapper(*args, **kwargs): + if g.user is None: + abort(401, message='Anonymous access is forbidden, authorization required') + else: + return func(*args, **kwargs) + wrapper.__name__ = func.__name__ + wrapper.__doc__ = func.__doc__ + return wrapper + + +@app.route('/api/token') +@login_really_required +def get_auth_token(): + assert False, 'All authorization is none via NOMAD-coe repository GUI' + # TODO all authorization is done via NOMAD-coe repository GUI + # token = g.user.generate_auth_token(600) + # return jsonify({'token': token.decode('ascii'), 'duration': 600}) + + +@app.route('%s/admin/<string:operation>' % base_path, methods=['POST']) +def call_admin_operation(operation): + if operation == 'repair_uploads': + Upload.repair_all() + if operation == 'reset': + infrastructure.reset() + else: + abort(400, message='Unknown operation %s' % operation) + + return 'done', 200 diff --git a/nomad/api/archive.py b/nomad/api/archive.py new file mode 100644 index 0000000000000000000000000000000000000000..4cf9347ee712de67e59fa8fd473e1288b154516b --- /dev/null +++ b/nomad/api/archive.py @@ -0,0 +1,123 @@ +# Copyright 2018 Markus Scheidgen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an"AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path + +from flask import send_file +from flask_restful import abort + +from nomad import config +from nomad.files import ArchiveFile, ArchiveLogFile +from nomad.utils import get_logger + +from .app import app, base_path + + +@app.route('%s/logs/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) +def get_calc_proc_log(upload_hash, calc_hash): + """ + Get calculation processing log. Calcs are references via *upload_hash*, *calc_hash* + pairs. + + .. :quickref: archive; Get calculation processing logs. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/logs/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 + Accept: application/json + + :param string upload_hash: the hash of the upload (from uploaded file contents) + :param string calc_hash: the hash of the calculation (from mainfile) + :resheader Content-Type: application/json + :status 200: calc successfully retrieved + :status 404: calc with given hashes does not exist + :returns: the log data, a line by line sequence of structured logs + """ + archive_id = '%s/%s' % (upload_hash, calc_hash) + + try: + archive = ArchiveLogFile(archive_id) + if not archive.exists(): + raise FileNotFoundError() + + archive_path = archive.os_path + + rv = send_file( + archive_path, + mimetype='application/text', + as_attachment=True, + attachment_filename=os.path.basename(archive_path)) + + return rv + except FileNotFoundError: + abort(404, message='Archive/calculation %s does not exist.' % archive_id) + except Exception as e: + logger = get_logger( + __name__, endpoint='logs', action='get', + upload_hash=upload_hash, calc_hash=calc_hash) + logger.error('Exception on accessing calc proc log', exc_info=e) + abort(500, message='Could not accessing the logs.') + + +@app.route('%s/archive/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) +def get_calc(upload_hash, calc_hash): + """ + Get calculation data in archive form. Calcs are references via *upload_hash*, *calc_hash* + pairs. + + .. :quickref: archive; Get calculation data in archive form. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/archive/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 + Accept: application/json + + :param string upload_hash: the hash of the upload (from uploaded file contents) + :param string calc_hash: the hash of the calculation (from mainfile) + :resheader Content-Type: application/json + :status 200: calc successfully retrieved + :status 404: calc with given hashes does not exist + :returns: the metainfo formated JSON data of the requested calculation + """ + archive_id = '%s/%s' % (upload_hash, calc_hash) + + try: + archive = ArchiveFile(archive_id) + if not archive.exists(): + raise FileNotFoundError() + + archive_path = archive.os_path + + rv = send_file( + archive_path, + mimetype='application/json', + as_attachment=True, + attachment_filename=os.path.basename(archive_path)) + + if config.files.compress_archive: + rv.headers['Content-Encoding'] = 'gzip' + + return rv + except FileNotFoundError: + abort(404, message='Archive %s does not exist.' % archive_id) + except Exception as e: + logger = get_logger( + __name__, endpoint='archive', action='get', + upload_hash=upload_hash, calc_hash=calc_hash) + logger.error('Exception on accessing archive', exc_info=e) + abort(500, message='Could not accessing the archive.') diff --git a/nomad/api/repository.py b/nomad/api/repository.py new file mode 100644 index 0000000000000000000000000000000000000000..c5f887668c66b46a8793573fa342253fd37c0e90 --- /dev/null +++ b/nomad/api/repository.py @@ -0,0 +1,305 @@ +# Copyright 2018 Markus Scheidgen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an"AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path +from contextlib import contextmanager +from zipfile import ZIP_DEFLATED + +import zipstream +from elasticsearch.exceptions import NotFoundError +from flask import Response, g, request, send_file +from flask_restful import Resource, abort +from werkzeug.exceptions import HTTPException + +from nomad.files import RepositoryFile +from nomad.repo import RepoCalc +from nomad.utils import get_logger + +from .app import api, app, auth, base_path + + +class RepoCalcRes(Resource): + def get(self, upload_hash, calc_hash): + """ + Get calculation data in repository form, which only entails the quanties shown + in the repository. This is basically the elastic search index entry for the + requested calculations. Calcs are references via *upload_hash*, *calc_hash* + pairs. + + .. :quickref: repo; Get calculation data in repository form. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/repo/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "calc_hash":"7ddvtfRfZAvc3Crr7jOJ8UH0T34I", + "upload_time":"2018-08-30T08:41:51.771367", + "upload_id":"5b87adb813a441000a70a968", + "upload_hash":"W36aqCzAKxOCfIiMFsBJh3nHPb4a", + "mainfile":"RopD3Mo8oMV_-E5bh8uW5PiiCRkH1/data/BrK_svSi/TFCC010.CAB/vasprun.xml.relax1", + "program_name":"VASP", + "program_version":"4.6.35 3Apr08 complex parallel LinuxIFC", + "chemical_composition_bulk_reduced":"BrKSi2", + "program_basis_set_type":"plane waves", + "atom_species":[ + 35, + 19, + 14, + 14 + ], + "system_type":"Bulk", + "crystal_system":"orthorhombic", + "space_group_number":47, + "configuration_raw_gid":"sq6wTJjRKb2VTajoDLVWDxHCgyN6i", + "XC_functional_name":"GGA_X_PBE" + } + + :param string upload_hash: the hash of the upload (from uploaded file contents) + :param string calc_hash: the hash of the calculation (from mainfile) + :resheader Content-Type: application/json + :status 200: calc successfully retrieved + :status 404: calc with given hashes does not exist + :returns: the repository calculation entry + """ + try: + return RepoCalc.get(id='%s/%s' % (upload_hash, calc_hash)).json_dict, 200 + except NotFoundError: + abort(404, message='There is no calculation for %s/%s' % (upload_hash, calc_hash)) + except Exception as e: + abort(500, message=str(e)) + + +class RepoCalcsRes(Resource): + @auth.login_required + def get(self): + """ + Get *'all'* calculations in repository from, paginated. + + .. :quickref: repo; Get *'all'* calculations in repository from, paginated. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/repo?page=1&per_page=25 HTTP/1.1 + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "pagination":{ + "total":1, + "page":1, + "per_page":25 + }, + "results":[ + { + "calc_hash":"7ddvtfRfZAvc3Crr7jOJ8UH0T34I", + "upload_time":"2018-08-30T08:41:51.771367", + "upload_id":"5b87adb813a441000a70a968", + "upload_hash":"W36aqCzAKxOCfIiMFsBJh3nHPb4a", + "mainfile":"RopD3Mo8oMV_-E5bh8uW5PiiCRkH1/data/BrK_svSi/TFCC010.CAB/vasprun.xml.relax1", + "program_name":"VASP", + "program_version":"4.6.35 3Apr08 complex parallel LinuxIFC", + "chemical_composition_bulk_reduced":"BrKSi2", + "program_basis_set_type":"plane waves", + "atom_species":[ + 35, + 19, + 14, + 14 + ], + "system_type":"Bulk", + "crystal_system":"orthorhombic", + "space_group_number":47, + "configuration_raw_gid":"sq6wTJjRKb2VTajoDLVWDxHCgyN6i", + "XC_functional_name":"GGA_X_PBE" + } + ] + } + + :qparam int page: the page starting with 1 + :qparam int per_page: desired calcs per page + :qparam string owner: specifies which cals to return: all|user|staging, default is all + :resheader Content-Type: application/json + :status 200: calcs successfully retrieved + :returns: a list of repository entries in ``results`` and pagination info + """ + # TODO use argparse? bad request reponse an bad params, pagination as decorator + page = int(request.args.get('page', 1)) + per_page = int(request.args.get('per_page', 10)) + owner = request.args.get('owner', 'all') + + try: + assert page >= 1 + assert per_page > 0 + except AssertionError: + abort(400, message='invalid pagination') + + if owner == 'all': + search = RepoCalc.search().query('match_all') + elif owner == 'user': + if g.user is None: + abort(401, message='Authentication required for owner value user.') + search = RepoCalc.search().query('match_all') + search = search.filter('term', user_id=g.user.email) + elif owner == 'staging': + if g.user is None: + abort(401, message='Authentication required for owner value user.') + search = RepoCalc.search().query('match_all') + search = search.filter('term', user_id=g.user.email).filter('term', staging=True) + else: + abort(400, message='Invalid owner value. Valid values are all|user|staging, default is all') + + search = search[(page - 1) * per_page: page * per_page] + return { + 'pagination': { + 'total': search.count(), + 'page': page, + 'per_page': per_page + }, + 'results': [result.json_dict for result in search] + } + + +@app.route('%s/raw/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET']) +def get_raw(upload_hash, calc_hash): + """ + Get calculation mainfile raw data. Calcs are references via *upload_hash*, *calc_hash* + pairs. Returns the mainfile, unless an aux_file is specified. Aux files are stored + in repository entries. See ``/repo`` endpoint. + + .. :quickref: repo; Get calculation raw data. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/raw/W36aqCzAKxOCfIiMFsBJh3nHPb4a/7ddvtfRfZAvc3Crr7jOJ8UH0T34I HTTP/1.1 + Accept: application/gz + + :param string upload_hash: the hash of the upload (from uploaded file contents) + :param string calc_hash: the hash of the calculation (from mainfile) + :qparam str auxfile: an optional aux_file to download the respective aux file, default is mainfile + :qparam all: set any value to get a .zip with main and aux files instead of an individual file + :resheader Content-Type: application/json + :status 200: calc raw data successfully retrieved + :status 404: calc with given hashes does not exist or the given aux file does not exist + :returns: the raw data in body + """ + archive_id = '%s/%s' % (upload_hash, calc_hash) + logger = get_logger(__name__, endpoint='raw', action='get', archive_id=archive_id) + + try: + repo = RepoCalc.get(id=archive_id) + except NotFoundError: + abort(404, message='There is no calculation for %s/%s' % (upload_hash, calc_hash)) + except Exception as e: + abort(500, message=str(e)) + + repository_file = RepositoryFile(upload_hash) + + @contextmanager + def raw_file(filename): + try: + the_file = repository_file.get_file(filename) + with the_file.open() as f: + yield f + except KeyError: + abort(404, message='The file %s does not exist.' % filename) + except FileNotFoundError: + abort(404, message='The file %s does not exist.' % filename) + + get_all = request.args.get('all', None) is not None + if get_all: + # retrieve the 'whole' calculation, meaning the mainfile and all aux files as + # a .zip archive + def generator(): + """ Stream a zip file with all files using zipstream. """ + def iterator(): + """ Replace the directory based iter of zipstream with an iter over all raw files. """ + def write(filename): + """ Write a raw file to the zipstream. """ + def iter_content(): + """ Iterate the raw file contents. """ + with raw_file(filename) as file_object: + while True: + data = file_object.read(1024) + if not data: + break + yield data + return dict(arcname=filename, iterable=iter_content()) + + yield write(repo.mainfile) + try: + for auxfile in repo.aux_files: + yield write(os.path.join(os.path.dirname(repo.mainfile), auxfile)) + except Exception as e: + logger.error('Exception while accessing auxfiles.', exc_info=e) + + zip_stream = zipstream.ZipFile(mode='w', compression=ZIP_DEFLATED) + zip_stream.paths_to_write = iterator() + + for chunk in zip_stream: + yield chunk + + response = Response(generator(), mimetype='application/zip') + response.headers['Content-Disposition'] = 'attachment; filename={}'.format('%s.zip' % archive_id) + return response + else: + # retrieve an individual raw file + auxfile = request.args.get('auxfile', None) + if auxfile: + filename = os.path.join(os.path.dirname(repo.mainfile), auxfile) + else: + filename = repo.mainfile + + try: + with raw_file(filename) as f: + rv = send_file( + f, + mimetype='application/octet-stream', + as_attachment=True, + attachment_filename=os.path.basename(filename)) + return rv + except HTTPException as e: + raise e + except Exception as e: + logger = get_logger( + __name__, endpoint='archive', action='get', + upload_hash=upload_hash, calc_hash=calc_hash) + logger.error('Exception on accessing archive', exc_info=e) + abort(500, message='Could not accessing the archive.') + + +api.add_resource(RepoCalcsRes, '%s/repo' % base_path) +api.add_resource(RepoCalcRes, '%s/repo/<string:upload_hash>/<string:calc_hash>' % base_path) diff --git a/nomad/api/upload.py b/nomad/api/upload.py new file mode 100644 index 0000000000000000000000000000000000000000..eb5541862bb1323b347f7f191fc9e326e8166f39 --- /dev/null +++ b/nomad/api/upload.py @@ -0,0 +1,424 @@ +# Copyright 2018 Markus Scheidgen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an"AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from datetime import datetime + +from flask import g, request +from flask_restful import Resource, abort + +from nomad.files import UploadFile +from nomad.processing import NotAllowedDuringProcessing, Upload +from nomad.utils import get_logger + +from .app import api, base_path, login_really_required + + +class UploadsRes(Resource): + """ Uploads """ + @login_really_required + def get(self): + """ + Get a list of current users uploads. + + .. :quickref: upload; Get a list of current users uploads. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/uploads HTTP/1.1 + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + [ + { + "name": "examples_vasp_6.zip", + "upload_id": "5b89469e0d80d40008077dbc", + "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", + "create_time": "2018-08-31T13:46:06.781000", + "upload_time": "2018-08-31T13:46:07.531000", + "is_stale": false, + "completed": true, + "status": "SUCCESS", + "current_task": "cleanup", + "tasks": ["uploading", "extracting", "parse_all", "cleanup"] + "errors": [], + "warnings": [] + } + ] + + :resheader Content-Type: application/json + :status 200: uploads successfully provided + :returns: list of :class:`nomad.data.Upload` + """ + return [upload.json_dict for upload in Upload.user_uploads(g.user)], 200 + + @login_really_required + def post(self): + """ + Create a new upload. Creating an upload on its own wont do much, but provide + a *presigned* upload URL. PUT a file to this URL to do the actual upload and + initiate the processing. + + .. :quickref: upload; Create a new upload. + + **Example request**: + + .. sourcecode:: http + + POST /nomad/api/uploads HTTP/1.1 + Accept: application/json + Content-Type: application/json + + { + "name": "vasp_data.zip" + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "name": "vasp_data.zip", + "upload_id": "5b89469e0d80d40008077dbc", + "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", + "create_time": "2018-08-31T13:46:06.781000", + "upload_time": "2018-08-31T13:46:07.531000", + "is_stale": false, + "completed": true, + "status": "SUCCESS", + "current_task": "cleanup", + "tasks": ["uploading", "extracting", "parse_all", "cleanup"] + "errors": [], + "warnings": [], + "calcs": [ + { + "current_task": "archiving", + "tasks": ["parsing", "normalizing", "archiving"] + "status": "SUCCESS", + "errors": [], + "warnings": [], + "parser": "parsers/vasp", + "mainfile": "Si.xml" + } + ] + } + + :jsonparam string name: An optional name for the upload. + :jsonparem string local_path: An optional path the a file that is already on the server. + In this case, uploading a file won't be possible, the local file is processed + immediatly as if it was uploaded. + :reqheader Content-Type: application/json + :resheader Content-Type: application/json + :status 200: upload successfully created + :returns: a new instance of :class:`nomad.data.Upload` + """ + json_data = request.get_json() + if json_data is None: + json_data = {} + + upload = Upload.create( + user=g.user, + name=json_data.get('name'), + local_path=json_data.get('local_path')) + + if upload.local_path is not None: + logger = get_logger( + __name__, endpoint='uploads', action='post', upload_id=upload.upload_id) + logger.info('file uploaded offline') + upload.upload_time = datetime.now() + upload.process() + logger.info('initiated processing') + + return upload.json_dict, 200 + + +class UploadRes(Resource): + """ Uploads """ + @login_really_required + def get(self, upload_id): + """ + Get an update on an existing upload. Will not only return the upload, but + also its calculations paginated. Use the pagination params to determine + the page. + + .. :quickref: upload; Get an update for an existing upload. + + **Example request**: + + .. sourcecode:: http + + GET /nomad/api/uploads/5b89469e0d80d40008077dbc HTTP/1.1 + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "name": "vasp_data.zip", + "upload_id": "5b89469e0d80d40008077dbc", + "presigned_url": "http://minio:9000/uploads/5b89469e0d80d40008077dbc?X-Amz-Algorithm=AWS4-...", + "create_time": "2018-08-31T13:46:06.781000", + "upload_time": "2018-08-31T13:46:07.531000", + "is_stale": false, + "completed": true, + "status": "SUCCESS", + "current_task": "cleanup", + "tasks": ["uploading", "extracting", "parse_all", "cleanup"] + "errors": [], + "warnings": [], + "calcs": { + "pagination": { + "total": 1, + "page": 1, + "per_page": 25 + }, + "results": [ + { + "current_task": "archiving", + "tasks": ["parsing", "normalizing", "archiving"] + "status": "SUCCESS", + "errors": [], + "warnings": [], + "parser": "parsers/vasp", + "mainfile": "Si.xml" + } + ] + } + } + + :param string upload_id: the id for the upload + :qparam int page: the page starting with 1 + :qparam int per_page: desired calcs per page + :qparam str order_by: the field to sort the calcs by, use [status,mainfile] + :resheader Content-Type: application/json + :status 200: upload successfully updated and retrieved + :status 404: upload with id does not exist + :returns: the :class:`nomad.data.Upload` instance + """ + try: + upload = Upload.get(upload_id) + except KeyError: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + if upload.user_id != g.user.email: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + try: + page = int(request.args.get('page', 1)) + per_page = int(request.args.get('per_page', 10)) + order_by = str(request.args.get('order_by', 'mainfile')) + order = int(str(request.args.get('order', -1))) + except Exception: + abort(400, message='invalid pagination or ordering') + + try: + assert page >= 1 + assert per_page > 0 + except AssertionError: + abort(400, message='invalid pagination') + + if order_by not in ['mainfile', 'status', 'parser']: + abort(400, message='invalid order_by field %s' % order_by) + + order_by = ('-%s' if order == -1 else '+%s') % order_by + + calcs = upload.all_calcs((page - 1) * per_page, page * per_page, order_by) + failed_calcs = upload.failed_calcs + result = upload.json_dict + result['calcs'] = { + 'pagination': dict( + total=upload.total_calcs, page=page, per_page=per_page, + successes=upload.processed_calcs - failed_calcs, failures=failed_calcs), + 'results': [calc.json_dict for calc in calcs] + } + + return result, 200 + + @login_really_required + def post(self, upload_id): + """ + Move an upload out of the staging area. This changes the visibility of the upload. + Clients can specify, if the calcs should be restricted. + + .. :quickref: upload; Move an upload out of the staging area. + + **Example request**: + + .. sourcecode:: http + + POST /nomad/api/uploads HTTP/1.1 + Accept: application/json + Content-Type: application/json + + { + "operation": "unstage" + } + + + :param string upload_id: the upload id + :resheader Content-Type: application/json + :status 200: upload unstaged successfully + :status 404: upload could not be found + :status 400: if the operation is not supported + :returns: the upload record + """ + try: + upload = Upload.get(upload_id) + except KeyError: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + if upload.user_id != g.user.email: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + json_data = request.get_json() + if json_data is None: + json_data = {} + + operation = json_data.get('operation') + if operation == 'unstage': + upload.unstage() + return upload.json_dict, 200 + + abort(400, message='Unsuported operation %s.' % operation) + + @login_really_required + def delete(self, upload_id): + """ + Deletes an existing upload. Only ``is_ready`` or ``is_stale`` uploads + can be deleted. Deleting an upload in processing is not allowed. + + .. :quickref: upload; Delete an existing upload. + + **Example request**: + + .. sourcecode:: http + + DELETE /nomad/api/uploads/5b89469e0d80d40008077dbc HTTP/1.1 + Accept: application/json + + :param string upload_id: the id for the upload + :resheader Content-Type: application/json + :status 200: upload successfully deleted + :status 400: upload cannot be deleted + :status 404: upload with id does not exist + :returns: the :class:`nomad.data.Upload` instance with the latest processing state + """ + try: + upload = Upload.get(upload_id) + except KeyError: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + if upload.user_id != g.user.email: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + try: + upload.delete() + return upload.json_dict, 200 + except NotAllowedDuringProcessing: + abort(400, message='You must not delete an upload during processing.') + + +class UploadFileRes(Resource): + """ + Upload a file to an existing upload. Can be used to upload files via bowser + or other http clients like curl. This will start the processing of the upload. + + There are two basic ways to upload a file: multipart-formdata or simply streaming + the file data. Both are supported. The later one does not allow to transfer a + filename or other meta-data. If a filename is available, it will become the + name of the upload. + + .. :quickref: upload; Upload a file to an existing upload. + + **Curl examples for both approaches**: + + .. sourcecode:: sh + + curl -X put "/nomad/api/uploads/5b89469e0d80d40008077dbc/file" -F file=@local_file + curl "/nomad/api/uploads/5b89469e0d80d40008077dbc/file" --upload-file local_file + + :param string upload_id: the upload_id of the upload + :resheader Content-Type: application/json + :status 200: upload successfully received. + :status 404: upload with given id does not exist + :status 400: if the fileformat is not supported or the form data is different than expected. + :returns: the upload (see GET /uploads/<upload_id>) + """ + @login_really_required + def put(self, upload_id): + logger = get_logger(__name__, endpoint='upload', action='put', upload_id=upload_id) + + try: + upload = Upload.get(upload_id) + except KeyError: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + if upload.upload_time is not None: + abort(400, message='A file was already uploaded to this uploade before.') + + uploadFile = UploadFile(upload_id) + + if request.mimetype == 'application/multipart-formdata': + # multipart formdata, e.g. with curl -X put "url" -F file=@local_file + # might have performance issues for large files: https://github.com/pallets/flask/issues/2086 + if 'file' in request.files: + abort(400, message='Bad multipart-formdata, there is no file part.') + file = request.files['file'] + if upload.name is '': + upload.name = file.filename + + file.save(uploadFile.os_path) + else: + # simple streaming data in HTTP body, e.g. with curl "url" -T local_file + try: + with uploadFile.open('wb') as f: + while not request.stream.is_exhausted: + f.write(request.stream.read(1024)) + + except Exception as e: + logger.error('Error on streaming upload', exc_info=e) + abort(400, message='Some IO went wrong, download probably aborted/disrupted.') + + if not uploadFile.is_valid: + uploadFile.delete() + abort(400, message='Bad file format, excpected %s.' % ", ".join(UploadFile.formats)) + + logger.info('received uploaded file') + upload.upload_time = datetime.now() + upload.process() + logger.info('initiated processing') + + return upload.json_dict, 200 + + +api.add_resource(UploadsRes, '%s/uploads' % base_path) +api.add_resource(UploadRes, '%s/uploads/<string:upload_id>' % base_path) +api.add_resource(UploadFileRes, '%s/uploads/<string:upload_id>/file' % base_path) diff --git a/nomad/client.py b/nomad/client.py index a602884b0eddf8105d5800ba5ae0987a25bc6383..8893bf8a5a3d6613836b41cfb2b6a390544a82e1 100644 --- a/nomad/client.py +++ b/nomad/client.py @@ -35,8 +35,8 @@ from nomad.normalizing import normalizers api_base = 'http://localhost/nomad/api' -user = 'other@gmail.com' -pw = 'nomad' +user = 'leonard.hofstadter@nomad-fairdi.tests.de' +pw = 'password' def handle_common_errors(func): @@ -325,11 +325,13 @@ def api(): @cli.command(help='Runs tests and linting. Useful before commit code.') -def qa(): +@click.option('--skip-tests', help='Do not test, just do code checks.', is_flag=True) +def qa(skip_tests: bool): os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) ret_code = 0 - click.echo('Run tests ...') - ret_code += os.system('python -m pytest tests') + if not skip_tests: + click.echo('Run tests ...') + ret_code += os.system('python -m pytest tests') click.echo('Run code style checks ...') ret_code += os.system('python -m pycodestyle --ignore=E501,E701 nomad tests') click.echo('Run linter ...') diff --git a/nomad/dependencies.py b/nomad/dependencies.py index f6d7ce65d5007a1857d9a3245fec46a050a8ce81..f910cfceb092a1dbdc18a8fafc85ae7064e36024 100644 --- a/nomad/dependencies.py +++ b/nomad/dependencies.py @@ -158,11 +158,12 @@ class PythonGit(): dependencies = [ - PythonGit( - name='repository-api', - git_url='https://gitlab.mpcdf.mpg.de/NoMaD/NomadRepositoryParser.git', - git_branch='v2.1' - ), + # repository api is not really usuable, because it is written in python 2.x + # PythonGit( + # name='repository-api', + # git_url='https://gitlab.mpcdf.mpg.de/NoMaD/NomadRepositoryParser.git', + # git_branch='v2.1' + # ), PythonGit( name='nomad-meta-info', git_url='https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-meta-info.git', diff --git a/nomad/infrastructure.py b/nomad/infrastructure.py index 7faca085a8e686076e4be586d666d95651e4be5e..8f60712d342c2da155a11c991b0dea620885a4c6 100644 --- a/nomad/infrastructure.py +++ b/nomad/infrastructure.py @@ -33,15 +33,17 @@ mongo_client = None """ The pymongo mongodb client. """ +repository_db = None +""" The repository postgres db sqlalchemy client. """ + + def setup(): """ Creates connections to mongodb and elastic search. """ global elastic_client setup_logging() setup_mongo() setup_elastic() - - from nomad import user - user.ensure_test_users() + setup_repository_db() def setup_logging(): @@ -79,11 +81,20 @@ def setup_elastic(): logger.info('init elastic index') +def setup_repository_db(): + """ Creates a sqlalchemy session for the NOMAD-coe repository postgres db. """ + from sqlalchemy import create_engine + from sqlalchemy.orm import sessionmaker + + global repository_db + + engine = create_engine('postgresql://postgres:nomad@localhost:5432/nomad', echo=False) + repository_db = sessionmaker(bind=engine)() + + def reset(): - """ Resets the databases mongo/user and elastic/calcs. Be careful. """ + """ Resets the databases mongo and elastic/calcs. Be careful. """ mongo_client.drop_database(config.mongo.users_db) - from nomad import user - user.ensure_test_users() elastic_client.indices.delete(index=config.elastic.calc_index) from nomad.repo import RepoCalc diff --git a/nomad/processing/data.py b/nomad/processing/data.py index 02a78b6c8b1c7a5614ee03f5149537ce83292485..25ab0d609931e62bbb5bf728cc81316fb57c0fa5 100644 --- a/nomad/processing/data.py +++ b/nomad/processing/data.py @@ -422,7 +422,7 @@ class Upload(Chord): kwargs.update(user_id=user.email) self = super().create(**kwargs) - basic_auth_token = base64.b64encode(b'%s:' % user.generate_auth_token()).decode('utf-8') + basic_auth_token = base64.b64encode(b'%s:' % user.get_auth_token()).decode('utf-8') self.upload_url = cls._external_objects_url('/uploads/%s/file' % self.upload_id) self.upload_command = 'curl -H "Authorization: Basic %s" "%s" --upload-file local_file' % ( diff --git a/nomad/user.py b/nomad/user.py index 16351bbb25417ba6de540b50a9d5fd6146021f43..a006be6a242d411e9036119a0eb08eabcb485126 100644 --- a/nomad/user.py +++ b/nomad/user.py @@ -16,79 +16,102 @@ Module with some prototypes/placeholder for future user management in nomad@FAIR. """ -from mongoengine import Document, EmailField, StringField, ReferenceField, ListField -from passlib.apps import custom_app_context as pwd_context -from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired +from passlib.hash import bcrypt +from sqlalchemy import Column, Integer, String +from sqlalchemy.ext.declarative import declarative_base -from nomad import config +from nomad import infrastructure -class User(Document): - """ Represents users in the database. """ - email = EmailField(primary_key=True) - name = StringField() - password_hash = StringField() +Base = declarative_base() - def hash_password(self, password): - self.password_hash = pwd_context.encrypt(password) - def verify_password(self, password): - return pwd_context.verify(password, self.password_hash) +class Session(Base): # type: ignore + __tablename__ = 'sessions' - def generate_auth_token(self, expiration=600): - s = Serializer(config.services.api_secret, expires_in=expiration) - return s.dumps({'id': self.id}) + token = Column(String, primary_key=True) + user_id = Column(String) + + +class LoginException(Exception): + pass + + +class User(Base): # type: ignore + """ + SQLAlchemy model class that represents NOMAD-coe repository postgresdb *users*. + Provides functions for authenticating via password or session token. + + It is not intended to create or update users. This should be done via the + NOMAD-coe repository GUI. + """ + __tablename__ = 'users' + + user_id = Column(Integer, primary_key=True) + email = Column(String) + firstname = Column(String) + lastname = Column(String) + password = Column(String) + + def __repr__(self): + return '<User(email="%s")>' % self.email + + def _hash_password(self, password): + assert False, 'Login functions are done by the NOMAD-coe repository GUI' + # password_hash = bcrypt.encrypt(password, ident='2y') + # self.password = password_hash + + def _verify_password(self, password): + return bcrypt.verify(password, self.password) + + def _generate_auth_token(self, expiration=600): + assert False, 'Login functions are done by the NOMAD-coe repository GUI' + + def get_auth_token(self): + repository_db = infrastructure.repository_db + session = repository_db.query(Session).filter_by(user_id=self.user_id).first() + if not session: + raise LoginException('No session, user probably not logged in at NOMAD-coe repository GUI') + + return session.token.encode('utf-8') + + @staticmethod + def verify_user_password(email, password): + repository_db = infrastructure.repository_db + user = repository_db.query(User).filter_by(email=email).first() + if not user: + return None + + if user._verify_password(password): + return user + else: + raise LoginException('Wrong password') @staticmethod def verify_auth_token(token): - s = Serializer(config.services.api_secret) - try: - data = s.loads(token) - except SignatureExpired: - return None # valid token, but expired - except BadSignature: - return None # invalid token - - return User.objects(email=data['id']).first() - - -class DataSet(Document): - name = StringField() - description = StringField() - doi = StringField() - - user = ReferenceField(User) - calcs = ListField(StringField) - - meta = { - 'indexes': [ - 'user', - 'doi', - 'calcs' - ] - } - - -# provid a test user for testing -me = None -other = None - - -def ensure_test_users(): - global me - me = User.objects(email='me@gmail.com').first() - if me is None: - me = User( - email='me@gmail.com', - name='Me Meyer') - me.hash_password('nomad') - me.save() - - global other - me = User.objects(email='other@gmail.com').first() - if me is None: - me = User( - email='other@gmail.com', - name='Other User') - me.hash_password('nomad') - me.save() + repository_db = infrastructure.repository_db + session = repository_db.query(Session).filter_by(token=token).first() + if session is None: + return None + + user = repository_db.query(User).filter_by(user_id=session.user_id).first() + assert user, 'User in sessions must exist.' + return user + + +def ensure_test_user(email): + """ + Allows tests to make sure that the default test users exist in the database. + Returns: + The user as :class:`User` instance. + """ + existing = infrastructure.repository_db.query(User).filter_by( + email=email).first() + assert existing, 'Test user %s does not exist.' % email + + session = infrastructure.repository_db.query(Session).filter_by( + user_id=existing.user_id).first() + assert session, 'Test user %s has no session.' % email + assert session.token == email, 'Test user %s session has unexpected token.' % email + + return existing diff --git a/repository/repo_postgres_schema b/repository/repo_postgres_schema new file mode 100644 index 0000000000000000000000000000000000000000..c473a1789122fbf991afb83874afd0671a18737a --- /dev/null +++ b/repository/repo_postgres_schema @@ -0,0 +1 @@ +- "login_token" or "sessions", whats the difference, what should be used for authenticating uploader. \ No newline at end of file diff --git a/repository/repository_es_example.js b/repository/repository_es_example.js new file mode 100644 index 0000000000000000000000000000000000000000..8a17b9e54bba52d14fab470c70f9240b44770478 --- /dev/null +++ b/repository/repository_es_example.js @@ -0,0 +1,2375 @@ +{ + "took": 20, + "timed_out": false, + "_shards": { + "total": 5, + "successful": 5, + "failed": 0 + }, + "hits": { + "total": 6349890, + "max_score": 1.0, + "hits": [ + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506001", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gt38MgRcejX1YUVjFk-cE2UNXDK88", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/vasprun.xml.relax2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/CHG.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/CHGCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/CONTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/DOSCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/EIGENVAL.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/INCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/KPOINTS.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/OUTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC010.CAB/POSCAR.relax2.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/CHG.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/CHGCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/CONTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/DOSCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/EIGENVAL.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/INCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/KPOINTS.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/OUTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/POSCAR.relax2.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC010.CAB/vasprun.xml.relax2", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506001, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 47 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "orthorhombic" + ], + "repository_checksum": "IQP6EKSSDVCBTOJZY6LNHCMFWVOCIDATBMCHPYQ6CVD6KCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "Tl2SnAg" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avqh", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506002", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gXRBk5EF_HmbWd4MWy6UHcub9R2YX", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/vasprun.xml.relax1", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/CHG.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/CHGCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/CONTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/DOSCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/EIGENVAL.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/INCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/KPOINTS.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/OUTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC011.ABC/POSCAR.relax1.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/CHG.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/CHGCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/CONTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/DOSCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/EIGENVAL.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/INCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/KPOINTS.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/OUTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/POSCAR.relax1.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC011.ABC/vasprun.xml.relax1", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506002, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 12 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "monoclinic" + ], + "repository_checksum": "MEMSZ2VNDBLPDCZAJFBSYL755HVXXRSMCULKBYS6UKLSSCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "TlSnAg2" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avqi", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506008", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gSrMDRLr5oWRwHc7KoDBDibWSJ3zm", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/vasprun.xml.relax1", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/CHG.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/CHGCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/CONTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/DOSCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/EIGENVAL.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/INCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/KPOINTS.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/OUTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC012.ABC/POSCAR.relax1.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/CHG.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/CHGCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/CONTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/DOSCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/EIGENVAL.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/INCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/KPOINTS.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/OUTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/POSCAR.relax1.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC012.ABC/vasprun.xml.relax1", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506008, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 123 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "DWZ5L2NBIRSEEPMPCXA6N52X2KYWOE3FZPST2XQI5CK7MCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "TlSnAg2" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avqo", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506018", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gv8gAUiLhjMmiawnsSLzzuNqTE15o", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/vasprun.xml.relax1", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/CHG.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/CHGCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/CONTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/DOSCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/EIGENVAL.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/INCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/KPOINTS.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/OUTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC013.CAB/POSCAR.relax1.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/CHG.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/CHGCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/CONTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/DOSCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/EIGENVAL.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/INCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/KPOINTS.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/OUTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/POSCAR.relax1.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC013.CAB/vasprun.xml.relax1", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506018, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 139 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "2TIEJZHCINQKA3RW52V7CYLXTVD2HZGXJBU2BV7LGP76OCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "Tl2SnAg" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avr2", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506021", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gWGK1JBXx3OD3cHewyDnM_wArEdux", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/vasprun.xml.relax2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/CHG.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/CHGCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/CONTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/DOSCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/EIGENVAL.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/INCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/KPOINTS.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/OUTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.ABC/POSCAR.relax2.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/CHG.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/CHGCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/CONTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/DOSCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/EIGENVAL.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/INCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/KPOINTS.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/OUTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/POSCAR.relax2.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.ABC/vasprun.xml.relax2", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506021, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 65 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "orthorhombic" + ], + "repository_checksum": "ZYTKKS2K6FZTJM5B4U5J7XMATBP6ZRTBFK6JJERDHA66ECI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "TlSnAg2" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avr5", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506022", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gvvel66ot1HiVN1LufST5WUWZ2t8K", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/vasprun.xml.relax1", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/CHG.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/CHGCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/CONTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/DOSCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/EIGENVAL.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/INCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/KPOINTS.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/OUTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC015.BCA/POSCAR.relax1.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/CHG.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/CHGCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/CONTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/DOSCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/EIGENVAL.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/INCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/KPOINTS.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/OUTCAR.relax1.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/POSCAR.relax1.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC015.BCA/vasprun.xml.relax1", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506022, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 65 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "orthorhombic" + ], + "repository_checksum": "ISXGCXRJOWV3MCCR5WILUXKFTD5LJLGVR4RDMNF46YPHGCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "TlSn2Ag" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avr6", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506031", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "ggrNZUdog7TC1060EzCsDuagBTxNN", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/vasprun.xml.relax2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/CHG.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/CHGCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/CONTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/DOSCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/EIGENVAL.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/INCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/KPOINTS.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/OUTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnTl_d/TFCC016.CAB/POSCAR.relax2.bz2" + ], + "secondary_file_uris": [ + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/CHG.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/CHGCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/CONTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/DOSCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/EIGENVAL.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/INCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/KPOINTS.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/OUTCAR.relax2.bz2", + "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/POSCAR.relax2.bz2" + ], + "main_file_uri": "nmd://Re2j2mzMtvO08obtU3op-6fd1v7yl/data/AgSnTl_d/TFCC016.CAB/vasprun.xml.relax2", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506031, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "Tl" + ], + "repository_spacegroup_nr": [ + 123 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "YWUJLRKDVHVCPFYW6T2D2XV7IUXGBEX6RFBC2HMWZKI36CI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "Tl2SnAg" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avrf", + "repository_archive_gid": [ + "Re2j2mzMtvO08obtU3op-6fd1v7yl" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506033", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "gQ91ORbXW-kxlqmPVqqa9PnIEngF8", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/vasprun.xml.relax2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/CHG.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/CHGCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/CONTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/DOSCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/EIGENVAL.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/INCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/KPOINTS.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/OUTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.ABC/POSCAR.relax2.bz2" + ], + "secondary_file_uris": [ + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/CHG.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/CHGCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/CONTCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/DOSCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/EIGENVAL.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/INCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/KPOINTS.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/OUTCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/POSCAR.relax2.bz2" + ], + "main_file_uri": "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.ABC/vasprun.xml.relax2", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506033, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "V" + ], + "repository_spacegroup_nr": [ + 99 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "XRHLXUZL26HPJ2NQGUM7A5ZTZMEWZIFEXTCR5G4PAXZCKCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "SnAg2V" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avrh", + "repository_archive_gid": [ + "RB1HuRB75DziRkYLw2loQ5HBBerDR" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506035", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "grM4KBBhHBnPMVo7mm8YjR-kbzrA0", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/vasprun.xml.relax2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/CHG.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/CHGCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/CONTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/DOSCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/EIGENVAL.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/INCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/KPOINTS.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/OUTCAR.relax2.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.BCA/POSCAR.relax2.bz2" + ], + "secondary_file_uris": [ + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/CHG.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/CHGCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/CONTCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/DOSCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/EIGENVAL.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/INCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/KPOINTS.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/OUTCAR.relax2.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/POSCAR.relax2.bz2" + ], + "main_file_uri": "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.BCA/vasprun.xml.relax2", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506035, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "V" + ], + "repository_spacegroup_nr": [ + 99 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "6H4ELR3CI62MTU32OYTUKPZGS74FXQ4ASB3WVSQBAWVSICI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "Sn2AgV" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avrj", + "repository_archive_gid": [ + "RB1HuRB75DziRkYLw2loQ5HBBerDR" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + }, + { + "_index": "index2018-11-14", + "_type": "repository_calculation", + "_id": "3506036", + "_score": 1.0, + "_source": { + "section_repository_info": { + "repository_grouping_checksum": "glj1RLL0DacCCtlvEboognORlpKHp", + "repository_filepaths": [ + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/vasprun.xml.relax1", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/CHG.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/CHGCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/CONTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/DOSCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/EIGENVAL.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/INCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/KPOINTS.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/OUTCAR.relax1.bz2", + "$EXTRACTED/ftp_upload_for_uid_125/aflowlib_data/LIB3_LIB/AgSnV_sv/TBCC006.CAB/POSCAR.relax1.bz2" + ], + "secondary_file_uris": [ + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/CHG.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/CHGCAR.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/CONTCAR.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/DOSCAR.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/EIGENVAL.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/INCAR.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/KPOINTS.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/OUTCAR.relax1.bz2", + "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/POSCAR.relax1.bz2" + ], + "main_file_uri": "nmd://RB1HuRB75DziRkYLw2loQ5HBBerDR/data/AgSnV_sv/TBCC006.CAB/vasprun.xml.relax1", + "section_repository_userdata": { + "section_citation": [ + { + "citation_repo_id": 52, + "citation_value": "http://aflowlib.org" + }, + { + "citation_repo_id": 53, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025614003322" + }, + { + "citation_repo_id": 54, + "citation_value": "http://www.sciencedirect.com/science/article/pii/S0927025612000687" + } + ], + "repository_access_now": "Open", + "section_author_info": [ + { + "author_repo_id": 134, + "author_first_name": "Frisco", + "author_last_name": "Rose", + "author_name": "Frisco Rose" + }, + { + "author_repo_id": 135, + "author_first_name": "Richard", + "author_last_name": "Taylor", + "author_name": "Richard Taylor" + }, + { + "author_repo_id": 136, + "author_first_name": "Ohad", + "author_last_name": "Levy", + "author_name": "Ohad Levy" + }, + { + "author_repo_id": 137, + "author_first_name": "Cormac", + "author_last_name": "Toher", + "author_name": "Cormac Toher" + }, + { + "author_repo_id": 138, + "author_first_name": "Marco Buongiorno", + "author_last_name": "Nardelli", + "author_name": "Marco Buongiorno Nardelli" + }, + { + "author_repo_id": 145, + "author_first_name": "Wahyu", + "author_last_name": "Setyawan", + "author_name": "Wahyu Setyawan" + }, + { + "author_repo_id": 146, + "author_first_name": "Shidong", + "author_last_name": "Wang", + "author_name": "Shidong Wang" + }, + { + "author_repo_id": 147, + "author_first_name": "Junkai", + "author_last_name": "Xue", + "author_name": "Junkai Xue" + }, + { + "author_repo_id": 148, + "author_first_name": "Kesong", + "author_last_name": "Yang", + "author_name": "Kesong Yang" + }, + { + "author_repo_id": 149, + "author_first_name": "Lance", + "author_last_name": "Nelson", + "author_name": "Lance Nelson" + }, + { + "author_repo_id": 150, + "author_first_name": "Gus", + "author_last_name": "Hart", + "author_name": "Gus Hart" + }, + { + "author_repo_id": 151, + "author_first_name": "Stefano", + "author_last_name": "Sanvito", + "author_name": "Stefano Sanvito" + }, + { + "author_repo_id": 152, + "author_first_name": "Natalio", + "author_last_name": "Mingo", + "author_name": "Natalio Mingo" + }, + { + "author_repo_id": 125, + "author_first_name": "Stefano", + "author_last_name": "Curtarolo", + "author_name": "Stefano Curtarolo" + } + ] + }, + "repository_calc_id": 3506036, + "upload_id": 967, + "section_repository_parserdata": { + "repository_code_version": [ + "VASP 4.6.35" + ], + "repository_atomic_elements": [ + "Ag", + "Sn", + "V" + ], + "repository_spacegroup_nr": [ + 99 + ], + "repository_system_type": [ + "Bulk" + ], + "repository_basis_set_type": [ + "plane waves" + ], + "repository_crystal_system": [ + "tetragonal" + ], + "repository_checksum": "LFXP7M7US6WD7WEE2YEERH5CJQRSFKGPO7RBAXAA67KWWCI", + "repository_parser_id": "RepoBaseParser", + "repository_chemical_formula": [ + "SnAgV2" + ], + "repository_atomic_elements_count": 3, + "repository_xc_treatment": [ + "GGA" + ], + "repository_program_name": [ + "VASP" + ] + }, + "section_uploader_info": { + "uploader_username": "stefano", + "uploader_last_name": "Curtarolo", + "uploader_repo_id": 125, + "uploader_first_name": "Stefano" + }, + "upload_date": 1425207600000, + "repository_calc_pid": "3avrk", + "repository_archive_gid": [ + "RB1HuRB75DziRkYLw2loQ5HBBerDR" + ] + }, + "query_auxiliary_data": { + "value_counts": { + "section_repository_info": { + "repository_grouping_checksum": 1, + "repository_filepaths": 10, + "secondary_file_uris": 9, + "main_file_uri": 1, + "section_repository_userdata": { + "section_citation": { + "citation_repo_id": 3, + "citation_value": 3 + }, + "repository_open_date": 0, + "repository_access_now": 1, + "repository_comment": 0, + "section_author_info": { + "author_name": 14, + "author_last_name": 14, + "author_first_name": 13, + "author_repo_id": 14 + } + }, + "repository_calc_id": 1, + "upload_id": 1, + "section_repository_parserdata": { + "repository_code_version": 1, + "repository_atomic_elements": 3, + "repository_spacegroup_nr": 1, + "repository_system_type": 1, + "repository_basis_set_type": 1, + "repository_crystal_system": 1, + "repository_checksum": 1, + "repository_parser_id": 1, + "repository_chemical_formula": 1, + "repository_atomic_elements_count": 1, + "repository_xc_treatment": 1, + "repository_program_name": 1 + }, + "section_uploader_info": { + "uploader_username": 1, + "uploader_last_name": 1, + "uploader_name": 0, + "uploader_repo_id": 1, + "uploader_first_name": 1 + }, + "upload_date": 1, + "repository_calc_pid": 1, + "repository_archive_gid": 1 + } + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/repository/utils.http b/repository/utils.http new file mode 100644 index 0000000000000000000000000000000000000000..afa0e7a6b892185f961a6baf27477642721e353a --- /dev/null +++ b/repository/utils.http @@ -0,0 +1,19 @@ +DELETE http://localhost:9200/index2018-11-15 HTTP/1.1; + +### +DELETE http://localhost:9200/topics2018-11-15 HTTP/1.1; + +### +GET http://localhost:9200/_cat/indices HTTP/1.1; + +### +GET http://localhost:9200/index2018-11-15/_search HTTP/1.1; + + +### +POST http://localhost:8111/repo/repo-update/index/calculation HTTP/1.1; +content-type: application/json + +{ + "calc_ids": [10] +} diff --git a/requirements.txt b/requirements.txt index f2e266a72df3a2b35492d90142015bc8323c6ae8..a143a738754b588fedcaaafc92c4d69d3f1302ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,3 +24,6 @@ sphinxcontrib.httpdomain sphinx_rtd_theme zipstream bagit +psycopg2 +sqlalchemy +bcrypt diff --git a/tests/conftest.py b/tests/conftest.py index 3f711d14697d6b16fbe623a2811493ca9fbd02c4..06aa62d576dbac567d6519d8387c27768c790a10 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -81,7 +81,6 @@ def mockmongo(monkeypatch): disconnect() connection = connect('test_db', host='mongomock://localhost') monkeypatch.setattr('nomad.infrastructure.setup_mongo', lambda **kwargs: None) - user.ensure_test_users() yield @@ -94,6 +93,22 @@ def elastic(): assert infrastructure.elastic_client is not None +@pytest.fixture(scope='session') +def repository_db(): + infrastructure.setup_repository_db() + assert infrastructure.repository_db is not None + + +@pytest.fixture(scope='session') +def test_user(repository_db): + return user.ensure_test_user(email='sheldon.cooper@nomad-fairdi.tests.de') + + +@pytest.fixture(scope='session') +def other_test_user(repository_db): + return user.ensure_test_user(email='leonard.hofstadter@nomad-fairdi.tests.de') + + @pytest.fixture(scope='function') def mocksearch(monkeypatch): uploads_by_hash = {} diff --git a/tests/processing/test_data.py b/tests/processing/test_data.py index fd69a6728a34d10070300509e40e57009f570eed..14033040c459b7059206d1513ed81b72855b9393 100644 --- a/tests/processing/test_data.py +++ b/tests/processing/test_data.py @@ -64,8 +64,8 @@ def uploaded_id_with_warning(request, clear_files) -> Generator[str, None, None] yield example_upload_id -def run_processing(uploaded_id: str) -> Upload: - upload = Upload.create(upload_id=uploaded_id, user=user.me) +def run_processing(uploaded_id: str, test_user) -> Upload: + upload = Upload.create(upload_id=uploaded_id, user=test_user) upload.upload_time = datetime.now() assert upload.status == 'RUNNING' @@ -110,33 +110,33 @@ def assert_processing(upload: Upload, mocksearch=None): @pytest.mark.timeout(30) -def test_processing(uploaded_id, worker, mocksearch, no_warn): - upload = run_processing(uploaded_id) +def test_processing(uploaded_id, worker, mocksearch, test_user, no_warn): + upload = run_processing(uploaded_id, test_user) assert_processing(upload, mocksearch) @pytest.mark.timeout(30) -def test_processing_with_warning(uploaded_id_with_warning, worker, mocksearch): - upload = run_processing(uploaded_id_with_warning) +def test_processing_with_warning(uploaded_id_with_warning, worker, test_user, mocksearch): + upload = run_processing(uploaded_id_with_warning, test_user) assert_processing(upload, mocksearch) @pytest.mark.parametrize('uploaded_id', [example_files[1]], indirect=True) -def test_processing_doublets(uploaded_id, worker, with_error): +def test_processing_doublets(uploaded_id, worker, test_user, with_error): - upload = run_processing(uploaded_id) + upload = run_processing(uploaded_id, test_user) assert upload.status == 'SUCCESS' assert RepoCalc.upload_exists(upload.upload_hash) # pylint: disable=E1101 - upload = run_processing(uploaded_id) + upload = run_processing(uploaded_id, test_user) assert upload.status == 'FAILURE' assert len(upload.errors) > 0 assert 'already' in upload.errors[0] @pytest.mark.timeout(30) -def test_process_non_existing(worker, with_error): - upload = run_processing('__does_not_exist') +def test_process_non_existing(worker, test_user, with_error): + upload = run_processing('__does_not_exist', test_user) assert upload.completed assert upload.current_task == 'extracting' @@ -146,7 +146,7 @@ def test_process_non_existing(worker, with_error): @pytest.mark.parametrize('task', ['extracting', 'parse_all', 'cleanup', 'parsing']) @pytest.mark.timeout(30) -def test_task_failure(monkeypatch, uploaded_id, worker, task, with_error): +def test_task_failure(monkeypatch, uploaded_id, worker, task, test_user, with_error): # mock the task method to through exceptions if hasattr(Upload, task): cls = Upload @@ -163,7 +163,7 @@ def test_task_failure(monkeypatch, uploaded_id, worker, task, with_error): monkeypatch.setattr('nomad.processing.data.%s.%s' % (cls.__name__, task), mock) # run the test - upload = run_processing(uploaded_id) + upload = run_processing(uploaded_id, test_user) assert upload.completed diff --git a/tests/test_api.py b/tests/test_api.py index 6e6624fbe13cf0643b87e9d7290eb6815fae28c5..e68e43ed5226601c388e6bf668951bfe3f6fed48 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -32,7 +32,7 @@ from tests.test_repo import example_elastic_calc # noqa pylint: disable=unused- @pytest.fixture(scope='function') -def client(mockmongo): +def client(mockmongo, repository_db): disconnect() connect('users_test', host=config.mongo.host, port=config.mongo.port, is_mock=True) @@ -43,18 +43,23 @@ def client(mockmongo): Upload._get_collection().drop() -@pytest.fixture(scope='session') -def test_user_auth(): +def create_auth_headers(user): + basic_auth_str = '%s:password' % user.email + basic_auth_bytes = basic_auth_str.encode('utf-8') + basic_auth_base64 = base64.b64encode(basic_auth_bytes).decode('utf-8') return { - 'Authorization': 'Basic %s' % base64.b64encode(b'me@gmail.com:nomad').decode('utf-8') + 'Authorization': 'Basic %s' % basic_auth_base64 } @pytest.fixture(scope='session') -def test_other_user_auth(): - return { - 'Authorization': 'Basic %s' % base64.b64encode(b'other@gmail.com:nomad').decode('utf-8') - } +def test_user_auth(test_user): + return create_auth_headers(test_user) + + +@pytest.fixture(scope='session') +def test_other_user_auth(other_test_user): + return create_auth_headers(other_test_user) def assert_uploads(upload_json_str, count=0, **kwargs): diff --git a/tests/test_repo.py b/tests/test_repo.py index d789890029fbea38ff7ab7c4e32356e67a52cdbd..ef572bf29a59028de3fa546f9abc4082c71e5768 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -27,7 +27,7 @@ from tests.test_parsing import parsed_template_example # pylint: disable=unused @pytest.fixture(scope='function') -def example_elastic_calc(normalized_template_example: LocalBackend, elastic) \ +def example_elastic_calc(normalized_template_example: LocalBackend, elastic, test_user) \ -> Generator[RepoCalc, None, None]: upload_file = UploadFile('test_upload_id', local_path=example_file) @@ -49,7 +49,7 @@ def example_elastic_calc(normalized_template_example: LocalBackend, elastic) \ additional=dict( mainfile=mainfile, upload_time=datetime.now(), - staging=True, restricted=False, user_id='me@gmail.com', + staging=True, restricted=False, user_id=test_user.email, aux_files=auxfiles), refresh='true') @@ -82,7 +82,7 @@ def test_create_elastic_calc(example_elastic_calc: RepoCalc, no_warn): def test_create_existing_elastic_calc( - example_elastic_calc: RepoCalc, normalized_template_example): + example_elastic_calc: RepoCalc, normalized_template_example, test_user): try: RepoCalc.create_from_backend( normalized_template_example, @@ -92,7 +92,7 @@ def test_create_existing_elastic_calc( additional=dict( mainfile='/test/mainfile', upload_time=datetime.now(), - staging=True, restricted=False, user_id='me'), + staging=True, restricted=False, user_id=test_user.email), refresh='true') assert False except AlreadyExists: diff --git a/tests/test_user.py b/tests/test_user.py new file mode 100644 index 0000000000000000000000000000000000000000..84b0b200bf6051bd5811adb65b79546ab015f909 --- /dev/null +++ b/tests/test_user.py @@ -0,0 +1,16 @@ +from nomad.user import User + + +def assert_user(user, reference): + assert user is not None + assert user.user_id == reference.user_id + + +def test_token_authorize(test_user): + user = User.verify_auth_token(test_user.email) + assert_user(user, test_user) + + +def test_password_authorize(test_user): + user = User.verify_user_password(test_user.email, 'password') + assert_user(user, test_user)