|
| 1 | +import csv |
| 2 | +import io |
1 | 3 | import json |
2 | 4 | import math |
3 | 5 | import re |
4 | 6 |
|
5 | 7 | import cherrypy |
6 | 8 | from girder.api import access |
7 | 9 | from girder.api.describe import Description, autoDescribeRoute |
8 | | -from girder.api.rest import Resource, getApiUrl |
| 10 | +from girder.api.rest import Resource, getApiUrl, setRawResponse, setResponseHeader |
9 | 11 | from girder.constants import AccessType |
10 | 12 | from girder.exceptions import RestException |
11 | 13 | from girder.models.file import File |
|
18 | 20 | from girder_worker.girder_plugin.utils import getWorkerApiUrl |
19 | 21 | import pymongo |
20 | 22 |
|
21 | | -from dive_utils import FALSY_META_VALUES, TRUTHY_META_VALUES |
| 23 | +from dive_utils import FALSY_META_VALUES, TRUTHY_META_VALUES, setContentDisposition |
22 | 24 | from dive_utils.constants import ( |
23 | 25 | DIVEMetadataClonedFilter, |
24 | 26 | DIVEMetadataClonedFilterBase, |
@@ -147,6 +149,7 @@ def __init__(self, resourceName): |
147 | 149 | ), |
148 | 150 | self.run_slicer_cli_task, |
149 | 151 | ) |
| 152 | + self.route("POST", (":id", "export"), self.export_metadata) |
150 | 153 |
|
151 | 154 | @access.user |
152 | 155 | @autoDescribeRoute( |
@@ -1105,3 +1108,65 @@ def run_slicer_cli_task( |
1105 | 1108 | job = Job().save(job) |
1106 | 1109 | Job().scheduleJob(job) |
1107 | 1110 | return job |
| 1111 | + |
| 1112 | + @access.user |
| 1113 | + @autoDescribeRoute( |
| 1114 | + Description("Export filtered DIVE metadata as JSON or CSV") |
| 1115 | + .modelParam( |
| 1116 | + "id", |
| 1117 | + description="Base root Folder to filter on", |
| 1118 | + model=Folder, |
| 1119 | + level=AccessType.READ, |
| 1120 | + ) |
| 1121 | + .jsonParam( |
| 1122 | + "filters", |
| 1123 | + "JSON Settings for the filtering", |
| 1124 | + required=False, |
| 1125 | + ) |
| 1126 | + .param( |
| 1127 | + "format", |
| 1128 | + description="Export format: 'json' or 'csv'", |
| 1129 | + required=True, |
| 1130 | + enum=["json", "csv"], |
| 1131 | + ) |
| 1132 | + ) |
| 1133 | + def export_metadata(self, folder, filters, format): |
| 1134 | + if folder['meta'].get(DIVEMetadataMarker, False) is False: |
| 1135 | + raise RestException('Folder is not a DIVE Metadata folder', code=404) |
| 1136 | + |
| 1137 | + user = self.getCurrentUser() |
| 1138 | + query = self.get_filter_query(folder, user, filters) |
| 1139 | + metadata_items = list(DIVE_Metadata().find(query, user=user)) |
| 1140 | + |
| 1141 | + filename = f"metadata_export.{format}" |
| 1142 | + if not metadata_items: |
| 1143 | + raise RestException('No metadata items to export.') |
| 1144 | + |
| 1145 | + if format == 'csv': |
| 1146 | + output = io.StringIO(newline='') |
| 1147 | + # Infer CSV headers from all keys used across items |
| 1148 | + headers = sorted( |
| 1149 | + {key for item in metadata_items for key in item.get('metadata', {}).keys()} |
| 1150 | + ) |
| 1151 | + |
| 1152 | + writer = csv.DictWriter(output, fieldnames=headers, extrasaction='ignore') |
| 1153 | + writer.writeheader() |
| 1154 | + for item in metadata_items: |
| 1155 | + row = {key: item.get('metadata', {}).get(key, '') for key in headers} |
| 1156 | + writer.writerow(row) |
| 1157 | + |
| 1158 | + csv_output = output.getvalue() |
| 1159 | + output.close() |
| 1160 | + |
| 1161 | + setRawResponse() |
| 1162 | + setContentDisposition(filename, mime='text/csv') |
| 1163 | + setResponseHeader('Content-Type', 'text/csv') |
| 1164 | + return csv_output.encode('utf-8') |
| 1165 | + |
| 1166 | + else: # JSON |
| 1167 | + export_data = [item['metadata'] for item in metadata_items] |
| 1168 | + setContentDisposition(filename, mime='application/json') |
| 1169 | + setRawResponse() |
| 1170 | + |
| 1171 | + setResponseHeader('Content-Type', 'application/json') |
| 1172 | + return json.dumps(export_data).encode('utf-8') |
0 commit comments