|
| 1 | +import tempfile |
1 | 2 | from dataclasses import asdict |
| 3 | +from pathlib import Path |
2 | 4 | from typing import List |
3 | 5 |
|
4 | 6 | import orjson |
5 | | -from flask import jsonify, request |
| 7 | +from flask import jsonify, make_response, request |
6 | 8 | from gena import generate_api |
7 | 9 | from gena.deserializer import get_dataclass_deserializer |
| 10 | +from sm.dataset import Dataset, Example, FullTable |
| 11 | +from sm.prelude import I, M, O |
| 12 | +from werkzeug.exceptions import BadRequest |
| 13 | + |
8 | 14 | from sand.controllers.helpers.upload import ( |
9 | 15 | ALLOWED_EXTENSIONS, |
10 | 16 | CSVParserOpts, |
|
14 | 20 | parse_upload, |
15 | 21 | save_upload, |
16 | 22 | ) |
| 23 | +from sand.controllers.table import get_friendly_fs_name |
17 | 24 | from sand.models import Project |
18 | | -from werkzeug.exceptions import BadRequest |
| 25 | +from sand.models.semantic_model import SemanticModel |
| 26 | +from sand.models.table import Table, TableRow |
19 | 27 |
|
20 | 28 | project_bp = generate_api(Project) |
21 | 29 |
|
@@ -127,3 +135,60 @@ def upload(id: int): |
127 | 135 | ], |
128 | 136 | } |
129 | 137 | ) |
| 138 | + |
| 139 | + |
| 140 | +@project_bp.route(f"/{project_bp.name}/<id>/export", methods=["GET"]) |
| 141 | +def export(id: int): |
| 142 | + """Export tables from the project""" |
| 143 | + try: |
| 144 | + project = Project.get_by_id(id) |
| 145 | + except: |
| 146 | + raise BadRequest("Project not found") |
| 147 | + |
| 148 | + examples = [] |
| 149 | + for tbl in Table.select().where(Table.project == project): |
| 150 | + tblrows = list(TableRow.select().where(TableRow.table == tbl)) |
| 151 | + basetbl = I.ColumnBasedTable.from_rows( |
| 152 | + records=[row.row for row in tblrows], |
| 153 | + table_id=tbl.name, |
| 154 | + headers=tbl.columns, |
| 155 | + strict=True, |
| 156 | + ) |
| 157 | + table = FullTable( |
| 158 | + table=basetbl, |
| 159 | + context=( |
| 160 | + I.Context( |
| 161 | + page_title=tbl.context_page.title, |
| 162 | + page_url=tbl.context_page.url, |
| 163 | + entities=( |
| 164 | + [I.EntityId(tbl.context_page.entity, "")] |
| 165 | + if tbl.context_page.entity is not None |
| 166 | + else [] |
| 167 | + ), |
| 168 | + ) |
| 169 | + if tbl.context_page is not None |
| 170 | + else I.Context() |
| 171 | + ), |
| 172 | + links=M.Matrix.default(basetbl.shape(), list), |
| 173 | + ) |
| 174 | + table.links = table.links.map_index( |
| 175 | + lambda ri, ci: tblrows[ri].links.get(ci, []) |
| 176 | + ) |
| 177 | + |
| 178 | + ex = Example(id=table.table.table_id, sms=[], table=table) |
| 179 | + |
| 180 | + for sm in SemanticModel.select().where(SemanticModel.table == tbl): |
| 181 | + ex.sms.append(sm.data) |
| 182 | + |
| 183 | + examples.append(ex) |
| 184 | + |
| 185 | + with tempfile.NamedTemporaryFile(suffix=".zip") as file: |
| 186 | + Dataset(Path(file.name)).save(examples, table_fmt_indent=2) |
| 187 | + dataset = Path(file.name).read_bytes() |
| 188 | + |
| 189 | + resp = make_response(dataset) |
| 190 | + resp.headers["Content-Type"] = "application/zip; charset=utf-8" |
| 191 | + resp.headers["Content-Disposition"] = ( |
| 192 | + f"attachment; filename={get_friendly_fs_name(str(project.name))}.zip" |
| 193 | + ) |
| 194 | + return resp |
0 commit comments