Package omeroweb :: Package webgateway :: Module views
[hide private]
[frames] | no frames]

Source Code for Module omeroweb.webgateway.views

   1  #!/usr/bin/env python 
   2  # -*- coding: utf-8 -*- 
   3  # 
   4  # webgateway/views.py - django application view handling functions 
   5  #  
   6  # Copyright (c) 2007, 2008, 2009 Glencoe Software, Inc. All rights reserved. 
   7  #  
   8  # This software is distributed under the terms described by the LICENCE file 
   9  # you can find at the root of the distribution bundle, which states you are 
  10  # free to use it only for non commercial purposes. 
  11  # If the file is missing please request a copy by contacting 
  12  # jason@glencoesoftware.com. 
  13  # 
  14  # Author: Carlos Neves <carlos(at)glencoesoftware.com> 
  15   
  16  import re 
  17   
  18  import omero 
  19  import omero.clients 
  20  from django.http import HttpResponse, HttpResponseServerError, HttpResponseRedirect, Http404 
  21  from django.utils import simplejson 
  22  from django.utils.encoding import smart_str 
  23  from django.utils.http import urlquote 
  24  from django.views.decorators.http import require_POST 
  25  from django.core import template_loader 
  26  from django.core.urlresolvers import reverse 
  27  from django.conf import settings 
  28  from django.template import RequestContext as Context 
  29  from django.core.servers.basehttp import FileWrapper 
  30  from omero.rtypes import rlong, unwrap 
  31  from omero.constants.namespaces import NSBULKANNOTATIONS 
  32  from marshal import imageMarshal, shapeMarshal 
  33   
  34  try: 
  35      from hashlib import md5 
  36  except: 
  37      from md5 import md5 
  38       
  39  from cStringIO import StringIO 
  40   
  41  from omero import client_wrapper, ApiUsageException 
  42  from omero.gateway import timeit, TimeIt 
  43   
  44  import Ice 
  45  import glob 
  46   
  47   
  48  import settings 
  49   
  50  #from models import StoredConnection 
  51   
  52  from webgateway_cache import webgateway_cache, CacheBase, webgateway_tempfile 
  53   
  54  cache = CacheBase() 
  55   
  56  import logging, os, traceback, time, zipfile, shutil 
  57   
  58  from omeroweb.decorators import login_required, ConnCleaningHttpResponse 
  59  from omeroweb.connector import Connector 
  60   
  61  logger = logging.getLogger(__name__) 
  62   
  63  try: 
  64      from PIL import Image 
  65      from PIL import ImageDraw 
  66  except: #pragma: nocover 
  67      try: 
  68          import Image 
  69          import ImageDraw 
  70      except: 
  71          logger.error('No PIL installed') 
