|
4 | 4 | import json
|
5 | 5 | import os
|
6 | 6 |
|
7 |
| -from flask import Flask, request, Response |
| 7 | +from utils import block_compute |
| 8 | + |
| 9 | +from flask import Flask, request, Response, send_file, jsonify |
8 | 10 | import numpy as np
|
9 | 11 | # from intern.remote.boss import BossRemote
|
10 | 12 | # from intern.resource.boss.resource import ChannelResource
|
11 | 13 | # from intern.utils.parallel import block_compute
|
12 | 14 | # from requests import codes, post
|
13 | 15 |
|
14 | 16 | APP = Flask(__name__)
|
| 17 | + |
15 | 18 | UPLOADS_PATH = "./uploads"
|
| 19 | +BLOCK_SIZE = (256, 256, 256) |
16 | 20 |
|
17 | 21 |
|
18 | 22 | """
|
19 | 23 | https://api.theboss.io/v1/cutout/:collection/:experiment/:channel/:resolution/:x_range/:y_range/:z_range/:time_range/?iso=:iso
|
20 | 24 | """
|
21 | 25 |
|
22 | 26 |
|
23 |
| -@APP.route("/v1/cutout/<collection>/<experiment>/<channel>/<resolution>/<x_range>/<y_range>/<z_range>/", methods=["POST"]) |
24 |
| -def upload_cutout_xyz(collection, experiment, channel, resolution, x_range, y_range, z_range): |
| 27 | +def file_compute(x_start, x_stop, |
| 28 | + y_start, y_stop, |
| 29 | + z_start, z_stop, |
| 30 | + origin=(0, 0, 0), |
| 31 | + block_size=(256, 256, 256)): |
25 | 32 | """
|
26 |
| - Upload a volume |
| 33 | + Which files do we need to pull for this box? |
| 34 | + """ |
| 35 | + # x |
| 36 | + |
| 37 | + x_block_origins = [ |
| 38 | + b |
| 39 | + for b in range(0, x_stop + block_size[0], block_size[0]) |
| 40 | + if b > (x_start - block_size[0]) and b < x_stop |
| 41 | + ] |
| 42 | + y_block_origins = [ |
| 43 | + b |
| 44 | + for b in range(0, y_stop + block_size[1], block_size[1]) |
| 45 | + if b > (y_start - block_size[1]) and b < y_stop |
| 46 | + ] |
| 47 | + z_block_origins = [ |
| 48 | + b |
| 49 | + for b in range(0, z_stop + block_size[2], block_size[2]) |
| 50 | + if b > (z_start - block_size[2]) and b < z_stop |
| 51 | + ] |
| 52 | + |
| 53 | + files = [] |
| 54 | + for x in x_block_origins: |
| 55 | + for y in y_block_origins: |
| 56 | + for z in z_block_origins: |
| 57 | + files.append((x, y, z)) |
| 58 | + return files |
| 59 | + |
| 60 | + |
| 61 | +def blockfile_indices(xs, ys, zs, origin=(0, 0, 0), block_size=(256, 256, 256)): |
| 62 | + blocks = block_compute( |
| 63 | + xs[0], xs[1], ys[0], ys[1], zs[0], zs[1], |
| 64 | + origin, block_size |
| 65 | + ) |
| 66 | + files = file_compute( |
| 67 | + xs[0], xs[1], ys[0], ys[1], zs[0], zs[1], |
| 68 | + origin, block_size |
| 69 | + ) |
| 70 | + |
| 71 | + inds = [] |
| 72 | + for b, f in zip(blocks, files): |
| 73 | + inds.append([ |
| 74 | + (b[0][0] - f[0], b[0][1] - f[0]), |
| 75 | + (b[1][0] - f[1], b[1][1] - f[1]), |
| 76 | + (b[2][0] - f[2], b[2][1] - f[2]) |
| 77 | + ]) |
27 | 78 |
|
| 79 | + return inds |
| 80 | + |
| 81 | + |
| 82 | + |
| 83 | +class StorageManager: |
28 | 84 | """
|
29 |
| - for _, f in request.files.items(): |
30 |
| - memfile = io.BytesIO() |
31 |
| - f.save(memfile) |
32 |
| - data = np.fromstring(memfile.getvalue(), dtype=np.uint8) |
| 85 | + Abstract class. |
| 86 | + """ |
| 87 | + pass |
| 88 | + |
| 89 | + |
| 90 | +class FilesystemStorageManager(StorageManager): |
| 91 | + |
| 92 | + def __init__(self, upload_path=UPLOADS_PATH, block_size=BLOCK_SIZE): |
| 93 | + self.upload_path = upload_path |
| 94 | + self.block_size = block_size |
| 95 | + |
| 96 | + def setdata(self, data, col, exp, chan, res, xs, ys, zs): |
| 97 | + """ |
| 98 | + Uploads the file. |
| 99 | + """ |
| 100 | + # Chunk the file into its parts |
| 101 | + blocks = block_compute( |
| 102 | + xs[0], xs[1], ys[0], ys[1], zs[0], zs[1], |
| 103 | + origin=(0, 0, 0), |
| 104 | + block_size=self.block_size |
| 105 | + ) |
| 106 | + |
| 107 | + for b in blocks: |
| 108 | + # Check to see if the file already exists |
| 109 | + |
| 110 | + # TODO: All of this assumes 0-padded block alignment |
| 111 | + self.store( |
| 112 | + data[b[0][0] - xs[0]: b[0][1] - xs[0], |
| 113 | + b[1][0] - ys[0]: b[1][1] - ys[0], |
| 114 | + b[2][0] - zs[0]: b[2][1] - zs[0]], |
| 115 | + col, exp, chan, res, b |
| 116 | + ) |
| 117 | + |
| 118 | + # if self._exists(col, exp, chan, res, b): |
| 119 | + # # Open file, add new data, write file |
| 120 | + # # data = self.read(col, exp, chan, res, b) |
| 121 | + # pass |
| 122 | + # else: |
| 123 | + # # Create the file with the appropriate contents |
| 124 | + # # and 0pad if necessary. |
| 125 | + # # pad0_data = np.zeros((self.block_size)) |
| 126 | + # pass |
| 127 | + |
| 128 | + def getdata(self, col, exp, chan, res, xs, ys, zs): |
| 129 | + """ |
| 130 | + Gets the data from disk. |
| 131 | + """ |
| 132 | + blocks = block_compute( |
| 133 | + xs[0], xs[1], ys[0], ys[1], zs[0], zs[1], |
| 134 | + origin=(0, 0, 0), |
| 135 | + block_size=self.block_size |
| 136 | + ) |
| 137 | + files = file_compute( |
| 138 | + xs[0], xs[1], ys[0], ys[1], zs[0], zs[1], |
| 139 | + origin=(0, 0, 0), |
| 140 | + block_size=self.block_size, |
| 141 | + ) |
| 142 | + indices = blockfile_indices( |
| 143 | + xs, ys, zs, |
| 144 | + origin=(0, 0, 0), |
| 145 | + block_size=self.block_size |
| 146 | + ) |
| 147 | + |
| 148 | + payload = np.zeros(( |
| 149 | + (xs[1] - xs[0]), |
| 150 | + (ys[1] - ys[0]), |
| 151 | + (zs[1] - zs[0]) |
| 152 | + ), dtype="uint8") |
| 153 | + for b, f, i in zip(blocks, files, indices): |
| 154 | + try: |
| 155 | + data_partial = self.retrieve(col, exp, chan, res, f)[ |
| 156 | + i[0][0]:i[0][1], |
| 157 | + i[1][0]:i[1][1], |
| 158 | + i[2][0]:i[2][1], |
| 159 | + ] |
| 160 | + payload[ |
| 161 | + f[0] + i[0][0]: f[0] + i[0][1], |
| 162 | + f[1] + i[1][0]: f[1] + i[1][1], |
| 163 | + f[2] + i[2][0]: f[2] + i[2][1], |
| 164 | + ] = data_partial |
| 165 | + |
| 166 | + except: |
| 167 | + payload[ |
| 168 | + f[0] + i[0][0]: f[0] + i[0][1], |
| 169 | + f[1] + i[1][0]: f[1] + i[1][1], |
| 170 | + f[2] + i[2][0]: f[2] + i[2][1], |
| 171 | + ] = np.zeros(self.block_size, dtype="uint8")[ |
| 172 | + i[0][0]:i[0][1], |
| 173 | + i[1][0]:i[1][1], |
| 174 | + i[2][0]:i[2][1], |
| 175 | + ] |
| 176 | + return payload |
| 177 | + |
| 178 | + def store(self, data, col, exp, chan, res, b): |
33 | 179 | os.makedirs("{}/{}/{}/{}/".format(
|
34 | 180 | UPLOADS_PATH,
|
35 |
| - collection, experiment, channel |
| 181 | + col, exp, chan |
36 | 182 | ), exist_ok=True)
|
37 |
| - np.save("{}/{}/{}/{}/{}-{}-{}-{}.npy".format( |
| 183 | + fname = "{}/{}/{}/{}/{}-{}-{}-{}.npy".format( |
38 | 184 | UPLOADS_PATH,
|
39 |
| - collection, experiment, channel, |
40 |
| - resolution, x_range, y_range, z_range, |
41 |
| - )) |
42 |
| - return "" |
| 185 | + col, exp, chan, |
| 186 | + res, b[0], b[1], b[2], |
| 187 | + ) |
| 188 | + # print(fname) |
| 189 | + return np.save(fname, data) |
43 | 190 |
|
| 191 | + def retrieve(self, col, exp, chan, res, b): |
| 192 | + fname = "{}/{}/{}/{}/{}-{}-{}-{}.npy".format( |
| 193 | + UPLOADS_PATH, |
| 194 | + col, exp, chan, |
| 195 | + res, |
| 196 | + (b[0], b[0] + self.block_size[0]), |
| 197 | + (b[1], b[1] + self.block_size[1]), |
| 198 | + (b[2], b[2] + self.block_size[2]), |
| 199 | + ) |
| 200 | + return np.load(fname) |
44 | 201 |
|
45 |
| -@APP.route("/v1/cutout/<collection>/<experiment>/<channel>/<resolution>/<x_range>/<y_range>/<z_range>/", methods=["GET"]) |
46 |
| -def get_cutout_xyz(collection, experiment, channel, resolution, x_range, y_range, z_range): |
| 202 | + |
| 203 | + |
| 204 | +MANAGER = FilesystemStorageManager() |
| 205 | + |
| 206 | +@APP.route("/v1/cutout/<collection>/<experiment>/<channel>/<resolution>/<x_range>/<y_range>/<z_range>/", methods=["POST"]) |
| 207 | +def upload_cutout_xyz(collection, experiment, channel, resolution, x_range, y_range, z_range): |
47 | 208 | """
|
48 | 209 | Upload a volume
|
49 | 210 |
|
50 | 211 | """
|
51 | 212 | for _, f in request.files.items():
|
52 | 213 | memfile = io.BytesIO()
|
53 | 214 | f.save(memfile)
|
54 |
| - data = np.fromstring(memfile.getvalue(), dtype=np.uint8) |
55 |
| - break |
| 215 | + data = np.fromstring(memfile.getvalue(), dtype="uint8") |
| 216 | + xs = [int(i) for i in x_range.split(":")] |
| 217 | + ys = [int(i) for i in y_range.split(":")] |
| 218 | + zs = [int(i) for i in z_range.split(":")] |
| 219 | + data = data.reshape(xs[1] - xs[0], ys[1] - ys[0], zs[1] - zs[0]) |
| 220 | + MANAGER.setdata(data, collection, experiment, channel, resolution, xs, ys, zs) |
56 | 221 | return ""
|
57 | 222 |
|
58 | 223 |
|
| 224 | +@APP.route("/v1/cutout/<collection>/<experiment>/<channel>/<resolution>/<x_range>/<y_range>/<z_range>/", methods=["GET"]) |
| 225 | +def get_cutout_xyz(collection, experiment, channel, resolution, x_range, y_range, z_range): |
| 226 | + """ |
| 227 | + Download a volume |
| 228 | + """ |
| 229 | + xs = [int(i) for i in x_range.split(":")] |
| 230 | + ys = [int(i) for i in y_range.split(":")] |
| 231 | + zs = [int(i) for i in z_range.split(":")] |
| 232 | + if ( |
| 233 | + os.path.isdir("{}/{}".format(UPLOADS_PATH, collection)) and |
| 234 | + os.path.isdir("{}/{}/{}".format(UPLOADS_PATH, collection, experiment)) and |
| 235 | + os.path.isdir("{}/{}/{}/{}".format(UPLOADS_PATH, collection, experiment, channel)) |
| 236 | + ): |
| 237 | + data = MANAGER.getdata(collection, experiment, channel, resolution, xs, ys, zs) |
| 238 | + res = {} |
| 239 | + res["dtype"] = str(data.dtype) |
| 240 | + res["data"] = data.tolist() |
| 241 | + return jsonify(res) |
| 242 | + else: |
| 243 | + return Response( |
| 244 | + json.dumps({"message": "DNE"}), |
| 245 | + status=402, |
| 246 | + mimetype="application/json" |
| 247 | + ) |
| 248 | + |
| 249 | + |
59 | 250 | @APP.route("/")
|
60 | 251 | def hello():
|
61 | 252 | """Root."""
|
|
0 commit comments