1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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:
67 try:
68 import Image
69 import ImageDraw
70 except:
71 logger.error('No PIL installed')
75 return unicode(s).encode('utf-8')
76
78 """
79 Represents the current user of the connection, with methods delegating to the connection itself.
80 """
81
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
94 """ Sets the loggedIn Flag to True """
95
96 self.loggedIn = True
97
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
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
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
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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
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
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
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
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
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:
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
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
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
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)
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
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
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
481
482
483 x,y,w,h = bBox
484 requiredWidth = max(w,h*3/2)
485 requiredHeight = requiredWidth*2/3
486 newW = int(requiredWidth * 1.5)
487 newH = int(requiredHeight * 1.5)
488
489 if newW < MAX_WIDTH:
490 newW = MAX_WIDTH
491 newH = newW*2/3
492
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
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
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
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
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
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)
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)
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)
582 elif 'xyList' in shape:
583
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
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:
597 x2 = start_x + 1
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
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
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
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
677
678
679
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
700
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
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
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
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
877
878 fnamemax = 255 - len(str(obj.getId())) - 10
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
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)
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
932 if fpath is None:
933 import tempfile
934 fo, fn = tempfile.mkstemp()
935 else:
936 fn = fpath
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
943
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
955 return HttpResponseRedirect(settings.STATIC_URL + 'webgateway/tfiles/' + rpath)
956 except:
957 logger.debug(traceback.format_exc())
958 raise
959
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
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
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
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
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)
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
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
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
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
1460 imgs = []
1461 for ds in img.getProject().listChildren():
1462 imgs.extend(ds.listChildren())
1463
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
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"
1516
1517 if fromid is not None and len(toids) == 0:
1518 request.session.modified = True
1519 request.session['fromid'] = fromid
1520 return True
1521
1522 if fromid is None:
1523 fromid = request.session.get('fromid', None)
1524
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
1543
1544
1545
1546
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
1572
1573
1574
1575
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
1605
1606
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
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
1667 finally:
1668 shutil.rmtree(temp_zip_dir, ignore_errors=True)
1669
1670 file_name = "%s.zip" % image.getName().replace(" ","_")
1671
1672
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
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
1738 shapes = []
1739 for s in r.copyShapes():
1740 if s is None:
1741 continue
1742 shapes.append(shapeMarshal(s))
1743
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'])
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):
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
1823
1824
1825
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
1949
1950 fileId = max(annotation['file'] for annotation in a['data'])
1951 return _table_query(request, fileId, conn, **kwargs)
1952