From 0f3e4ef1de18dd8559f09870f4bfed270a788edb Mon Sep 17 00:00:00 2001
From: Markus Scheidgen <markus.scheidgen@gmail.com>
Date: Sat, 25 Aug 2018 08:25:10 +0200
Subject: [PATCH] Added api and json gui for list all repo calcs.

---
 gui/src/api.js             |  8 +++++-
 gui/src/components/App.js  |  3 ++-
 gui/src/components/Repo.js | 55 ++++++++++++++++++++++++++++++++++++++
 nomad/api.py               | 42 ++++++++++++++++++++++++++---
 nomad/search.py            |  6 ++++-
 tests/test_api.py          | 14 ++++++++--
 6 files changed, 119 insertions(+), 9 deletions(-)
 create mode 100644 gui/src/components/Repo.js

diff --git a/gui/src/api.js b/gui/src/api.js
index f7dec4856d..1379c9b3f4 100644
--- a/gui/src/api.js
+++ b/gui/src/api.js
@@ -66,11 +66,17 @@ function repo(uploadHash, calcHash) {
     .then(response => response.json())
 }
 
+function repoAll(uploadHash, calcHash) {
+  return fetch(`${apiBase}/repo`)
+    .then(response => response.json())
+}
+
 const api = {
   createUpload: createUpload,
   getUploads: getUploads,
   archive: archive,
-  repo: repo
+  repo: repo,
+  repoAll: repoAll,
 };
 
 export default api;
\ No newline at end of file
diff --git a/gui/src/components/App.js b/gui/src/components/App.js
index 23838bcc1e..b8781cfe30 100644
--- a/gui/src/components/App.js
+++ b/gui/src/components/App.js
@@ -6,6 +6,7 @@ import { BrowserRouter, Switch, Route } from 'react-router-dom';
 import Uploads from './Uploads'
 import ArchiveCalc from './ArchiveCalc';
 import RepoCalc from './RepoCalc';
+import Repo from './Repo';
 
 function App() {
   return (
@@ -14,7 +15,7 @@ function App() {
         <Navigation>
           <Switch>
             <Route exact path="/" render={() => <div>Home</div>} />
-            <Route exact path="/repo" render={() => <div>Browse</div>} />
+            <Route exact path="/repo" component={Repo} />
             <Route path="/repo/:uploadHash/:calcHash" component={RepoCalc} />
             <Route path="/upload" component={Uploads} />
             <Route exact path="/archive" render={() => <div>Archive</div>} />
diff --git a/gui/src/components/Repo.js b/gui/src/components/Repo.js
new file mode 100644
index 0000000000..cb67835267
--- /dev/null
+++ b/gui/src/components/Repo.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles, Paper, LinearProgress } from '@material-ui/core';
+import ReactJson from 'react-json-view'
+import api from '../api';
+import Markdown from './Markdown';
+
+
+class Repo extends React.Component {
+  static propTypes = {
+    classes: PropTypes.object.isRequired
+  }
+  static styles = theme => ({
+    root: {},
+    calcData: {
+      padding: theme.spacing.unit
+    }
+  });
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      data: null
+    }
+  }
+
+  componentDidMount() {
+    api.repoAll().then(data => {
+      this.setState({data: data})
+    })
+  }
+
+  render() {
+    const { classes } = this.props
+    const { data } = this.state
+
+    return (
+      <div className={classes.root}>
+        <Markdown>{`
+          ## The Repository – Raw Code Data
+        `}</Markdown>
+        <Paper className={classes.calcData}>
+          {
+            data ?
+              <ReactJson src={this.state.data} enableClipboard={false} collapsed={4} /> :
+              <LinearProgress variant="query" />
+          }
+        </Paper>
+      </div>
+
+    )
+  }
+}
+
+export default withStyles(Repo.styles)(Repo);
\ No newline at end of file
diff --git a/nomad/api.py b/nomad/api.py
index 2e8a003ceb..cd25efbbef 100644
--- a/nomad/api.py
+++ b/nomad/api.py
@@ -76,10 +76,11 @@ class Upload(Resource):
         return Uploads._render(upload), 200
 
 
-class Repo(Resource):
+class RepoCalc(Resource):
     @staticmethod
     def _render(data: dict):
-        if 'upload_time' in data:
+        upload_time = data.get('upload_time', None)
+        if upload_time is not None and isinstance(upload_time, datetime):
             data['upload_time'] = data['upload_time'].isoformat()
 
         return {key: value for key, value in data.items() if value is not None}
@@ -92,7 +93,39 @@ class Repo(Resource):
         except Exception as e:
             abort(500, message=str(e))
 
-        return Repo._render(data.to_dict()), 200
+        return RepoCalc._render(data.to_dict()), 200
+
+
+class RepoCalcs(Resource):
+    def get(self):
+        page = request.args.get('page', 1)
+        per_page = request.args.get('per_page', 10)
+
+        assert page >= 1
+        assert per_page > 0
+
+        body = {
+            'from': page - 1,
+            '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']]
+        }
 
 
 @app.route('/archive/<string:upload_hash>/<string:calc_hash>', methods=['GET'])
@@ -111,7 +144,8 @@ def get_calc(upload_hash, calc_hash):
 
 api.add_resource(Uploads, '/uploads')
 api.add_resource(Upload, '/uploads/<string:upload_id>')
-api.add_resource(Repo, '/repo/<string:upload_hash>/<string:calc_hash>')
+api.add_resource(RepoCalcs, '/repo')
+api.add_resource(RepoCalc, '/repo/<string:upload_hash>/<string:calc_hash>')
 
 
 if __name__ == '__main__':
diff --git a/nomad/search.py b/nomad/search.py
index 994b19a2fc..9c096be20a 100644
--- a/nomad/search.py
+++ b/nomad/search.py
@@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
 
 # ensure elastic connection
 if 'sphinx' not in sys.modules:
-    connections.create_connection(hosts=[config.elastic.host])
+    client = connections.create_connection(hosts=[config.elastic.host])
 
 
 key_mappings = {
@@ -66,6 +66,10 @@ class Calc(Document):
     class Index:
         name = config.elastic.calc_index
 
+    @staticmethod
+    def search(body):
+        return client.search(index=config.elastic.calc_index, body=body)
+
     @staticmethod
     def add_from_backend(backend: LocalBackend, **kwargs) -> 'Calc':
         """
diff --git a/tests/test_api.py b/tests/test_api.py
index 6e8f891608..d525bdec4f 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -153,16 +153,26 @@ def test_processing(client, file, celery_session_worker):
     assert_exists(config.files.uploads_bucket, upload['upload_id'])
 
 
-def test_get_repo(client, example_entry):
+def test_get_repo_calc(client, example_entry):
     rv = client.get('/repo/%s/%s' % (example_entry.upload_hash, example_entry.calc_hash))
     assert rv.status_code == 200
 
 
-def test_non_existing_repo(client):
+def test_non_existing_repo_cals(client):
     rv = client.get('/repo/doesnt/exist')
     assert rv.status_code == 404
 
 
+def test_get_repo_calcs(client, example_entry):
+    rv = client.get('/repo')
+    assert rv.status_code == 200
+    data = json.loads(rv.data)
+    results = data.get('results', None)
+    assert results is not None
+    assert isinstance(results, list)
+    assert len(results) >= 1
+
+
 def test_get_archive(client, archive_id):
     rv = client.get('/archive/%s' % archive_id)
     assert rv.status_code == 302
-- 
GitLab