Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""WSGI Flask-app for browsing a database.
3::
5 +---------------------+
6 | layout.html |
7 | +-----------------+ | +--------------+
8 | | search.html | | | layout.html |
9 | | + | | | +---------+ |
10 | | table.html ----------->| |row.html | |
11 | | | | | +---------+ |
12 | +-----------------+ | +--------------+
13 +---------------------+
15You can launch Flask's local webserver like this::
17 $ ase db abc.db -w
19or this::
21 $ python3 -m ase.db.app abc.db
23"""
25import io
26import sys
27from typing import Dict, Any, Set
28from pathlib import Path
30from flask import Flask, render_template, request
32from ase.db import connect
33from ase.db.core import Database
34from ase.formula import Formula
35from ase.db.web import create_key_descriptions, Session
36from ase.db.row import row2dct, AtomsRow
37from ase.db.table import all_columns
40root = Path(__file__).parent.parent.parent
41app = Flask(__name__, template_folder=str(root))
43projects: Dict[str, Dict[str, Any]] = {}
46@app.route('/', defaults={'project_name': 'default'})
47@app.route('/<project_name>')
48@app.route('/<project_name>/')
49def search(project_name: str):
50 """Search page.
52 Contains input form for database query and a table result rows.
53 """
54 if project_name == 'favicon.ico':
55 return '', 204, [] # 204: "No content"
56 session = Session(project_name)
57 project = projects[project_name]
58 return render_template(project['search_template'],
59 q=request.args.get('query', ''),
60 p=project,
61 session_id=session.id)
64@app.route('/update/<int:sid>/<what>/<x>/')
65def update(sid: int, what: str, x: str):
66 """Update table of rows inside search page.
68 ``what`` must be one of:
70 * query: execute query in request.args (x not used)
71 * limit: set number of rows to show to x
72 * toggle: toggle column x
73 * sort: sort after column x
74 * page: show page x
75 """
76 session = Session.get(sid)
77 project = projects[session.project_name]
78 session.update(what, x, request.args, project)
79 table = session.create_table(project['database'],
80 project['uid_key'],
81 keys=list(project['key_descriptions']))
82 return render_template(project['table_template'],
83 t=table,
84 p=project,
85 s=session)
88@app.route('/<project_name>/row/<uid>')
89def row(project_name: str, uid: str):
90 """Show details for one database row."""
91 project = projects[project_name]
92 uid_key = project['uid_key']
93 row = project['database'].get('{uid_key}={uid}'
94 .format(uid_key=uid_key, uid=uid))
95 dct = project['row_to_dict_function'](row, project)
96 return render_template(project['row_template'],
97 d=dct, row=row, p=project, uid=uid)
100@app.route('/atoms/<project_name>/<int:id>/<type>')
101def atoms(project_name: str, id: int, type: str):
102 """Return atomic structure as cif, xyz or json."""
103 row = projects[project_name]['database'].get(id=id)
104 a = row.toatoms()
105 if type == 'cif':
106 b = io.BytesIO()
107 a.pbc = True
108 a.write(b, 'cif', wrap=False)
109 return b.getvalue(), 200, []
111 fd = io.StringIO()
112 if type == 'xyz':
113 a.write(fd, 'xyz')
114 elif type == 'json':
115 con = connect(fd, type='json')
116 con.write(row,
117 data=row.get('data', {}),
118 **row.get('key_value_pairs', {}))
119 else:
120 1 / 0
122 headers = [('Content-Disposition',
123 'attachment; filename="{project_name}-{id}.{type}"'
124 .format(project_name=project_name, id=id, type=type))]
125 txt = fd.getvalue()
126 return txt, 200, headers
129@app.route('/gui/<int:id>')
130def gui(id: int):
131 """Pop ud ase gui window."""
132 from ase.visualize import view
133 atoms = projects['default']['database'].get_atoms(id)
134 view(atoms)
135 return '', 204, []
138@app.route('/test')
139def test():
140 from pyjokes import get_joke as j
141 return j()
144@app.route('/robots.txt')
145def robots():
146 return ('User-agent: *\n'
147 'Disallow: /\n'
148 '\n'
149 'User-agent: Baiduspider\n'
150 'Disallow: /\n'
151 '\n'
152 'User-agent: SiteCheck-sitecrawl by Siteimprove.com\n'
153 'Disallow: /\n',
154 200)
157def handle_query(args) -> str:
158 """Converts request args to ase.db query string."""
159 return args['query']
162def row_to_dict(row: AtomsRow, project: Dict[str, Any]) -> Dict[str, Any]:
163 """Convert row to dict for use in html template."""
164 dct = row2dct(row, project['key_descriptions'])
165 dct['formula'] = Formula(Formula(row.formula).format('abc')).format('html')
166 return dct
169def add_project(db: Database) -> None:
170 """Add database to projects with name 'default'."""
171 all_keys: Set[str] = set()
172 for row in db.select(columns=['key_value_pairs'], include_data=False):
173 all_keys.update(row._keys)
175 key_descriptions = {key: (key, '', '') for key in all_keys}
177 meta: Dict[str, Any] = db.metadata
179 if 'key_descriptions' in meta:
180 key_descriptions.update(meta['key_descriptions'])
182 default_columns = meta.get('default_columns')
183 if default_columns is None:
184 default_columns = all_columns[:]
186 projects['default'] = {
187 'name': 'default',
188 'title': meta.get('title', ''),
189 'uid_key': 'id',
190 'key_descriptions': create_key_descriptions(key_descriptions),
191 'database': db,
192 'row_to_dict_function': row_to_dict,
193 'handle_query_function': handle_query,
194 'default_columns': default_columns,
195 'search_template': 'ase/db/templates/search.html',
196 'row_template': 'ase/db/templates/row.html',
197 'table_template': 'ase/db/templates/table.html'}
200if __name__ == '__main__':
201 db = connect(sys.argv[1])
202 add_project(db)
203 app.run(host='0.0.0.0', debug=True)