api.py 4.97 KB
Newer Older
1
from flask import Flask, request, redirect
2
3
4
from flask_restful import Resource, Api, abort
from datetime import datetime
import mongoengine.errors
5
6
from flask_cors import CORS
import logging
7
from elasticsearch.exceptions import NotFoundError
8

Markus Scheidgen's avatar
Markus Scheidgen committed
9
from nomad import users, files, search, config
Markus Scheidgen's avatar
Markus Scheidgen committed
10
from nomad.processing import UploadProc
11
from nomad.utils import get_logger
12

Markus Scheidgen's avatar
Markus Scheidgen committed
13
14
base_path = config.services.api_base_path

15
app = Flask(__name__, static_url_path='/docs', static_folder='../docs/.build/html')
16
CORS(app)
17
18
19
api = Api(app)


20
# provid a fake user for testing
21
22
23
24
25
26
27
28
29
30
me = users.User.objects(email='me@gmail.com').first()
if me is None:
    me = users.User(email='me@gmail.com', name='Me Meyer')
    me.save()


class Uploads(Resource):

    @staticmethod
    def _render(upload: users.Upload):
Markus Scheidgen's avatar
Markus Scheidgen committed
31
32
33
34
35
36
        if upload.proc:
            proc = UploadProc(**upload.proc)
            proc.update_from_backend()
        else:
            proc = None

37
        data = {
38
            'name': upload.name,
39
            'upload_id': upload.upload_id,
40
41
            'presigned_url': upload.presigned_url,
            'create_time': upload.create_time.isoformat() if upload.create_time is not None else None,
42
            'upload_time': upload.upload_time.isoformat() if upload.upload_time is not None else None,
Markus Scheidgen's avatar
Markus Scheidgen committed
43
44
            'proc_time': upload.proc_time.isoformat() if upload.proc_time is not None else None,
            'proc': proc
45
46
47
48
49
50
51
52
        }

        return {key: value for key, value in data.items() if value is not None}

    def get(self):
        return [Uploads._render(user) for user in users.Upload.objects()], 200

    def post(self):
53
54
55
56
57
        json_data = request.get_json()
        if json_data is None:
            json_data = {}

        upload = users.Upload(user=me, name=json_data.get('name'))
58
59
60
61
        upload.save()

        upload.presigned_url = files.get_presigned_upload_url(upload.upload_id)
        upload.create_time = datetime.now()
Markus Scheidgen's avatar
Markus Scheidgen committed
62
        upload.proc = UploadProc(upload.upload_id)
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
        upload.save()

        return Uploads._render(upload), 200


class Upload(Resource):
    def get(self, upload_id):
        try:
            upload = users.Upload.objects(id=upload_id).first()
        except mongoengine.errors.ValidationError:
            abort(400, message='%s is not a valid upload id.' % upload_id)

        if upload is None:
            abort(404, message='Upload with id %s does not exist.' % upload_id)

        return Uploads._render(upload), 200


81
class RepoCalc(Resource):
82
83
    @staticmethod
    def _render(data: dict):
84
85
        upload_time = data.get('upload_time', None)
        if upload_time is not None and isinstance(upload_time, datetime):
86
87
88
89
            data['upload_time'] = data['upload_time'].isoformat()

        return {key: value for key, value in data.items() if value is not None}

90
91
92
    def get(self, upload_hash, calc_hash):
        try:
            data = search.Calc.get(id='%s/%s' % (upload_hash, calc_hash))
93
94
        except NotFoundError:
            abort(404, message='There is no calculation for %s/%s' % (upload_hash, calc_hash))
95
        except Exception as e:
96
            abort(500, message=str(e))
97

98
99
100
101
102
        return RepoCalc._render(data.to_dict()), 200


class RepoCalcs(Resource):
    def get(self):
103
104
105
        # 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))
106
107
108
109
110

        assert page >= 1
        assert per_page > 0

        body = {
111
            'from': (page - 1) * per_page,
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
            'size': per_page,
            'query': {
                'match_all': {}
            }
        }

        try:
            results = search.Calc.search(body=body)
        except Exception as e:
            get_logger(__name__).error('Could not execute repo calcs get.', exc_info=e)
            abort(500, message=str(e))

        return {
            'pagination': {
                'total': results['hits']['total'],
                'page': page,
                'per_page': per_page
            },
            'results': [RepoCalc._render(hit['_source']) for hit in results['hits']['hits']]
        }
132
133


Markus Scheidgen's avatar
Markus Scheidgen committed
134
@app.route('%s/archive/<string:upload_hash>/<string:calc_hash>' % base_path, methods=['GET'])
135
136
137
138
def get_calc(upload_hash, calc_hash):
    archive_id = '%s/%s' % (upload_hash, calc_hash)
    logger = get_logger(__name__, archive_id=archive_id)
    try:
139
140
        url = files.archive_url(archive_id)
        return redirect(url, 302)
141
142
143
    except KeyError:
        abort(404, message='Archive %s does not exist.' % archive_id)
    except Exception as e:
144
145
        logger.error('Exception on accessing archive', exc_info=e)
        abort(500, message='Could not accessing the archive.')
146
147


Markus Scheidgen's avatar
Markus Scheidgen committed
148
149
150
151
api.add_resource(Uploads, '%s/uploads' % base_path)
api.add_resource(Upload, '%s/uploads/<string:upload_id>' % base_path)
api.add_resource(RepoCalcs, '%s/repo' % base_path)
api.add_resource(RepoCalc, '%s/repo/<string:upload_hash>/<string:calc_hash>' % base_path)
152

153
154

if __name__ == '__main__':
155
    logging.basicConfig(level=logging.DEBUG)
156
    app.run(debug=True)