72 73 74 -def _safestr (s):
75 return unicode(s).encode('utf-8')
76
77 -class UserProxy (object):
78 """ 79 Represents the current user of the connection, with methods delegating to the connection itself. 80 """ 81
82 - def __init__ (self, blitzcon):
83 """ 84 Initialises the User proxy with the L{omero.gateway.BlitzGateway} connection 85 86 @param blitzcon: connection 87 @type blitzcon: L{omero.gateway.BlitzGateway} 88 """ 89 90 self._blitzcon = blitzcon 91 self.loggedIn = False
92
93 - def logIn (self):
94 """ Sets the loggedIn Flag to True """ 95 96 self.loggedIn = True
97
98 - def isAdmin (self):
99 """ 100 True if the current user is an admin 101 102 @return: True if the current user is an admin 103 @rtype: Boolean 104 """ 105 106 return self._blitzcon.isAdmin()
107
108 - def canBeAdmin (self):
109 """ 110 True if the current user can be admin 111 112 @return: True if the current user can be admin 113 @rtype: Boolean 114 """ 115 116 return self._blitzcon.canBeAdmin()
117
118 - def getId (self):
119 """ 120 Returns the ID of the current user 121 122 @return: User ID 123 @rtype: Long 124 """ 125 126 return self._blitzcon.getUserId()
127
128 - def getName (self):
129 """ 130 Returns the Name of the current user 131 132 @return: User Name 133 @rtype: String 134 """ 135 136 return self._blitzcon.getUser().omeName
137
138 - def getFirstName (self):
139 """ 140 Returns the first name of the current user 141 142 @return: First Name 143 @rtype: String 144 """ 145 146 return self._blitzcon.getUser().firstName or self.getName()
147
148 # def getPreferences (self): 149 # return self._blitzcon._user.getPreferences() 150 # 151 # def getUserObj (self): 152 # return self._blitzcon._user 153 154 #class SessionCB (object): 155 # def _log (self, what, c): 156 # logger.debug('CONN:%s %s:%d:%s' % (what, c._user, os.getpid(), c._sessionUuid)) 157 # 158 # def create (self, c): 159 # self._log('create',c) 160 # 161 # def join (self, c): 162 # self._log('join',c) 163 # 164 # def close (self, c): 165 # self._log('close',c) 166 #_session_cb = SessionCB() 167 168 -def _split_channel_info (rchannels):
169 """ 170 Splits the request query channel information for images into a sequence of channels, window ranges 171 and channel colors. 172 173 @param rchannels: The request string with channel info. E.g 1|100:505$0000FF,-2,3|620:3879$FF0000 174 @type rchannels: String 175 @return: E.g. [1, -2, 3] [[100.0, 505.0], (None, None), [620.0, 3879.0]] [u'0000FF', None, u'FF0000'] 176 @rtype: tuple of 3 lists 177 """ 178 179 channels = [] 180 windows = [] 181 colors = [] 182 for chan in rchannels.split(','): 183 chan = chan.split('|') 184 t = chan[0].strip() 185 color = None 186 if t.find('$')>=0: 187 t,color = t.split('$') 188 try: 189 channels.append(int(t)) 190 ch_window = (None, None) 191 if len(chan) > 1: 192 t = chan[1].strip() 193 if t.find('$')>=0: 194 t, color = t.split('$') 195 t = t.split(':') 196 if len(t) == 2: 197 try: 198 ch_window = [float(x) for x in t] 199 except ValueError: 200 pass 201 windows.append(ch_window) 202 colors.append(color) 203 except ValueError: 204 pass 205 logger.debug(str(channels)+","+str(windows)+","+str(colors)) 206 return channels, windows, colors
207
208 -def getImgDetailsFromReq (request, as_string=False):
209 """ Break the GET information from the request object into details on how to render the image. 210 The following keys are recognized: 211 z - Z axis position 212 t - T axis position 213 q - Quality set (0,0..1,0) 214 m - Model (g for greyscale, c for color) 215 p - Projection (see blitz_gateway.ImageWrapper.PROJECTIONS for keys) 216 x - X position (for now based on top/left offset on the browser window) 217 y - Y position (same as above) 218 c - a comma separated list of channels to be rendered (start index 1) 219 - format for each entry [-]ID[|wndst:wndend][#HEXCOLOR][,...] 220 zm - the zoom setting (as a percentual value) 221 222 @param request: http request with keys above 223 @param as_string: If True, return a string representation of the rendering details 224 @return: A dict or String representation of rendering details above. 225 @rtype: Dict or String 226 """ 227 228 r = request.REQUEST 229 rv = {} 230 for k in ('z', 't', 'q', 'm', 'zm', 'x', 'y', 'p'): 231 if r.has_key(k): 232 rv[k] = r[k] 233 if r.has_key('c'): 234 rv['c'] = [] 235 ci = _split_channel_info(r['c']) 236 logger.debug(ci) 237 for i in range(len(ci[0])): 238 # a = abs channel, i = channel, s = window start, e = window end, c = color 239 rv['c'].append({'a':abs(ci[0][i]), 'i':ci[0][i], 's':ci[1][i][0], 'e':ci[1][i][1], 'c':ci[2][i]}) 240 if as_string: 241 return "&".join(["%s=%s" % (x[0], x[1]) for x in rv.items()]) 242 return rv
243
244 @login_required() 245 -def render_birds_eye_view (request, iid, size=None, 246 conn=None, **kwargs):
247 """ 248 Returns an HttpResponse wrapped jpeg with the rendered bird's eye view 249 for image 'iid'. We now use a thumbnail for performance. #10626 250 251 @param request: http request 252 @param iid: Image ID 253 @param conn: L{omero.gateway.BlitzGateway} connection 254 @param size: Maximum size of the longest side of the resulting bird's eye view. 255 @return: http response containing jpeg 256 """ 257 if size is None: 258 size = 96 # Use cached thumbnail 259 return render_thumbnail(request, iid, w=size)
260
261 @login_required() 262 -def render_thumbnail (request, iid, w=None, h=None, conn=None, _defcb=None, **kwargs):
263 """ 264 Returns an HttpResponse wrapped jpeg with the rendered thumbnail for image 'iid' 265 266 @param request: http request 267 @param iid: Image ID 268 @param w: Thumbnail max width. 64 by default 269 @param h: Thumbnail max height 270 @return: http response containing jpeg 271 """ 272 server_id = request.session['connector'].server_id 273 direct = True 274 if w is None: 275 size = (64,) 276 else: 277 if h is None: 278 size = (int(w),) 279 else: 280 size = (int(w), int(h)) 281 if size == (96,): 282 direct = False 283 user_id = conn.getUserId() 284 jpeg_data = webgateway_cache.getThumb(request, server_id, user_id, iid, size) 285 if jpeg_data is None: 286 prevent_cache = False 287 img = conn.getObject("Image", iid) 288 if img is None: 289 logger.debug("(b)Image %s not found..." % (str(iid))) 290 if _defcb: 291 jpeg_data = _defcb(size=size) 292 prevent_cache = True 293 else: 294 raise Http404 295 else: 296 jpeg_data = img.getThumbnail(size=size, direct=direct) 297 if jpeg_data is None: 298 logger.debug("(c)Image %s not found..." % (str(iid))) 299 if _defcb: 300 jpeg_data = _defcb(size=size) 301 prevent_cache = True 302 else: 303 return HttpResponseServerError('Failed to render thumbnail') 304 else: 305 prevent_cache = img._thumbInProgress 306 if not prevent_cache: 307 webgateway_cache.setThumb(request, server_id, user_id, iid, jpeg_data, size) 308 else: 309 pass 310 rsp = HttpResponse(jpeg_data, mimetype='image/jpeg') 311 return rsp
312
313 @login_required() 314 -def render_roi_thumbnail (request, roiId, w=None, h=None, conn=None, **kwargs):
315 """ 316 For the given ROI, choose the shape to render (first time-point, mid z-section) then render 317 a region around that shape, scale to width and height (or default size) and draw the 318 shape on to the region 319 """ 320 server_id = request.session['connector'].server_id 321 322 # need to find the z indices of the first shape in T 323 roiResult = conn.getRoiService().findByRoi(long(roiId), None, conn.SERVICE_OPTS) 324 if roiResult is None or roiResult.rois is None: 325 raise Http404 326 zz = set() 327 minT = None 328 shapes = {} 329 for roi in roiResult.rois: 330 imageId = roi.image.id.val 331 for s in roi.copyShapes(): 332 if s is None: # seems possible in some situations 333 continue 334 t = s.getTheT().getValue() 335 z = s.getTheZ().getValue() 336 shapes[(z,t)] = s 337 if minT is None: minT = t 338 if t < minT: 339 zz = set([z]) 340 minT = t 341 elif minT == t: 342 zz.add(z) 343 zList = list(zz) 344 zList.sort() 345 midZ = zList[len(zList)/2] 346 s = shapes[(midZ, minT)] 347 348 pi = _get_prepared_image(request, imageId, server_id=server_id, conn=conn) 349 350 if pi is None: 351 raise Http404 352 image, compress_quality = pi 353 354 return get_shape_thumbnail (request, conn, image, s, compress_quality)
355
356 @login_required() 357 -def render_shape_thumbnail (request, shapeId, w=None, h=None, conn=None, **kwargs):
358 """ 359 For the given Shape, redner a region around that shape, scale to width and height (or default size) and draw the 360 shape on to the region. 361 """ 362 server_id = request.session['connector'].server_id 363 364 # need to find the z indices of the first shape in T 365 params = omero.sys.Parameters() 366 params.map = {'id':rlong(shapeId)} 367 shape = conn.getQueryService().findByQuery("select s from Shape s join fetch s.roi where s.id = :id", params, conn.SERVICE_OPTS) 368 369 if shape is None: 370 raise Http404 371 372 imageId = shape.roi.image.id.val 373 374 pi = _get_prepared_image(request, imageId, server_id=server_id, conn=conn) 375 if pi is None: 376 raise Http404 377 image, compress_quality = pi 378 379 return get_shape_thumbnail (request, conn, image, shape, compress_quality)
380
381 382 -def get_shape_thumbnail (request, conn, image, s, compress_quality):
383 """ 384 Render a region around the specified Shape, scale to width and height (or default size) and draw the 385 shape on to the region. Returns jpeg data. 386 387 @param image: ImageWrapper 388 @param s: omero.model.Shape 389 """ 390 391 MAX_WIDTH = 250 392 color = request.REQUEST.get("color", "fff") 393 colours = {"f00":(255,0,0), "0f0":(0,255,0), "00f":(0,0,255), "ff0":(255,255,0), "fff":(255,255,255), "000":(0,0,0)} 394 lineColour = colours["f00"] 395 if color in colours: 396 lineColour = colours[color] 397 bg_color = (221,221,221) # used for padding if we go outside the image area 398 399 def pointsStringToXYlist(string): 400 """ 401 Method for converting the string returned from omero.model.ShapeI.getPoints() 402 into list of (x,y) points. 403 E.g: "points[309,427, 366,503, 190,491] points1[309,427, 366,503, 190,491] points2[309,427, 366,503, 190,491]" 404 """ 405 pointLists = string.strip().split("points") 406 if len(pointLists) < 2: 407 logger.error("Unrecognised ROI shape 'points' string: %s" % string) 408 return "" 409 firstList = pointLists[1] 410 xyList = [] 411 for xy in firstList.strip(" []").split(", "): 412 x, y = xy.split(",") 413 xyList.append( ( int( x.strip() ), int(y.strip() ) ) ) 414 return xyList
415 416 def xyListToBbox(xyList): 417 """ Returns a bounding box (x,y,w,h) that will contain the shape represented by the XY points list """ 418 xList, yList = [], [] 419 for xy in xyList: 420 x, y = xy 421 xList.append(x) 422 yList.append(y) 423 return (min(xList), min(yList), max(xList)-min(xList), max(yList)-min(yList)) 424 425 bBox = None # bounding box: (x, y, w, h) 426 shape = {} 427 theT = s.getTheT() is not None and s.getTheT().getValue() or 0 428 theZ = s.getTheZ() is not None and s.getTheZ().getValue() or 0 429 if type(s) == omero.model.RectI: 430 shape['type'] = 'Rectangle' 431 shape['x'] = s.getX().getValue() 432 shape['y'] = s.getY().getValue() 433 shape['width'] = s.getWidth().getValue() 434 shape['height'] = s.getHeight().getValue() 435 bBox = (shape['x'], shape['y'], shape['width'], shape['height']) 436 elif type(s) == omero.model.MaskI: 437 shape['type'] = 'Mask' 438 shape['x'] = s.getX().getValue() 439 shape['y'] = s.getY().getValue() 440 shape['width'] = s.getWidth().getValue() 441 shape['height'] = s.getHeight().getValue() 442 bBox = (shape['x'], shape['y'], shape['width'], shape['height']) 443 # TODO: support for mask 444 elif type(s) == omero.model.EllipseI: 445 shape['type'] = 'Ellipse' 446 shape['cx'] = int(s.getCx().getValue()) 447 shape['cy'] = int(s.getCy().getValue()) 448 shape['rx'] = int(s.getRx().getValue()) 449 shape['ry'] = int(s.getRy().getValue()) 450 bBox = (shape['cx']-shape['rx'], shape['cy']-shape['ry'], 2*shape['rx'], 2*shape['ry']) 451 elif type(s) == omero.model.PolylineI: 452 shape['type'] = 'PolyLine' 453 shape['xyList'] = pointsStringToXYlist(s.getPoints().getValue()) 454 bBox = xyListToBbox(shape['xyList']) 455 elif type(s) == omero.model.LineI: 456 shape['type'] = 'Line' 457 shape['x1'] = int(s.getX1().getValue()) 458 shape['x2'] = int(s.getX2().getValue()) 459 shape['y1'] = int(s.getY1().getValue()) 460 shape['y2'] = int(s.getY2().getValue()) 461 x = min(shape['x1'],shape['x2']) 462 y = min(shape['y1'],shape['y2']) 463 bBox = (x, y, max(shape['x1'],shape['x2'])-x, max(shape['y1'],shape['y2'])-y) 464 elif type(s) == omero.model.PointI: 465 shape['type'] = 'Point' 466 shape['cx'] = s.getCx().getValue() 467 shape['cy'] = s.getCy().getValue() 468 bBox = (shape['cx']-50, shape['cy']-50, 100, 100) 469 elif type(s) == omero.model.PolygonI: 470 shape['type'] = 'Polygon' 471 shape['xyList'] = pointsStringToXYlist(s.getPoints().getValue()) 472 bBox = xyListToBbox(shape['xyList']) 473 elif type(s) == omero.model.LabelI: 474 shape['type'] = 'Label' 475 shape['x'] = s.getX().getValue() 476 shape['y'] = s.getY().getValue() 477 bBox = (shape['x']-50, shape['y']-50, 100, 100) 478 else: 479 logger.debug("Shape type not supported: %s" % str(type(s))) 480 #print shape 481 482 # we want to render a region larger than the bounding box 483 x,y,w,h = bBox 484 requiredWidth = max(w,h*3/2) # make the aspect ratio (w/h) = 3/2 485 requiredHeight = requiredWidth*2/3 486 newW = int(requiredWidth * 1.5) # make the rendered region 1.5 times larger than the bounding box 487 newH = int(requiredHeight * 1.5) 488 # Don't want the region to be smaller than the thumbnail dimensions 489 if newW < MAX_WIDTH: 490 newW = MAX_WIDTH 491 newH = newW*2/3 492 # Don't want the region to be bigger than a 'Big Image'! 493 def getConfigValue(key): 494 try: 495 return conn.getConfigService().getConfigValue(key) 496 except: 497 logger.warn("webgateway: get_shape_thumbnail() could not get Config-Value for %s" % key) 498 pass 499 max_plane_width = getConfigValue("omero.pixeldata.max_plane_width") 500 max_plane_height = getConfigValue("omero.pixeldata.max_plane_height") 501 if max_plane_width is None or max_plane_height is None or (newW > int(max_plane_width)) or (newH > int(max_plane_height)): 502 # generate dummy image to return 503 dummy = Image.new('RGB', (MAX_WIDTH, MAX_WIDTH*2/3), bg_color) 504 draw = ImageDraw.Draw(dummy) 505 draw.text((10,30), "Shape too large to \ngenerate thumbnail", fill=(255,0,0)) 506 rv = StringIO() 507 dummy.save(rv, 'jpeg', quality=90) 508 return HttpResponse(rv.getvalue(), mimetype='image/jpeg') 509 510 xOffset = (newW - w)/2 511 yOffset = (newH - h)/2 512 newX = int(x - xOffset) 513 newY = int(y - yOffset) 514 515 # Need to check if any part of our region is outside the image. (assume that SOME of the region is within the image!) 516 sizeX = image.getSizeX() 517 sizeY = image.getSizeY() 518 left_xs, right_xs, top_xs, bottom_xs = 0,0,0,0 519 if newX < 0: 520 newW = newW + newX 521 left_xs = abs(newX) 522 newX = 0 523 if newY < 0: 524 newH = newH + newY 525 top_xs = abs(newY) 526 newY = 0 527 if newW+newX > sizeX: 528 right_xs = (newW+newX) - sizeX 529 newW = newW - right_xs 530 if newH+newY > sizeY: 531 bottom_xs = (newH+newY) - sizeY 532 newH = newH - bottom_xs 533 534 # now we should be getting the correct region 535 jpeg_data = image.renderJpegRegion(theZ,theT,newX, newY, newW, newH,level=None, compression=compress_quality) 536 img = Image.open(StringIO(jpeg_data)) 537 538 # add back on the xs we were forced to trim 539 if left_xs != 0 or right_xs != 0 or top_xs != 0 or bottom_xs != 0: 540 jpg_w, jpg_h = img.size 541 xs_w = jpg_w + right_xs + left_xs 542 xs_h = jpg_h + bottom_xs + top_xs 543 xs_image = Image.new('RGBA', (xs_w, xs_h), bg_color) 544 xs_image.paste(img, (left_xs, top_xs)) 545 img = xs_image 546 547 # we have our full-sized region. Need to resize to thumbnail. 548 current_w, current_h = img.size 549 factor = float(MAX_WIDTH) / current_w 550 resizeH = int(current_h * factor) 551 img = img.resize((MAX_WIDTH, resizeH)) 552 553 draw = ImageDraw.Draw(img) 554 if shape['type'] == 'Rectangle': 555 rectX = int(xOffset * factor) 556 rectY = int(yOffset * factor) 557 rectW = int((w+xOffset) * factor) 558 rectH = int((h+yOffset) * factor) 559 draw.rectangle((rectX, rectY, rectW, rectH), outline=lineColour) 560 draw.rectangle((rectX-1, rectY-1, rectW+1, rectH+1), outline=lineColour) # hack to get line width of 2 561 elif shape['type'] == 'Line': 562 lineX1 = (shape['x1'] - newX + left_xs) * factor 563 lineX2 = (shape['x2'] - newX + left_xs) * factor 564 lineY1 = (shape['y1'] - newY + top_xs) * factor 565 lineY2 = (shape['y2'] - newY + top_xs) * factor 566 draw.line((lineX1, lineY1, lineX2, lineY2), fill=lineColour, width=2) 567 elif shape['type'] == 'Ellipse': 568 rectX = int(xOffset * factor) 569 rectY = int(yOffset * factor) 570 rectW = int((w+xOffset) * factor) 571 rectH = int((h+yOffset) * factor) 572 draw.ellipse((rectX, rectY, rectW, rectH), outline=lineColour) 573 draw.ellipse((rectX-1, rectY-1, rectW+1, rectH+1), outline=lineColour) # hack to get line width of 2 574 elif shape['type'] == 'Point': 575 point_radius = 2 576 rectX = (MAX_WIDTH/2) - point_radius 577 rectY = int(resizeH/2) - point_radius 578 rectW = rectX + (point_radius * 2) 579 rectH = rectY + (point_radius * 2) 580 draw.ellipse((rectX, rectY, rectW, rectH), outline=lineColour) 581 draw.ellipse((rectX-1, rectY-1, rectW+1, rectH+1), outline=lineColour) # hack to get line width of 2 582 elif 'xyList' in shape: 583 #resizedXY = [ (int(x*factor), int(y*factor)) for (x,y) in shape['xyList'] ] 584 def resizeXY(xy): 585 x,y = xy 586 return (int((x-newX + left_xs)*factor), int((y-newY + top_xs)*factor)) 587 resizedXY = [ resizeXY(xy) for xy in shape['xyList'] ] 588 #draw.polygon(resizedXY, outline=lineColour) # doesn't support 'width' of line 589 x2 = y2 = None 590 for l in range(1, len(resizedXY)): 591 x1, y1 = resizedXY[l-1] 592 x2, y2 = resizedXY[l] 593 draw.line((x1, y1, x2, y2), fill=lineColour, width=2) 594 start_x, start_y = resizedXY[0] 595 if shape['type'] != 'PolyLine': 596 if x2 is None: # Seems possible to have Polygon with only 1 point! 597 x2 = start_x + 1 # This will create a visible dot 598 if y2 is None: 599 y2 = start_y + 1 600 draw.line((x2, y2, start_x, start_y), fill=lineColour, width=2) 601 602 rv = StringIO() 603 compression = 0.9 604 img.save(rv, 'jpeg', quality=int(compression*100)) 605 jpeg = rv.getvalue() 606 607 return HttpResponse(jpeg, mimetype='image/jpeg') 608
609 610 -def _get_signature_from_request (request):
611 """ 612 returns a string that identifies this image, along with the settings passed on the request. 613 Useful for using as img identifier key, for prepared image. 614 615 @param request: http request 616 @return: String 617 """ 618 619 r = request.REQUEST 620 rv = r.get('m','_') + r.get('p','_')+r.get('c','_')+r.get('q', '_') 621 return rv
622
623 -def _get_prepared_image (request, iid, server_id=None, conn=None, saveDefs=False, retry=True):
624 """ 625 Fetches the Image object for image 'iid' and prepares it according to the request query, setting the channels, 626 rendering model and projection arguments. The compression level is parsed and returned too. 627 For parameters in request, see L{getImgDetailsFromReq} 628 629 @param request: http request 630 @param iid: Image ID 631 @param conn: L{omero.gateway.BlitzGateway} connection 632 @param saveDefs: Try to save the rendering settings, default z and t. 633 @param retry: Try an extra attempt at this method 634 @return: Tuple (L{omero.gateway.ImageWrapper} image, quality) 635 """ 636 r = request.REQUEST 637 logger.debug('Preparing Image:%r saveDefs=%r ' \ 638 'retry=%r request=%r conn=%s' % (iid, saveDefs, retry, 639 r, str(conn))) 640 img = conn.getObject("Image", iid) 641 if img is None: 642 return 643 if r.has_key('c'): 644 logger.debug("c="+r['c']) 645 channels, windows, colors = _split_channel_info(r['c']) 646 if not img.setActiveChannels(channels, windows, colors): 647 logger.debug("Something bad happened while setting the active channels...") 648 if r.get('m', None) == 'g': 649 img.setGreyscaleRenderingModel() 650 elif r.get('m', None) == 'c': 651 img.setColorRenderingModel() 652 img.setProjection(r.get('p', None)) 653 img.setInvertedAxis(bool(r.get('ia', "0") == "1")) 654 compress_quality = r.get('q', None) 655 if saveDefs: 656 r.has_key('z') and img._re.setDefaultZ(long(r['z'])-1) 657 r.has_key('t') and img._re.setDefaultT(long(r['t'])-1) 658 img.saveDefaults() 659 return (img, compress_quality)
660
661 @login_required() 662 -def render_image_region(request, iid, z, t, conn=None, **kwargs):
663 """ 664 Returns a jpeg of the OMERO image, rendering only a region specified in query string as 665 region=x,y,width,height. E.g. region=0,512,256,256 666 Rendering settings can be specified in the request parameters. 667 668 @param request: http request 669 @param iid: image ID 670 @param z: Z index 671 @param t: T index 672 @param conn: L{omero.gateway.BlitzGateway} connection 673 @return: http response wrapping jpeg 674 """ 675 server_id = request.session['connector'].server_id 676 # if the region=x,y,w,h is not parsed correctly to give 4 ints then we simply provide whole image plane. 677 # alternatively, could return a 404? 678 #if h == None: 679 # return render_image (request, iid, z, t, server_id=None, _conn=None, **kwargs) 680 pi = _get_prepared_image(request, iid, server_id=server_id, conn=conn) 681 682 if pi is None: 683 raise Http404 684 img, compress_quality = pi 685 686 tile = request.REQUEST.get('tile', None) 687 region = request.REQUEST.get('region', None) 688 level = None 689 690 if tile: 691 try: 692 img._prepareRenderingEngine() 693 tiles = img._re.requiresPixelsPyramid() 694 w, h = img._re.getTileSize() 695 levels = img._re.getResolutionLevels()-1 696 697 zxyt = tile.split(",") 698 699 #w = int(zxyt[3]) 700 #h = int(zxyt[4]) 701 level = levels-int(zxyt[0]) 702 703 x = int(zxyt[1])*w 704 y = int(zxyt[2])*h 705 except: 706 logger.debug("render_image_region: tile=%s" % tile) 707 logger.debug(traceback.format_exc()) 708 709 elif region: 710 try: 711 xywh = region.split(",") 712 713 x = int(xywh[0]) 714 y = int(xywh[1]) 715 w = int(xywh[2]) 716 h = int(xywh[3]) 717 except: 718 logger.debug("render_image_region: region=%s" % region) 719 logger.debug(traceback.format_exc()) 720 721 # region details in request are used as key for caching. 722 jpeg_data = webgateway_cache.getImage(request, server_id, img, z, t) 723 if jpeg_data is None: 724 jpeg_data = img.renderJpegRegion(z,t,x,y,w,h,level=level, compression=compress_quality) 725 if jpeg_data is None: 726 raise Http404 727 webgateway_cache.setImage(request, server_id, img, z, t, jpeg_data) 728 rsp = HttpResponse(jpeg_data, mimetype='image/jpeg') 729 return rsp
730
731 @login_required() 732 -def render_image (request, iid, z=None, t=None, conn=None, **kwargs):
733 """ 734 Renders the image with id {{iid}} at {{z}} and {{t}} as jpeg. 735 Many options are available from the request dict. See L{getImgDetailsFromReq} for list. 736 I am assuming a single Pixels object on image with image-Id='iid'. May be wrong 737 738 @param request: http request 739 @param iid: image ID 740 @param z: Z index 741 @param t: T index 742 @param conn: L{omero.gateway.BlitzGateway} connection 743 @return: http response wrapping jpeg 744 """ 745 server_id = request.session['connector'].server_id 746 pi = _get_prepared_image(request, iid, server_id=server_id, conn=conn) 747 if pi is None: 748 raise Http404 749 img, compress_quality = pi 750 jpeg_data = webgateway_cache.getImage(request, server_id, img, z, t) 751 if jpeg_data is None: 752 jpeg_data = img.renderJpeg(z,t, compression=compress_quality) 753 if jpeg_data is None: 754 raise Http404 755 webgateway_cache.setImage(request, server_id, img, z, t, jpeg_data) 756 757 rsp = HttpResponse(jpeg_data, mimetype='image/jpeg') 758 if 'download' in kwargs and kwargs['download']: 759 rsp['Content-Type'] = 'application/force-download' 760 rsp['Content-Length'] = len(jpeg_data) 761 rsp['Content-Disposition'] = 'attachment; filename=%s.jpg' % (img.getName().replace(" ","_")) 762 return rsp
763
764 @login_required() 765 -def render_ome_tiff (request, ctx, cid, conn=None, **kwargs):
766 """ 767 Renders the OME-TIFF representation of the image(s) with id cid in ctx (i)mage, 768 (d)ataset, or (p)roject. 769 For multiple images export, images that require pixels pyramid (big images) will be silently skipped. 770 If exporting a single big image or if all images in a multple image export are big, 771 a 404 will be triggered. 772 A request parameter dryrun can be passed to return the count of images that would actually be exported. 773 774 @param request: http request 775 @param ctx: 'p' or 'd' or 'i' 776 @param cid: Project, Dataset or Image ID 777 @param conn: L{omero.gateway.BlitzGateway} connection 778 @return: http response wrapping the tiff (or zip for multiple files), or redirect to temp file/zip 779 if dryrun is True, returns count of images that would be exported 780 """ 781 server_id = request.session['connector'].server_id 782 imgs = [] 783 if ctx == 'p': 784 obj = conn.getObject("Project", cid) 785 if obj is None: 786 raise Http404 787 for d in obj.listChildren(): 788 imgs.extend(list(d.listChildren())) 789 name = obj.getName() 790 elif ctx == 'd': 791 obj = conn.getObject("Dataset", cid) 792 if obj is None: 793 raise Http404 794 imgs.extend(list(obj.listChildren())) 795 selection = filter(None, request.REQUEST.get('selection', '').split(',')) 796 if len(selection): 797 logger.debug(selection) 798 logger.debug(imgs) 799 imgs = filter(lambda x: str(x.getId()) in selection, imgs) 800 logger.debug(imgs) 801 if len(imgs) == 0: 802 raise Http404 803 name = '%s-%s' % (obj.getParent().getName(), obj.getName()) 804 elif ctx == 'w': 805 obj = conn.getObject("Well", cid) 806 if obj is None: 807 raise Http404 808 imgs.extend([x.getImage() for x in obj.listChildren()]) 809 plate = obj.getParent() 810 coord = "%s%s" % (plate.getRowLabels()[obj.row],plate.getColumnLabels()[obj.column]) 811 name = '%s-%s-%s' % (plate.getParent().getName(), plate.getName(), coord) 812 else: 813 obj = conn.getObject("Image", cid) 814 if obj is None: 815 raise Http404 816 imgs.append(obj) 817 818 imgs = filter(lambda x: not x.requiresPixelsPyramid(), imgs) 819 820 if request.REQUEST.get('dryrun', False): 821 rv = simplejson.dumps(len(imgs)) 822 c = request.REQUEST.get('callback', None) 823 if c is not None and not kwargs.get('_internal', False): 824 rv = '%s(%s)' % (c, rv) 825 return HttpResponse(rv, mimetype='application/javascript') 826 827 if len(imgs) == 0: 828 raise Http404 829 if len(imgs) == 1: 830 obj = imgs[0] 831 key = '_'.join((str(x.getId()) for x in obj.getAncestry())) + '_' + str(obj.getId()) + '_ome_tiff' 832 fnamemax = 255 - len(str(obj.getId())) - 10 # total name len <= 255, 9 is for .ome.tiff 833 objname = obj.getName()[:fnamemax] 834 fpath, rpath, fobj = webgateway_tempfile.new(str(obj.getId()) + '-'+ objname + '.ome.tiff', key=key) 835 if fobj is True: 836 # already exists 837 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath) 838 tiff_data = webgateway_cache.getOmeTiffImage(request, server_id, imgs[0]) 839 if tiff_data is None: 840 try: 841 tiff_data = imgs[0].exportOmeTiff() 842 except: 843 logger.debug('Failed to export image (2)', exc_info=True) 844 tiff_data = None 845 if tiff_data is None: 846 webgateway_tempfile.abort(fpath) 847 raise Http404 848 webgateway_cache.setOmeTiffImage(request, server_id, imgs[0], tiff_data) 849 if fobj is None: 850 rsp = HttpResponse(tiff_data, mimetype='image/tiff') 851 rsp['Content-Disposition'] = 'attachment; filename="%s.ome.tiff"' % (str(obj.getId()) + '-'+objname) 852 rsp['Content-Length'] = len(tiff_data) 853 return rsp 854 else: 855 fobj.write(tiff_data) 856 fobj.close() 857 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath) 858 else: 859 try: 860 img_ids = '+'.join((str(x.getId()) for x in imgs)) 861 key = '_'.join((str(x.getId()) for x in imgs[0].getAncestry())) + '_' + md5(img_ids).hexdigest() + '_ome_tiff_zip' 862 fpath, rpath, fobj = webgateway_tempfile.new(name + '.zip', key=key) 863 if fobj is True: 864 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath) 865 logger.debug(fpath) 866 if fobj is None: 867 fobj = StringIO() 868 zobj = zipfile.ZipFile(fobj, 'w', zipfile.ZIP_STORED) 869 for obj in imgs: 870 tiff_data = webgateway_cache.getOmeTiffImage(request, server_id, obj) 871 if tiff_data is None: 872 tiff_data = obj.exportOmeTiff() 873 if tiff_data is None: 874 continue 875 webgateway_cache.setOmeTiffImage(request, server_id, obj, tiff_data) 876 # While ZIP itself doesn't have the 255 char limit for filenames, the FS where these 877 # get unarchived might, so trim names 878 fnamemax = 255 - len(str(obj.getId())) - 10 # total name len <= 255, 9 is for .ome.tiff 879 objname = obj.getName()[:fnamemax] 880 zobj.writestr(str(obj.getId()) + '-'+objname + '.ome.tiff', tiff_data) 881 zobj.close() 882 if fpath is None: 883 zip_data = fobj.getvalue() 884 rsp = HttpResponse(zip_data, mimetype='application/zip') 885 rsp['Content-Disposition'] = 'attachment; filename="%s.zip"' % name 886 rsp['Content-Length'] = len(zip_data) 887 return rsp 888 except: 889 logger.debug(traceback.format_exc()) 890 raise 891 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath)
892
893 @login_required() 894 -def render_movie (request, iid, axis, pos, conn=None, **kwargs):
895 """ 896 Renders a movie from the image with id iid 897 898 @param request: http request 899 @param iid: Image ID 900 @param axis: Movie frames are along 'z' or 't' dimension. String 901 @param pos: The T index (for z axis) or Z index (for t axis) 902 @param conn: L{omero.gateway.BlitzGateway} connection 903 @return: http response wrapping the file, or redirect to temp file 904 """ 905 server_id = request.session['connector'].server_id 906 try: 907 # Prepare a filename we'll use for temp cache, and check if file is already there 908 opts = {} 909 opts['format'] = 'video/' + request.REQUEST.get('format', 'quicktime') 910 opts['fps'] = int(request.REQUEST.get('fps', 4)) 911 opts['minsize'] = (512,512, 'Black') 912 ext = '.avi' 913 key = "%s-%s-%s-%d-%s-%s" % (iid, axis, pos, opts['fps'], _get_signature_from_request(request), 914 request.REQUEST.get('format', 'quicktime')) 915 916 pos = int(pos) 917 pi = _get_prepared_image(request, iid, server_id=server_id, conn=conn) 918 if pi is None: 919 raise Http404 920 img, compress_quality = pi 921 922 fpath, rpath, fobj = webgateway_tempfile.new(img.getName() + ext, key=key) 923 logger.debug(fpath, rpath, fobj) 924 if fobj is True: 925 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath)#os.path.join(rpath, img.getName() + ext)) 926 927 if kwargs.has_key('optsCB'): 928 opts.update(kwargs['optsCB'](img)) 929 opts.update(kwargs.get('opts', {})) 930 logger.debug('rendering movie for img %s with axis %s, pos %i and opts %s' % (iid, axis, pos, opts)) 931 #fpath, rpath = webgateway_tempfile.newdir() 932 if fpath is None: 933 import tempfile 934 fo, fn = tempfile.mkstemp() 935 else: 936 fn = fpath #os.path.join(fpath, img.getName()) 937 if axis.lower() == 'z': 938 dext, mimetype = img.createMovie(fn, 0, img.getSizeZ()-1, pos-1, pos-1, opts) 939 else: 940 dext, mimetype = img.createMovie(fn, pos-1, pos-1, 0, img.getSizeT()-1, opts) 941 if dext is None and mimetype is None: 942 # createMovie is currently only available on 4.1_custom 943 # http://trac.openmicroscopy.org.uk/ome/ticket/3857 944 raise Http404 945 if fpath is None: 946 movie = open(fn).read() 947 os.close(fo) 948 rsp = HttpResponse(movie, mimetype=mimetype) 949 rsp['Content-Disposition'] = 'attachment; filename="%s"' % (img.getName()+ext) 950 rsp['Content-Length'] = len(movie) 951 return rsp 952 else: 953 fobj.close() 954 #shutil.move(fn, fn + ext) 955 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath)#os.path.join(rpath, img.getName() + ext)) 956 except: 957 logger.debug(traceback.format_exc()) 958 raise
959
960 @login_required() 961 -def render_split_channel (request, iid, z, t, conn=None, **kwargs):
962 """ 963 Renders a split channel view of the image with id {{iid}} at {{z}} and {{t}} as jpeg. 964 Many options are available from the request dict. 965 Requires PIL to be installed on the server. 966 967 @param request: http request 968 @param iid: Image ID 969 @param z: Z index 970 @param t: T index 971 @param conn: L{omero.gateway.BlitzGateway} connection 972 @return: http response wrapping a jpeg 973 """ 974 server_id = request.session['connector'].server_id 975 pi = _get_prepared_image(request, iid, server_id=server_id, conn=conn) 976 if pi is None: 977 raise Http404 978 img, compress_quality = pi 979 compress_quality = compress_quality and float(compress_quality) or 0.9 980 jpeg_data = webgateway_cache.getSplitChannelImage(request, server_id, img, z, t) 981 if jpeg_data is None: 982 jpeg_data = img.renderSplitChannel(z,t, compression=compress_quality) 983 if jpeg_data is None: 984 raise Http404 985 webgateway_cache.setSplitChannelImage(request, server_id, img, z, t, jpeg_data) 986 rsp = HttpResponse(jpeg_data, mimetype='image/jpeg') 987 return rsp
988
989 -def debug (f):
990 """ 991 Decorator for adding debugging functionality to methods. 992 993 @param f: The function to wrap 994 @return: The wrapped function 995 """ 996 997 def wrap (request, *args, **kwargs): 998 debug = request.REQUEST.getlist('debug') 999 if 'slow' in debug: 1000 time.sleep(5) 1001 if 'fail' in debug: 1002 raise Http404 1003 if 'error' in debug: 1004 raise AttributeError('Debug requested error') 1005 return f(request, *args, **kwargs)
1006 wrap.func_name = f.func_name 1007 return wrap 1008
1009 -def jsonp (f):
1010 """ 1011 Decorator for adding connection debugging and returning function result as json, depending on 1012 values in kwargs 1013 1014 @param f: The function to wrap 1015 @return: The wrapped function, which will return json 1016 """ 1017 1018 def wrap (request, *args, **kwargs): 1019 logger.debug('jsonp') 1020 try: 1021 server_id = kwargs.get('server_id', None) 1022 if server_id is None: 1023 server_id = request.session['connector'].server_id 1024 kwargs['server_id'] = server_id 1025 rv = f(request, *args, **kwargs) 1026 if kwargs.get('_raw', False): 1027 return rv 1028 if isinstance(rv, HttpResponse): 1029 return rv 1030 rv = simplejson.dumps(rv) 1031 c = request.REQUEST.get('callback', None) 1032 if c is not None and not kwargs.get('_internal', False): 1033 rv = '%s(%s)' % (c, rv) 1034 if kwargs.get('_internal', False): 1035 return rv 1036 return HttpResponse(rv, mimetype='application/javascript') 1037 except omero.ServerError: 1038 if kwargs.get('_raw', False) or kwargs.get('_internal', False): 1039 raise 1040 return HttpResponseServerError('("error in call","%s")' % traceback.format_exc(), mimetype='application/javascript') 1041 except: 1042 logger.debug(traceback.format_exc()) 1043 if kwargs.get('_raw', False) or kwargs.get('_internal', False): 1044 raise 1045 return HttpResponseServerError('("error in call","%s")' % traceback.format_exc(), mimetype='application/javascript')
1046 wrap.func_name = f.func_name 1047 return wrap 1048
1049 @debug 1050 @login_required() 1051 -def render_row_plot (request, iid, z, t, y, conn=None, w=1, **kwargs):
1052 """ 1053 Renders the line plot for the image with id {{iid}} at {{z}} and {{t}} as gif with transparent background. 1054 Many options are available from the request dict. 1055 I am assuming a single Pixels object on image with Image ID='iid'. May be wrong 1056 TODO: cache 1057 1058 @param request: http request 1059 @param iid: Image ID 1060 @param z: Z index 1061 @param t: T index 1062 @param y: Y position of row to measure 1063 @param conn: L{omero.gateway.BlitzGateway} connection 1064 @param w: Line width 1065 @return: http response wrapping a gif 1066 """ 1067 1068 if not w: 1069 w = 1 1070 pi = _get_prepared_image(request, iid, conn=conn) 1071 if pi is None: 1072 raise Http404 1073 img, compress_quality = pi 1074 try: 1075 gif_data = img.renderRowLinePlotGif(int(z),int(t),int(y), int(w)) 1076 except: 1077 logger.debug('a', exc_info=True) 1078 raise 1079 if gif_data is None: 1080 raise Http404 1081 rsp = HttpResponse(gif_data, mimetype='image/gif') 1082 return rsp
1083
1084 @debug 1085 @login_required() 1086 -def render_col_plot (request, iid, z, t, x, w=1, conn=None, **kwargs):
1087 """ 1088 Renders the line plot for the image with id {{iid}} at {{z}} and {{t}} as gif with transparent background. 1089 Many options are available from the request dict. 1090 I am assuming a single Pixels object on image with id='iid'. May be wrong 1091 TODO: cache 1092 1093 @param request: http request 1094 @param iid: Image ID 1095 @param z: Z index 1096 @param t: T index 1097 @param x: X position of column to measure 1098 @param conn: L{omero.gateway.BlitzGateway} connection 1099 @param w: Line width 1100 @return: http response wrapping a gif 1101 """ 1102 1103 if not w: 1104 w = 1 1105 pi = _get_prepared_image(request, iid, conn=conn) 1106 if pi is None: 1107 raise Http404 1108 img, compress_quality = pi 1109 gif_data = img.renderColLinePlotGif(int(z),int(t),int(x), int(w)) 1110 if gif_data is None: 1111 raise Http404 1112 rsp = HttpResponse(gif_data, mimetype='image/gif') 1113 return rsp
1114
1115 @login_required() 1116 @jsonp 1117 -def imageData_json (request, conn=None, _internal=False, **kwargs):
1118 """ 1119 Get a dict with image information 1120 TODO: cache 1121 1122 @param request: http request 1123 @param conn: L{omero.gateway.BlitzGateway} 1124 @param _internal: TODO: ? 1125 @return: Dict 1126 """ 1127 1128 iid = kwargs['iid'] 1129 key = kwargs.get('key', None) 1130 image = conn.getObject("Image", iid) 1131 if image is None: 1132 return HttpResponseServerError('""', mimetype='application/javascript') 1133 rv = imageMarshal(image, key) 1134 return rv
1135
1136 @login_required() 1137 @jsonp 1138 -def wellData_json (request, conn=None, _internal=False, **kwargs):
1139 """ 1140 Get a dict with image information 1141 TODO: cache 1142 1143 @param request: http request 1144 @param conn: L{omero.gateway.BlitzGateway} 1145 @param _internal: TODO: ? 1146 @return: Dict 1147 """ 1148 1149 wid = kwargs['wid'] 1150 well = conn.getObject("Well", wid) 1151 if well is None: 1152 return HttpResponseServerError('""', mimetype='application/javascript') 1153 prefix = kwargs.get('thumbprefix', 'webgateway.views.render_thumbnail') 1154 def urlprefix(iid): 1155 return reverse(prefix, args=(iid,))
1156 xtra = {'thumbUrlPrefix': kwargs.get('urlprefix', urlprefix)} 1157 rv = well.simpleMarshal(xtra=xtra) 1158 return rv 1159
1160 @login_required() 1161 @jsonp 1162 -def plateGrid_json (request, pid, field=0, conn=None, **kwargs):
1163 """ 1164 """ 1165 plate = conn.getObject('plate', long(pid)) 1166 try: 1167 field = long(field or 0) 1168 except ValueError: 1169 field = 0 1170 if plate is None: 1171 return HttpResponseServerError('""', mimetype='application/javascript') 1172 grid = [] 1173 prefix = kwargs.get('thumbprefix', 'webgateway.views.render_thumbnail') 1174 thumbsize = int(request.REQUEST.get('size', 64)) 1175 logger.debug(thumbsize) 1176 1177 def urlprefix(iid): 1178 return reverse(prefix, args=(iid,thumbsize))
1179 xtra = {'thumbUrlPrefix': kwargs.get('urlprefix', urlprefix)} 1180 server_id = kwargs['server_id'] 1181 1182 rv = webgateway_cache.getJson(request, server_id, plate, 'plategrid-%d-%d' % (field, thumbsize)) 1183 if rv is None: 1184 plate.setGridSizeConstraints(8,12) 1185 for row in plate.getWellGrid(field): 1186 tr = [] 1187 for e in row: 1188 if e: 1189 i = e.getImage() 1190 if i: 1191 t = i.simpleMarshal(xtra=xtra) 1192 t['wellId'] = e.getId() 1193 t['field'] = field 1194 tr.append(t) 1195 continue 1196 tr.append(None) 1197 grid.append(tr) 1198 rv = {'grid': grid, 1199 'collabels': plate.getColumnLabels(), 1200 'rowlabels': plate.getRowLabels()} 1201 webgateway_cache.setJson(request, server_id, plate, simplejson.dumps(rv), 'plategrid-%d-%d' % (field, thumbsize)) 1202 else: 1203 rv = simplejson.loads(rv) 1204 return rv 1205
1206 @login_required() 1207 @jsonp 1208 -def listImages_json (request, did, conn=None, **kwargs):
1209 """ 1210 lists all Images in a Dataset, as json 1211 TODO: cache 1212 1213 @param request: http request 1214 @param did: Dataset ID 1215 @param conn: L{omero.gateway.BlitzGateway} 1216 @return: list of image json. 1217 """ 1218 1219 dataset = conn.getObject("Dataset", did) 1220 if dataset is None: 1221 return HttpResponseServerError('""', mimetype='application/javascript') 1222 prefix = kwargs.get('thumbprefix', 'webgateway.views.render_thumbnail') 1223 def urlprefix(iid): 1224 return reverse(prefix, args=(iid,))
1225 xtra = {'thumbUrlPrefix': kwargs.get('urlprefix', urlprefix), 1226 'tiled': request.REQUEST.get('tiled', False),} 1227 return map(lambda x: x.simpleMarshal(xtra=xtra), dataset.listChildren()) 1228
1229 @login_required() 1230 @jsonp 1231 -def listWellImages_json (request, did, conn=None, **kwargs):
1232 """ 1233 lists all Images in a Well, as json 1234 TODO: cache 1235 1236 @param request: http request 1237 @param did: Well ID 1238 @param conn: L{omero.gateway.BlitzGateway} 1239 @return: list of image json. 1240 """ 1241 1242 well = conn.getObject("Well", did) 1243 if well is None: 1244 return HttpResponseServerError('""', mimetype='application/javascript') 1245 prefix = kwargs.get('thumbprefix', 'webgateway.views.render_thumbnail') 1246 def urlprefix(iid): 1247 return reverse(prefix, args=(iid,))
1248 xtra = {'thumbUrlPrefix': kwargs.get('urlprefix', urlprefix)} 1249 return map(lambda x: x.getImage() and x.getImage().simpleMarshal(xtra=xtra), well.listChildren()) 1250
1251 @login_required() 1252 @jsonp 1253 -def listDatasets_json (request, pid, conn=None, **kwargs):
1254 """ 1255 lists all Datasets in a Project, as json 1256 TODO: cache 1257 1258 @param request: http request 1259 @param pid: Project ID 1260 @param conn: L{omero.gateway.BlitzGateway} 1261 @return: list of dataset json. 1262 """ 1263 1264 project = conn.getObject("Project", pid) 1265 rv = [] 1266 if project is None: 1267 return HttpResponse('[]', mimetype='application/javascript') 1268 return [x.simpleMarshal(xtra={'childCount':0}) for x in project.listChildren()]
1269
1270 @login_required() 1271 @jsonp 1272 -def datasetDetail_json (request, did, conn=None, **kwargs):
1273 """ 1274 return json encoded details for a dataset 1275 TODO: cache 1276 """ 1277 ds = conn.getObject("Dataset", did) 1278 return ds.simpleMarshal()
1279
1280 @login_required() 1281 @jsonp 1282 -def listProjects_json (request, conn=None, **kwargs):
1283 """ 1284 lists all Projects, as json 1285 TODO: cache 1286 1287 @param request: http request 1288 @param conn: L{omero.gateway.BlitzGateway} 1289 @return: list of project json. 1290 """ 1291 1292 rv = [] 1293 for pr in conn.listProjects(): 1294 rv.append( {'id': pr.id, 'name': pr.name, 'description': pr.description or ''} ) 1295 return rv
1296
1297 @login_required() 1298 @jsonp 1299 -def projectDetail_json (request, pid, conn=None, **kwargs):
1300 """ 1301 grab details from one specific project 1302 TODO: cache 1303 1304 @param request: http request 1305 @param pid: Project ID 1306 @param conn: L{omero.gateway.BlitzGateway} 1307 @return: project details as dict. 1308 """ 1309 1310 pr = conn.getObject("Project", pid) 1311 rv = pr.simpleMarshal() 1312 return rv
1313
1314 -def searchOptFromRequest (request):
1315 """ 1316 Returns a dict of options for searching, based on 1317 parameters in the http request 1318 Request keys include: 1319 - ctx: (http request) 'imgs' to search only images 1320 - text: (http request) the actual text phrase 1321 - start: starting index (0 based) for result 1322 - limit: nr of results to retuen (0 == unlimited) 1323 - author: 1324 - grabData: 1325 - parents: 1326 1327 @param request: http request 1328 @return: Dict of options 1329 """ 1330 1331 try: 1332 r = request.REQUEST 1333 opts = { 1334 'search': unicode(r.get('text', '')).encode('utf8'), 1335 'ctx': r.get('ctx', ''), 1336 'grabData': not not r.get('grabData', False), 1337 'parents': not not bool(r.get('parents', False)), 1338 'start': int(r.get('start', 0)), 1339 'limit': int(r.get('limit', 0)), 1340 'key': r.get('key', None) 1341 } 1342 author = r.get('author', '') 1343 if author: 1344 opts['search'] += ' author:'+author 1345 return opts 1346 except: 1347 logger.error(traceback.format_exc()) 1348 return {}
1349
1350 @TimeIt(logging.INFO) 1351 @login_required() 1352 @jsonp 1353 -def search_json (request, conn=None, **kwargs):
1354 """ 1355 Search for objects in blitz. 1356 Returns json encoded list of marshalled objects found by the search query 1357 Request keys include: 1358 - text: The text to search for 1359 - ctx: (http request) 'imgs' to search only images 1360 - text: (http request) the actual text phrase 1361 - start: starting index (0 based) for result 1362 - limit: nr of results to retuen (0 == unlimited) 1363 - author: 1364 - grabData: 1365 - parents: 1366 1367 @param request: http request 1368 @param conn: L{omero.gateway.BlitzGateway} 1369 @return: json search results 1370 TODO: cache 1371 """ 1372 opts = searchOptFromRequest(request) 1373 rv = [] 1374 logger.debug("searchObjects(%s)" % (opts['search'])) 1375 # search returns blitz_connector wrapper objects 1376 def urlprefix(iid): 1377 return reverse('webgateway.views.render_thumbnail', args=(iid,))
1378 xtra = {'thumbUrlPrefix': kwargs.get('urlprefix', urlprefix)} 1379 pks = None 1380 try: 1381 if opts['ctx'] == 'imgs': 1382 sr = conn.searchObjects(["image"], opts['search'], conn.SERVICE_OPTS) 1383 else: 1384 sr = conn.searchObjects(None, opts['search'], conn.SERVICE_OPTS) # searches P/D/I 1385 except ApiUsageException: 1386 return HttpResponseServerError('"parse exception"', mimetype='application/javascript') 1387 def marshal (): 1388 rv = [] 1389 if (opts['grabData'] and opts['ctx'] == 'imgs'): 1390 bottom = min(opts['start'], len(sr)-1) 1391 if opts['limit'] == 0: 1392 top = len(sr) 1393 else: 1394 top = min(len(sr), bottom + opts['limit']) 1395 for i in range(bottom, top): 1396 e = sr[i] 1397 #for e in sr: 1398 try: 1399 rv.append(imageData_json(request, server_id, iid=e.id, key=opts['key'], conn=conn, _internal=True)) 1400 except AttributeError, x: 1401 logger.debug('(iid %i) ignoring Attribute Error: %s' % (e.id, str(x))) 1402 pass 1403 except omero.ServerError, x: 1404 logger.debug('(iid %i) ignoring Server Error: %s' % (e.id, str(x))) 1405 return rv 1406 else: 1407 return map(lambda x: x.simpleMarshal(xtra=xtra, parents=opts['parents']), sr) 1408 rv = timeit(marshal)() 1409 logger.debug(rv) 1410 return rv 1411
1412 @login_required() 1413 -def save_image_rdef_json (request, iid, conn=None, **kwargs):
1414 """ 1415 Requests that the rendering defs passed in the request be set as the default for this image. 1416 Rendering defs in request listed at L{getImgDetailsFromReq} 1417 TODO: jsonp 1418 1419 @param request: http request 1420 @param iid: Image ID 1421 @param conn: L{omero.gateway.BlitzGateway} 1422 @return: http response 'true' or 'false' 1423 """ 1424 server_id = request.session['connector'].server_id 1425 r = request.REQUEST 1426 pi = _get_prepared_image(request, iid, server_id=server_id, conn=conn, saveDefs=True) 1427 if pi is None: 1428 json_data = 'false' 1429 else: 1430 user_id = pi[0]._conn.getEventContext().userId 1431 webgateway_cache.invalidateObject(server_id, user_id, pi[0]) 1432 pi[0].getThumbnail() 1433 json_data = 'true' 1434 if r.get('callback', None): 1435 json_data = '%s(%s)' % (r['callback'], json_data) 1436 return HttpResponse(json_data, mimetype='application/javascript')
1437
1438 @login_required() 1439 -def list_compatible_imgs_json (request, iid, conn=None, **kwargs):
1440 """ 1441 Lists the images on the same project that would be viable targets for copying rendering settings. 1442 TODO: change method to: 1443 list_compatible_imgs_json (request, iid, server_id=None, conn=None, **kwargs): 1444 1445 @param request: http request 1446 @param iid: Image ID 1447 @param conn: L{omero.gateway.BlitzGateway} 1448 @return: json list of image IDs 1449 """ 1450 1451 json_data = 'false' 1452 r = request.REQUEST 1453 if conn is None: 1454 img = None 1455 else: 1456 img = conn.getObject("Image", iid) 1457 1458 if img is not None: 1459 # List all images in project 1460 imgs = [] 1461 for ds in img.getProject().listChildren(): 1462 imgs.extend(ds.listChildren()) 1463 # Filter the ones that would pass the applySettingsToImages call 1464 img_ptype = img.getPrimaryPixels().getPixelsType().getValue() 1465 img_ccount = img.getSizeC() 1466 img_ew = [x.getLabel() for x in img.getChannels()] 1467 img_ew.sort() 1468 def compat (i): 1469 if long(i.getId()) == long(iid): 1470 return False 1471 pp = i.getPrimaryPixels() 1472 if pp is None or \ 1473 i.getPrimaryPixels().getPixelsType().getValue() != img_ptype or \ 1474 i.getSizeC() != img_ccount: 1475 return False 1476 ew = [x.getLabel() for x in i.getChannels()] 1477 ew.sort() 1478 if ew != img_ew: 1479 return False 1480 return True
1481 imgs = filter(compat, imgs) 1482 json_data = simplejson.dumps([x.getId() for x in imgs]) 1483 1484 if r.get('callback', None): 1485 json_data = '%s(%s)' % (r['callback'], json_data) 1486 return HttpResponse(json_data, mimetype='application/javascript') 1487
1488 @login_required() 1489 @jsonp 1490 -def copy_image_rdef_json (request, conn=None, **kwargs):
1491 """ 1492 If 'fromid' is in request, copy the image ID to session, 1493 for applying later using this same method. 1494 If list of 'toids' is in request, paste the image ID from the session 1495 to the specified images. 1496 If 'fromid' AND 'toids' are in the reqest, we simply 1497 apply settings and don't save anything to request. 1498 If 'to_type' is in request, this can be 'dataset', 'plate', 'acquisition' 1499 Returns json dict of Boolean:[Image-IDs] for images that have successfully 1500 had the rendering settings applied, or not. 1501 1502 @param request: http request 1503 @param server_id: 1504 @param conn: L{omero.gateway.BlitzGateway} 1505 @return: json dict of Boolean:[Image-IDs] 1506 """ 1507 1508 server_id = request.session['connector'].server_id 1509 json_data = False 1510 r = request.REQUEST 1511 fromid = r.get('fromid', None) 1512 toids = r.getlist('toids') 1513 to_type = str(r.get('to_type', 'image')) 1514 if to_type not in ('dataset', 'plate', 'acquisition'): 1515 to_type = "Image" # default is image 1516 # Only 'fromid' is given, simply save to session 1517 if fromid is not None and len(toids) == 0: 1518 request.session.modified = True 1519 request.session['fromid'] = fromid 1520 return True 1521 # Check session for 'fromid' 1522 if fromid is None: 1523 fromid = request.session.get('fromid', None) 1524 # If we have both, apply settings... 1525 try: 1526 fromid = long(fromid) 1527 toids = map(lambda x: long(x), toids) 1528 except TypeError: 1529 fromid = None 1530 except ValueError: 1531 fromid = None 1532 if fromid is not None and len(toids) > 0: 1533 fromimg = conn.getObject("Image", fromid) 1534 userid = fromimg.getOwner().getId() 1535 json_data = conn.applySettingsToSet(fromid, to_type, toids) 1536 if json_data and True in json_data: 1537 for iid in json_data[True]: 1538 img = conn.getObject("Image", iid) 1539 img is not None and webgateway_cache.invalidateObject(server_id, userid, img) 1540 return json_data
1541 #
1542 # json_data = simplejson.dumps(json_data) 1543 # 1544 # if r.get('callback', None): 1545 # json_data = '%s(%s)' % (r['callback'], json_data) 1546 # return HttpResponse(json_data, mimetype='application/javascript') 1547 1548 @login_required() 1549 @jsonp 1550 -def reset_image_rdef_json (request, iid, conn=None, **kwargs):
1551 """ 1552 Try to remove all rendering defs the logged in user has for this image. 1553 1554 @param request: http request 1555 @param iid: Image ID 1556 @param conn: L{omero.gateway.BlitzGateway} 1557 @return: json 'true', or 'false' if failed 1558 """ 1559 1560 img = conn.getObject("Image", iid) 1561 1562 if img is not None and img.resetRDefs(): 1563 user_id = conn.getEventContext().userId 1564 server_id = request.session['connector'].server_id 1565 webgateway_cache.invalidateObject(server_id, user_id, img) 1566 return True 1567 json_data = 'true' 1568 else: 1569 json_data = 'false' 1570 return False
1571 # if _conn is not None:
1572 # return json_data == 'true' # TODO: really return a boolean? (not json) 1573 # if r.get('callback', None): 1574 # json_data = '%s(%s)' % (r['callback'], json_data) 1575 # return HttpResponse(json_data, mimetype='application/javascript') 1576 1577 @login_required() 1578 -def full_viewer (request, iid, conn=None, **kwargs):
1579 """ 1580 This view is responsible for showing the omero_image template 1581 Image rendering options in request are used in the display page. See L{getImgDetailsFromReq}. 1582 1583 @param request: http request. 1584 @param iid: Image ID 1585 @param conn: L{omero.gateway.BlitzGateway} 1586 @param **kwargs: Can be used to specify the html 'template' for rendering 1587 @return: html page of image and metadata 1588 """ 1589 1590 rid = getImgDetailsFromReq(request) 1591 try: 1592 image = conn.getObject("Image", iid) 1593 if image is None: 1594 logger.debug("(a)Image %s not found..." % (str(iid))) 1595 raise Http404 1596 d = {'blitzcon': conn, 1597 'image': image, 1598 'parents': image.getAncestry(), 1599 'opts': rid, 1600 'roiCount': image.getROICount(), 1601 'viewport_server': kwargs.get('viewport_server', reverse('webgateway')), 1602 'object': 'image:%i' % int(iid)} 1603 if len(d['parents']) > 1 and isinstance(d['parents'][1], omero.gateway.WellWrapper): 1604 # The following calls are needed as the Well children collections have 1605 # been loaded with only the one child object, and we need all the 1606 # existing children (fields) 1607 d['parents'][1].__loadedHotSwap__() 1608 d['parents'][1].__reset__() 1609 d['wellFields'] = [d['parents'][1].getImage(x).id\ 1610 for x in range(d['parents'][1].countWellSample())] 1611 template = kwargs.get('template', "webgateway/viewport/omero_image.html") 1612 t = template_loader.get_template(template) 1613 c = Context(request,d) 1614 rsp = t.render(c) 1615 except omero.SecurityViolation: 1616 raise Http404 1617 return HttpResponse(rsp)
1618
1619 1620 @login_required(doConnectionCleanup=False) 1621 -def archived_files(request, iid, conn=None, **kwargs):
1622 """ 1623 Downloads the archived file(s) as a single file or as a zip (if more than one file) 1624 """ 1625 image = conn.getObject("Image", iid) 1626 if image is None: 1627 logger.debug("Cannot download archived file becuase Image does not exist.") 1628 return HttpResponseServerError("Cannot download archived file becuase Image does not exist (id:%s)." % (iid)) 1629 1630 files = list(image.getImportedImageFiles()) 1631 1632 if len(files) == 0: 1633 logger.debug("Tried downloading archived files from image with no files archived.") 1634 return HttpResponseServerError("This image has no Archived Files.") 1635 1636 if len(files) == 1: 1637 orig_file = files[0] 1638 rsp = ConnCleaningHttpResponse(orig_file.getFileInChunks()) 1639 rsp.conn = conn 1640 rsp['Content-Length'] = orig_file.getSize() 1641 rsp['Content-Disposition'] = 'attachment; filename=%s' % (orig_file.getName().replace(" ","_")) 1642 else: 1643 import tempfile 1644 temp = tempfile.NamedTemporaryFile(suffix='.archive') 1645 try: 1646 temp_zip_dir = tempfile.mkdtemp() 1647 logger.debug("download dir: %s" % temp_zip_dir) 1648 try: 1649 for a in files: 1650 temp_f = os.path.join(temp_zip_dir, a.name) 1651 f = open(str(temp_f),"wb") 1652 try: 1653 for chunk in a.getFileInChunks(): 1654 f.write(chunk) 1655 finally: 1656 f.close() 1657 1658 # create zip 1659 zip_file = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED) 1660 try: 1661 a_files = os.path.join(temp_zip_dir, "*") 1662 for name in glob.glob(a_files): 1663 zip_file.write(name, os.path.basename(name)) 1664 finally: 1665 zip_file.close() 1666 # delete temp dir 1667 finally: 1668 shutil.rmtree(temp_zip_dir, ignore_errors=True) 1669 1670 file_name = "%s.zip" % image.getName().replace(" ","_") 1671 1672 # return the zip or single file 1673 archivedFile_data = FileWrapper(temp) 1674 rsp = ConnCleaningHttpResponse(archivedFile_data) 1675 rsp.conn = conn 1676 rsp['Content-Length'] = temp.tell() 1677 rsp['Content-Disposition'] = 'attachment; filename=%s' % file_name 1678 temp.seek(0) 1679 except Exception, x: 1680 temp.close() 1681 stack = traceback.format_exc() 1682 logger.error(stack) 1683 return HttpResponseServerError("Cannot download file (id:%s).\n%s" % (iid, stack)) 1684 1685 rsp['Content-Type'] = 'application/force-download' 1686 return rsp
1687
1688 1689 @login_required() 1690 @jsonp 1691 -def original_file_paths(request, iid, conn=None, **kwargs):
1692 """ Get a list of path/name strings for original files associated with the imgae """ 1693 1694 image = conn.getObject("Image", iid) 1695 if image is None: 1696 logger.debug("Cannot get original file paths becuase Image does not exist.") 1697 return HttpResponseServerError("Cannot get original file paths becuase Image does not exist (id:%s)." % (iid)) 1698 1699 files = list(image.getImportedImageFiles()) 1700 1701 if len(files) == 0: 1702 return HttpResponseServerError("This image has no Original Files.") 1703 1704 fileNames = [ f.getPath() + f.getName() for f in files] 1705 return fileNames
1706
1707 1708 @login_required() 1709 -def get_shape_json(request, roiId, shapeId, conn=None, **kwargs):
1710 roiId = int(roiId) 1711 shapeId = int(shapeId) 1712 shape = conn.getQueryService().findByQuery( 1713 'select shape from Roi as roi ' \ 1714 'join roi.shapes as shape ' \ 1715 'where roi.id = %d and shape.id = %d' % (roiId, shapeId), 1716 None) 1717 logger.debug('Shape: %r' % shape) 1718 if shape is None: 1719 logger.debug('No such shape: %r' % shapeId) 1720 raise Http404 1721 return HttpResponse(simplejson.dumps(shapeMarshal(shape)), 1722 mimetype='application/javascript')
1723
1724 @login_required() 1725 -def get_rois_json(request, imageId, conn=None, **kwargs):
1726 """ 1727 Returns json data of the ROIs in the specified image. 1728 """ 1729 rois = [] 1730 roiService = conn.getRoiService() 1731 #rois = webfigure_utils.getRoiShapes(roiService, long(imageId)) # gets a whole json list of ROIs 1732 result = roiService.findByImage(long(imageId), None, conn.SERVICE_OPTS) 1733 1734 for r in result.rois: 1735 roi = {} 1736 roi['id'] = r.getId().getValue() 1737 # go through all the shapes of the ROI 1738 shapes = [] 1739 for s in r.copyShapes(): 1740 if s is None: # seems possible in some situations 1741 continue 1742 shapes.append(shapeMarshal(s)) 1743 # sort shapes by Z, then T. 1744 shapes.sort(key=lambda x: 1745 "%03d%03d"% (x.get('theZ', -1), x.get('theT', -1))); 1746 roi['shapes'] = shapes 1747 rois.append(roi) 1748 1749 rois.sort(key=lambda x: x['id']) # sort by ID - same as in measurement tool. 1750 1751 return HttpResponse(simplejson.dumps(rois), mimetype='application/javascript')
1752
1753 1754 -def test (request):
1755 """ 1756 Tests the L{full_viewer} with no args passed to the template. 1757 1758 @param request: http request. 1759 @return: blank page template 1760 """ 1761 1762 context = {} 1763 1764 t = template_loader.get_template('webgateway/viewport/omero_image.html') 1765 c = Context(request,context) 1766 return HttpResponse(t.render(c))
1767 1768 @login_required(isAdmin=True)
1769 @jsonp 1770 -def su (request, user, conn=None, **kwargs):
1771 """ 1772 If current user is admin, switch the session to a new connection owned by 'user' 1773 (puts the new session ID in the request.session) 1774 Return False if not possible 1775 1776 @param request: http request. 1777 @param user: Username of new connection owner 1778 @param conn: L{omero.gateway.BlitzGateway} 1779 @param **kwargs: Can be used to specify the html 'template' for rendering 1780 @return: Boolean 1781 """ 1782 conn.setGroupNameForSession('system') 1783 connector = request.session['connector'] 1784 connector = Connector(connector.server_id, connector.is_secure) 1785 session = conn.getSessionService().getSession(conn._sessionUuid) 1786 ttl = session.getTimeToIdle().val 1787 connector.omero_session_key = conn.suConn(user, ttl=ttl)._sessionUuid 1788 request.session['connector'] = connector 1789 conn.revertGroupForSession() 1790 conn.seppuku() 1791 return True
1792
1793 1794 -def _annotations(request, objtype, objid, conn=None, **kwargs):
1795 """ 1796 Retrieve annotations for object specified by object type and identifier, 1797 optionally traversing object model graph. 1798 Returns dictionary containing annotations in NSBULKANNOTATIONS namespace 1799 if successful, error information otherwise 1800 1801 Example: /annotations/Plate/1/ 1802 retrieves annotations for plate with identifier 1 1803 Example: /annotations/Plate.wells/1/ 1804 retrieves annotations for plate that contains well with 1805 identifier 1 1806 Example: /annotations/Screen.plateLinks.child.wells/22/ 1807 retrieves annotations for screen that contains plate with 1808 well with identifier 22 1809 1810 @param request: http request. 1811 @param objtype: Type of target object, or type of target object followed 1812 by a slash-separated list of properties to resolve 1813 @param objid: Identifier of target object, or identifier of object 1814 reached by resolving given properties 1815 @param conn: L{omero.gateway.BlitzGateway} 1816 @param **kwargs: unused 1817 @return: A dictionary with key 'error' with an error message or 1818 with key 'data' containing an array of dictionaries 1819 with keys 'id' and 'file' of the retrieved annotations 1820 """ 1821 q = conn.getQueryService() 1822 # If more than one objtype is specified, use all in query to 1823 # traverse object model graph 1824 # Example: /annotations/Plate/wells/1/ 1825 # retrieves annotations from Plate that contains Well 1 1826 objtype = objtype.split('.') 1827 1828 query = "select obj0 from %s obj0\n" % objtype[0] 1829 for i, t in enumerate(objtype[1:]): 1830 query += "join fetch obj%d.%s obj%d\n" % (i, t, i+1) 1831 query += """ 1832 left outer join fetch obj0.annotationLinks links 1833 left outer join fetch links.child 1834 where obj%d.id=:id""" % (len(objtype) - 1) 1835 1836 try: 1837 obj = q.findByQuery(query, omero.sys.ParametersI().addId(objid), 1838 conn.createServiceOptsDict()) 1839 except omero.QueryException, ex: 1840 return dict(error='%s cannot be queried' % objtype, 1841 query=query) 1842 1843 if not obj: 1844 return dict(error='%s with id %s not found' % (objtype, objid)) 1845 1846 return dict(data=[ 1847 dict(id=annotation.id.val, 1848 file=annotation.file.id.val) 1849 for annotation in obj.linkedAnnotationList() 1850 if unwrap(annotation.getNs()) == NSBULKANNOTATIONS 1851 ])
1852 1853 annotations = login_required()(jsonp(_annotations))
1854 1855 1856 -def _table_query(request, fileid, conn=None, **kwargs):
1857 """ 1858 Query a table specified by fileid 1859 Returns a dictionary with query result if successful, error information 1860 otherwise 1861 1862 @param request: http request; querystring must contain key 'query' 1863 with query to be executed, or '*' to retrieve all rows. 1864 If query is in the format word-number, e.g. "Well-7", 1865 if will be run as (word==number), e.g. "(Well==7)". 1866 This is supported to allow more readable query strings. 1867 @param fileid: Numeric identifier of file containing the table 1868 @param conn: L{omero.gateway.BlitzGateway} 1869 @param **kwargs: unused 1870 @return: A dictionary with key 'error' with an error message 1871 or with key 'data' containing a dictionary with keys 1872 'columns' (an array of column names) and 'rows' 1873 (an array of rows, each an array of values) 1874 """ 1875 query = request.GET.get('query') 1876 if not query: 1877 return dict(error='Must specify query parameter, use * to retrieve all') 1878 1879 r = conn.getSharedResources() 1880 t = r.openTable(omero.model.OriginalFileI(fileid), 1881 conn.createServiceOptsDict()) 1882 if not t: 1883 return dict(error="Table %s not found" % fileid) 1884 1885 cols = t.getHeaders() 1886 rows = t.getNumberOfRows() 1887 1888 if query == '*': 1889 hits = range(rows) 1890 else: 1891 match = re.match(r'^(\w+)-(\d+)', query) 1892 if match: 1893 query = '(%s==%s)' % (match.group(1), match.group(2)) 1894 try: 1895 hits = t.getWhereList(query, None, 0, rows, 1) 1896 except Exception, e: 1897 return dict(error='Error executing query: %s' % query) 1898 1899 return dict(data=dict( 1900 columns=[col.name for col in cols], 1901 rows=[ 1902 [col.values[0] for col in t.read(range(len(cols)), hit, hit+1).columns] 1903 for hit in hits 1904 ], 1905 ) 1906 )
1907 1908 table_query = login_required()(jsonp(_table_query))
1909 1910 1911 @login_required() 1912 @jsonp 1913 -def object_table_query(request, objtype, objid, conn=None, **kwargs):
1914 """ 1915 Query bulk annotations table attached to an object specified by 1916 object type and identifier, optionally traversing object model graph. 1917 Returns a dictionary with query result if successful, error information 1918 otherwise 1919 1920 Example: /table/Plate/1/query/?query=* 1921 queries bulk annotations table for plate with identifier 1 1922 Example: /table/Plate.wells/1/query/?query=* 1923 queries bulk annotations table for plate that contains well with 1924 identifier 1 1925 Example: /table/Screen.plateLinks.child.wells/22/query/?query=Well-22 1926 queries bulk annotations table for screen that contains plate with 1927 well with identifier 22 1928 1929 @param request: http request. 1930 @param objtype: Type of target object, or type of target object followed 1931 by a slash-separated list of properties to resolve 1932 @param objid: Identifier of target object, or identifier of object 1933 reached by resolving given properties 1934 @param conn: L{omero.gateway.BlitzGateway} 1935 @param **kwargs: unused 1936 @return: A dictionary with key 'error' with an error message 1937 or with key 'data' containing a dictionary with keys 1938 'columns' (an array of column names) and 'rows' 1939 (an array of rows, each an array of values) 1940 """ 1941 a = _annotations(request, objtype, objid, conn, **kwargs) 1942 if (a.has_key('error')): 1943 return a 1944 1945 if len(a['data']) < 1: 1946 return dict(error='Could not retrieve bulk annotations table') 1947 1948 # multiple bulk annotations files could be attached, use the most recent 1949 # one (= the one with the highest identifier) 1950 fileId = max(annotation['file'] for annotation in a['data']) 1951 return _table_query(request, fileId, conn, **kwargs)
1952