1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 ''' A view functions is simply a Python function that takes a Web request and
27 returns a Web response. This response can be the HTML contents of a Web page,
28 or a redirect, or the 404 and 500 error, or an XML document, or an image...
29 or anything.'''
30
31 import sys
32 import copy
33 import re
34 import os
35 import calendar
36 import cStringIO
37 import datetime
38 import httplib
39 import Ice
40 import locale
41 import logging
42 import traceback
43
44 import shutil
45 import zipfile
46
47 from time import time
48 from thread import start_new_thread
49
50 from omero_version import omero_version
51 import omero, omero.scripts
52 from omero.rtypes import *
53
54 from django.conf import settings
55 from django.contrib.sessions.backends.cache import SessionStore
56 from django.core import template_loader
57 from django.core.cache import cache
58 from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseServerError, HttpResponseForbidden
59 from django.shortcuts import render_to_response
60 from django.template import RequestContext as Context
61 from django.utils import simplejson
62 from django.utils.http import urlencode
63 from django.views.defaults import page_not_found, server_error
64 from django.views import debug
65 from django.core.urlresolvers import reverse
66 from django.utils.translation import ugettext_lazy as _
67 from django.utils.encoding import smart_str
68 from django.core.servers.basehttp import FileWrapper
69
70 from webclient.webclient_gateway import OmeroWebGateway
71
72 from webclient_http import HttpJavascriptRedirect, HttpJavascriptResponse, HttpLoginRedirect
73
74 from webclient_utils import _formatReport, _purgeCallback
75 from forms import ShareForm, BasketShareForm, \
76 ContainerForm, ContainerNameForm, ContainerDescriptionForm, \
77 CommentAnnotationForm, TagsAnnotationForm, \
78 UsersForm, ActiveGroupForm, \
79 MetadataFilterForm, MetadataDetectorForm, MetadataChannelForm, \
80 MetadataEnvironmentForm, MetadataObjectiveForm, MetadataObjectiveSettingsForm, MetadataStageLabelForm, \
81 MetadataLightSourceForm, MetadataDichroicForm, MetadataMicroscopeForm, \
82 FilesAnnotationForm, WellIndexForm
83
84 from controller import BaseController
85 from controller.index import BaseIndex
86 from controller.basket import BaseBasket
87 from controller.container import BaseContainer
88 from controller.help import BaseHelp
89 from controller.history import BaseCalendar
90 from controller.impexp import BaseImpexp
91 from controller.search import BaseSearch
92 from controller.share import BaseShare
93
94 from omeroweb.connector import Server
95
96 from omeroweb.webadmin.forms import LoginForm
97 from omeroweb.webadmin.webadmin_utils import toBoolean, upgradeCheck
98
99 from omeroweb.webgateway import views as webgateway_views
100
101 from omeroweb.feedback.views import handlerInternalError
102
103 from omeroweb.webclient.decorators import login_required
104 from omeroweb.webclient.decorators import render_response
105 from omeroweb.connector import Connector
106 from omeroweb.decorators import ConnCleaningHttpResponse
107
108 logger = logging.getLogger(__name__)
109
110 logger.info("INIT '%s'" % os.getpid())
119
120
121
122
123 -def login(request):
124 """
125 Webclient Login - Also can be used by other Apps to log in to OMERO.
126 Uses the 'server' id from request to lookup the server-id (index), host and port from settings. E.g. "localhost", 4064.
127 Stores these details, along with username, password etc in the request.session.
128 Resets other data parameters in the request.session.
129 Tries to get connection to OMERO and if this works, then we are redirected to the 'index' page or url specified in REQUEST.
130 If we can't connect, the login page is returned with appropriate error messages.
131 """
132
133 request.session.modified = True
134
135 conn = None
136 error = None
137
138 server_id = request.REQUEST.get('server')
139 form = LoginForm(data=request.REQUEST.copy())
140 useragent = 'OMERO.web'
141 if form.is_valid():
142 username = form.cleaned_data['username']
143 password = form.cleaned_data['password']
144 server_id = form.cleaned_data['server']
145 is_secure = toBoolean(form.cleaned_data['ssl'])
146
147 connector = Connector(server_id, is_secure)
148
149
150 if server_id is not None and username is not None and password is not None \
151 and connector.check_version(useragent):
152 conn = connector.create_connection(useragent, username, password)
153 if conn is not None:
154
155 userGroupId = conn.getAdminService().getSecurityRoles().userGroupId
156 if userGroupId in conn.getEventContext().memberOfGroups:
157 request.session['connector'] = connector
158 upgradeCheck()
159
160
161 if request.session.get('active_group'):
162 if request.session.get('active_group') not in conn.getEventContext().memberOfGroups:
163 del request.session['active_group']
164 if request.session.get('user_id'):
165 del request.session['user_id']
166
167
168 if request.REQUEST.get('noredirect'):
169 return HttpResponse('OK')
170 url = request.REQUEST.get("url")
171 if url is not None and len(url) != 0:
172 return HttpResponseRedirect(url)
173 else:
174 return HttpResponseRedirect(reverse("webindex"))
175 elif username == "guest":
176 error = "Guest account is for internal OMERO use only. Not for login."
177 else:
178 error = "This user is not active."
179
180
181 if request.method == 'POST' and server_id is not None:
182 connector = Connector(server_id, True)
183 if not connector.is_server_up(useragent):
184 error = "Server is not responding, please contact administrator."
185 elif not connector.check_version(useragent):
186 error = "Client version does not match server, please contact administrator."
187 else:
188 error = "Connection not available, please check your user name and password."
189 url = request.REQUEST.get("url")
190
191 template = "webclient/login.html"
192 if request.method != 'POST':
193 if server_id is not None:
194 initial = {'server': unicode(server_id)}
195 form = LoginForm(initial=initial)
196 else:
197 form = LoginForm()
198
199 context = {"version": omero_version, 'error':error, 'form':form}
200 if url is not None and len(url) != 0:
201 context['url'] = urlencode({'url':url})
202
203 t = template_loader.get_template(template)
204 c = Context(request, context)
205 rsp = t.render(c)
206 return HttpResponse(rsp)
207
208 @login_required(ignore_login_fail=True)
209 -def keepalive_ping(request, conn=None, **kwargs):
210 """ Keeps the OMERO session alive by pinging the server """
211
212
213 return HttpResponse("OK")
214
215 @login_required()
216 @render_response()
217 -def feed(request, conn=None, **kwargs):
218 """
219 Viewing this page doesn't perform any action. All we do here is assemble various data for display.
220 Last imports, tag cloud etc are retrived via separate AJAX calls.
221 """
222 template = "webclient/index/index.html"
223
224 controller = BaseIndex(conn)
225
226 context = {'controller':controller}
227 context['template'] = template
228 return context
229
230
231 @login_required()
232 @render_response()
233 -def index_last_imports(request, conn=None, **kwargs):
244
245 @login_required()
246 @render_response()
247 -def index_most_recent(request, conn=None, **kwargs):
248 """ Gets the most recent 'shares' and 'share' comments. Used by the homepage via AJAX call """
249
250 controller = BaseIndex(conn)
251 controller.loadMostRecent()
252
253 context = {'controller':controller}
254 context['template'] = "webclient/index/index_most_recent.html"
255 return context
256
257 @login_required()
258 @render_response()
259 -def index_tag_cloud(request, conn=None, **kwargs):
268
271 """
272 Simply changes the request.session['active_group'] which is then used by the
273 @login_required decorator to configure conn for any group-based queries.
274 Finally this redirects to the 'url'.
275 """
276 switch_active_group(request)
277 url = url or reverse("webindex")
278 return HttpResponseRedirect(url)
279
281 """
282 Simply changes the request.session['active_group'] which is then used by the
283 @login_required decorator to configure conn for any group-based queries.
284 """
285 if active_group is None:
286 active_group = request.REQUEST.get('active_group')
287 active_group = int(active_group)
288 if 'active_group' not in request.session or active_group != request.session['active_group']:
289 request.session.modified = True
290 request.session['active_group'] = active_group
291 request.session['imageInBasket'] = set()
292 request.session['basket_counter'] = 0
293
294 @login_required(login_redirect='webindex')
295 -def logout(request, conn=None, **kwargs):
311
312
313
314 @login_required()
315 @render_response()
316 -def load_template(request, menu, conn=None, url=None, **kwargs):
317 """
318 This view handles most of the top-level pages, as specified by 'menu' E.g. userdata, usertags, history, search etc.
319 Query string 'path' that specifies an object to display in the data tree is parsed.
320 We also prepare the list of users in the current group, for the switch-user form. Change-group form is also prepared.
321 """
322 request.session.modified = True
323
324 if menu == 'userdata':
325 template = "webclient/data/containers.html"
326 elif menu == 'usertags':
327 template = "webclient/data/container_tags.html"
328 else:
329 template = "webclient/%s/%s.html" % (menu,menu)
330
331
332 init = {'initially_open':None, 'initially_select': []}
333 first_sel = None
334 initially_open_owner = None
335
336 path = request.REQUEST.get('path', '')
337 i = path.split("|")[-1]
338 if i.split("=")[0] in ('project', 'dataset', 'image', 'screen', 'plate', 'tag'):
339 init['initially_select'].append(str(i).replace("=",'-'))
340
341 show = request.REQUEST.get('show', '')
342 for i in show.split("|"):
343 if i.split("-")[0] in ('project', 'dataset', 'image', 'screen', 'plate', 'tag', 'acquisition', 'run', 'well'):
344 i = i.replace('run', 'acquisition')
345 init['initially_select'].append(str(i))
346 if len(init['initially_select']) > 0:
347
348 init['initially_open'] = [ init['initially_select'][0] ]
349 first_obj, first_id = init['initially_open'][0].split("-",1)
350
351 if first_obj == "tag" and menu != "usertags":
352 return HttpResponseRedirect(reverse(viewname="load_template", args=['usertags']) + "?show=" + init['initially_select'][0])
353 try:
354 conn.SERVICE_OPTS.setOmeroGroup('-1')
355 if first_obj == "tag":
356 first_sel = conn.getObject("TagAnnotation", long(first_id))
357 else:
358 first_sel = conn.getObject(first_obj, long(first_id))
359 initially_open_owner = first_sel.details.owner.id.val
360
361 if first_obj == "well":
362 parentNode = first_sel.getWellSample().getPlateAcquisition()
363 ptype = "acquisition"
364 if parentNode is None:
365 parentNode = first_sel.getParent()
366 ptype = "plate"
367 first_sel = parentNode
368 init['initially_open'] = ["%s-%s" % (ptype, parentNode.getId())]
369 init['initially_select'] = init['initially_open'][:]
370 except:
371 pass
372 if first_obj not in ("project", "screen"):
373
374 if first_sel is not None:
375 for p in first_sel.getAncestry():
376 if first_obj == "tag":
377 init['initially_open'].insert(0, "tag-%s" % p.getId())
378 else:
379 init['initially_open'].insert(0, "%s-%s" % (p.OMERO_CLASS.lower(), p.getId()))
380 initially_open_owner = p.details.owner.id.val
381 if init['initially_open'][0].split("-")[0] == 'image':
382 init['initially_open'].insert(0, "orphaned-0")
383
384 if first_sel is not None:
385 switch_active_group(request, first_sel.details.group.id.val)
386
387
388 if menu == "search" and request.REQUEST.get('search_query'):
389 init['query'] = str(request.REQUEST.get('search_query')).replace(" ", "%20")
390
391
392 url = reverse(viewname="load_template", args=[menu])
393
394 manager = BaseContainer(conn)
395
396
397 active_group = request.session.get('active_group') or conn.getEventContext().groupId
398
399 s = conn.groupSummary(active_group)
400 leaders = s["leaders"]
401 members = s["colleagues"]
402 userIds = [u.id for u in leaders]
403 userIds.extend( [u.id for u in members] )
404 users = []
405 if len(leaders) > 0:
406 users.append( ("Owners", leaders) )
407 if len(members) > 0:
408 users.append( ("Members", members) )
409 users = tuple(users)
410
411
412 user_id = request.REQUEST.get('experimenter')
413 if initially_open_owner is not None:
414 if (request.session.get('user_id', None) != -1):
415 user_id = initially_open_owner
416 try:
417 user_id = long(user_id)
418 except:
419 user_id = None
420 if user_id is not None:
421 form_users = UsersForm(initial={'users': users, 'empty_label':None, 'menu':menu}, data=request.REQUEST.copy())
422 if not form_users.is_valid():
423 if user_id != -1:
424 user_id = None
425 if user_id is None:
426
427 user_id = request.session.get('user_id', None)
428 if user_id is None or int(user_id) not in userIds:
429 if user_id != -1:
430 user_id = conn.getEventContext().userId
431
432 request.session['user_id'] = user_id
433
434 if conn.isAdmin():
435 myGroups = [g for g in conn.getObjects("ExperimenterGroup") if g.getName() not in ("user", "guest")]
436 else:
437 myGroups = list(conn.getGroupsMemberOf())
438 myGroups.sort(key=lambda x: x.getName().lower())
439 new_container_form = ContainerForm()
440
441 context = {'init':init, 'myGroups':myGroups, 'new_container_form':new_container_form}
442 context['groups'] = myGroups
443 context['active_group'] = conn.getObject("ExperimenterGroup", long(active_group))
444 for g in context['groups']:
445 g.groupSummary()
446 context['active_user'] = conn.getObject("Experimenter", long(user_id))
447
448 context['isLeader'] = conn.isLeader()
449 context['current_url'] = url
450 context['template'] = template
451 return context
452
453
454 @login_required(setGroupContext=True)
455 @render_response()
456 -def load_data(request, o1_type=None, o1_id=None, o2_type=None, o2_id=None, o3_type=None, o3_id=None, conn=None, **kwargs):
457 """
458 This loads data for the tree, via AJAX calls.
459 The template is specified by query string. E.g. icon, table, tree.
460 By default this loads Projects and Datasets.
461 E.g. /load_data?view=tree provides data for the tree as <li>.
462 """
463
464
465 page = getIntOrDefault(request, 'page', 1)
466
467
468 view = str(request.REQUEST.get('view', None))
469
470
471 index = getIntOrDefault(request, 'index', 0)
472
473
474 kw = dict()
475 if o1_type is not None:
476 if o1_id is not None and o1_id > 0:
477 kw[str(o1_type)] = long(o1_id)
478 else:
479 kw[str(o1_type)] = bool(o1_id)
480 if o2_type is not None and o2_id > 0:
481 kw[str(o2_type)] = long(o2_id)
482 if o3_type is not None and o3_id > 0:
483 kw[str(o3_type)] = long(o3_id)
484
485 try:
486 manager= BaseContainer(conn, **kw)
487 except AttributeError, x:
488 return handlerInternalError(request, x)
489
490
491 filter_user_id = request.session.get('user_id')
492 form_well_index = None
493
494 context = {'manager':manager, 'form_well_index':form_well_index, 'index':index}
495
496
497 template = None
498 if kw.has_key('orphaned'):
499 manager.listOrphanedImages(filter_user_id, page)
500 if view =='icon':
501 template = "webclient/data/containers_icon.html"
502 else:
503 template = "webclient/data/container_subtree.html"
504 elif len(kw.keys()) > 0 :
505 if kw.has_key('dataset'):
506 load_pixels = (view == 'icon')
507 filter_user_id = None
508 manager.listImagesInDataset(kw.get('dataset'), filter_user_id, page, load_pixels=load_pixels)
509 if view =='icon':
510 template = "webclient/data/containers_icon.html"
511 else:
512 template = "webclient/data/container_subtree.html"
513 elif kw.has_key('plate') or kw.has_key('acquisition'):
514 if view == 'tree':
515 template = "webclient/data/container_subtree.html"
516 else:
517 fields = manager.getNumberOfFields()
518 if fields is not None:
519 form_well_index = WellIndexForm(initial={'index':index, 'range':fields})
520 if index == 0:
521 index = fields[0]
522 show = request.REQUEST.get('show', None)
523 if show is not None:
524 select_wells = [w.split("-")[1] for w in show.split("|") if w.startswith("well-")]
525 context['select_wells'] = ",".join(select_wells)
526 context['baseurl'] = reverse('webgateway').rstrip('/')
527 context['form_well_index'] = form_well_index
528 template = "webclient/data/plate.html"
529 else:
530 manager.listContainerHierarchy(filter_user_id)
531 if view =='tree':
532 template = "webclient/data/containers_tree.html"
533 elif view =='icon':
534 template = "webclient/data/containers_icon.html"
535 else:
536 template = "webclient/data/containers.html"
537
538 context['template_view'] = view
539 context['isLeader'] = conn.isLeader()
540 context['template'] = template
541 return context
542
543
544 @login_required(setGroupContext=True)
545 @render_response()
546 -def load_chgrp_target(request, group_id, target_type, conn=None, **kwargs):
547 """ Loads a tree for user to pick target Project, Dataset or Screen """
548
549
550 conn.SERVICE_OPTS.setOmeroGroup(int(group_id))
551
552 manager= BaseContainer(conn)
553 manager.listContainerHierarchy()
554 template = 'webclient/data/chgrp_target_tree.html'
555
556 show_projects = target_type in ('project', 'dataset')
557 context = {'manager': manager, 'target_type': target_type, 'show_projects':show_projects, 'template': template}
558 return context
559
560 @login_required(setGroupContext=True)
561 @render_response()
562 -def load_searching(request, form=None, conn=None, **kwargs):
563 """
564 Handles AJAX calls to search
565 """
566
567 manager = BaseSearch(conn)
568
569 if form is not None:
570 query_search = request.REQUEST.get('query').replace("+", " ")
571 template = "webclient/search/search_details.html"
572
573 onlyTypes = list()
574 if request.REQUEST.get('projects') is not None and request.REQUEST.get('projects') == 'on':
575 onlyTypes.append('projects')
576 if request.REQUEST.get('datasets') is not None and request.REQUEST.get('datasets') == 'on':
577 onlyTypes.append('datasets')
578 if request.REQUEST.get('images') is not None and request.REQUEST.get('images') == 'on':
579 onlyTypes.append('images')
580 if request.REQUEST.get('plates') is not None and request.REQUEST.get('plates') == 'on':
581 onlyTypes.append('plates')
582 if request.REQUEST.get('screens') is not None and request.REQUEST.get('screens') == 'on':
583 onlyTypes.append('screens')
584
585 startdate = request.REQUEST.get('startdateinput', None)
586 startdate = startdate is not None and smart_str(startdate) or None
587 enddate = request.REQUEST.get('enddateinput', None)
588 enddate = enddate is not None and smart_str(enddate) or None
589 date = None
590 if startdate is not None:
591 if enddate is None:
592 enddate = startdate
593 date = "%s_%s" % (startdate, enddate)
594
595
596 if len(onlyTypes) == 0:
597 onlyTypes = ['images']
598
599
600 manager.search(query_search, onlyTypes, date)
601 else:
602
603 template = "webclient/search/search.html"
604
605
606 batch_query = request.REQUEST.get('batch_query')
607 if batch_query is not None:
608 delimiter = request.REQUEST.get('delimiter')
609 delimiter = delimiter.decode("string_escape")
610 batch_query = batch_query.split("\n")
611 batch_query = [query.split(delimiter) for query in batch_query]
612 template = "webclient/search/search_details.html"
613 manager.batch_search(batch_query)
614
615 context = {'manager':manager}
616 context['template'] = template
617 return context
618
619
620 @login_required(setGroupContext=True)
621 @render_response()
622 -def load_data_by_tag(request, o_type=None, o_id=None, conn=None, **kwargs):
623 """
624 Loads data for the tag tree and center panel.
625 Either get the P/D/I etc under tags, or the images etc under a tagged Dataset or Project.
626 @param o_type 'tag' or 'project', 'dataset'.
627 """
628
629 if request.REQUEST.get("o_type") is not None and len(request.REQUEST.get("o_type")) > 0:
630 o_type = request.REQUEST.get("o_type")
631 try:
632 o_id = long(request.REQUEST.get("o_id"))
633 except:
634 pass
635
636
637 view = request.REQUEST.get("view")
638
639
640 index = getIntOrDefault(request, 'index', 0)
641
642
643
644 filter_user_id = request.session.get('user_id')
645
646
647 kw = dict()
648 if o_type is not None and o_id > 0:
649 kw[str(o_type)] = long(o_id)
650
651 try:
652 manager= BaseContainer(conn, **kw)
653 except AttributeError, x:
654 return handlerInternalError(request, x)
655
656 if o_id is not None:
657 if o_type == "tag":
658 manager.loadDataByTag()
659 if view == "tree":
660 template = "webclient/data/container_tags_containers.html"
661 elif view == "icon":
662 template = "webclient/data/containers_icon.html"
663
664 elif o_type == "dataset":
665 manager.listImagesInDataset(o_id, filter_user_id)
666 template = "webclient/data/container_tags_subtree.html"
667 else:
668 manager.loadTags(filter_user_id)
669 template = "webclient/data/container_tags_tree.html"
670
671 form_well_index = None
672
673
674 context = {'manager':manager}
675 context['template_view'] = view
676 context['isLeader'] = conn.isLeader()
677 context['template'] = template
678 return context
679
680
681 @login_required()
682 @render_response()
683 -def open_astex_viewer(request, obj_type, obj_id, conn=None, **kwargs):
684 """
685 Opens the Open Astex Viewer applet, to display volume masks in a couple of formats:
686 - mrc.map files that are attached to images. obj_type = 'file'
687 - Convert OMERO image to mrc on the fly. obj_type = 'image_8bit' or 'image'
688 In this case, we may use 'scipy' to scale the image volume.
689 """
690
691
692 image = None
693 data_storage_mode = ""
694 pixelRange = None
695 contourSliderInit, contourSliderIncr = None, None
696 sizeOptions = None
697
698 if obj_type == 'file':
699 ann = conn.getObject("Annotation", obj_id)
700 if ann is None:
701 return handlerInternalError(request, "Can't find file Annotation ID %s as data source for Open Astex Viewer." % obj_id)
702
703 imageName = ann.getFileName()
704 if imageName.endswith(".bit"):
705 data_url = reverse("open_astex_bit", args=[obj_id])
706 else:
707 data_url = reverse("open_astex_map", args=[obj_id])
708
709 elif obj_type in ('image', 'image_8bit'):
710 image = conn.getObject("Image", obj_id)
711 if image is None:
712 return handlerInternalError(request, "Can't find image ID %s as data source for Open Astex Viewer." % obj_id)
713 imageName = image.getName()
714 c = image.getChannels()[0]
715
716 DEFAULTMAPSIZE = 120
717 BIGGERMAPSIZE = 160
718 targetSize = DEFAULTMAPSIZE * DEFAULTMAPSIZE * DEFAULTMAPSIZE
719 biggerSize = BIGGERMAPSIZE * BIGGERMAPSIZE * BIGGERMAPSIZE
720 imgSize = image.getSizeX() * image.getSizeY() * image.getSizeZ()
721 if imgSize > targetSize:
722 try:
723 import scipy.ndimage
724 sizeOptions = {}
725 factor = float(targetSize)/ imgSize
726 f = pow(factor,1.0/3)
727 sizeOptions["small"] = {'x':image.getSizeX() * f, 'y':image.getSizeY() * f, 'z':image.getSizeZ() * f, 'size':DEFAULTMAPSIZE}
728 if imgSize > biggerSize:
729 factor2 = float(biggerSize)/ imgSize
730 f2 = pow(factor2,1.0/3)
731 sizeOptions["medium"] = {'x':image.getSizeX() * f2, 'y':image.getSizeY() * f2, 'z':image.getSizeZ() * f2, 'size':BIGGERMAPSIZE}
732 else:
733 sizeOptions["full"] = {'x':image.getSizeX(), 'y':image.getSizeY(), 'z':image.getSizeZ()}
734 except ImportError:
735 DEFAULTMAPSIZE = 0
736 pass
737 pixelRange = (c.getWindowMin(), c.getWindowMax())
738 contourSliderInit = (pixelRange[0] + pixelRange[1])/2
739
740 def calcPrecision(range):
741 dec=0
742 if (range == 0): dec = 0
743 elif (range < 0.0000001): dec = 10
744 elif (range < 0.000001): dec = 9
745 elif (range < 0.00001): dec = 8
746 elif (range < 0.0001): dec = 7
747 elif (range < 0.001): dec = 6
748 elif (range < 0.01): dec = 5
749 elif (range < 0.1): dec = 4
750 elif (range < 1.0): dec = 3
751 elif (range < 10.0): dec = 2
752 elif (range < 100.0): dec = 1
753 return dec
754 dec = calcPrecision(pixelRange[1]-pixelRange[0])
755 contourSliderIncr = "%.*f" % (dec,abs((pixelRange[1]-pixelRange[0])/128.0))
756
757 if obj_type == 'image_8bit':
758 data_storage_mode = 1
759 data_url = reverse("webclient_image_as_map_8bit", args=[obj_id, DEFAULTMAPSIZE])
760 else:
761 if image.getPrimaryPixels().getPixelsType.value == 'float':
762 data_storage_mode = 2
763 else:
764 data_storage_mode = 1
765 data_url = reverse("webclient_image_as_map", args=[obj_id, DEFAULTMAPSIZE])
766
767 context = {'data_url': data_url, "image": image,
768 "sizeOptions":sizeOptions, "contourSliderInit":contourSliderInit, "contourSliderIncr":contourSliderIncr,
769 "data_storage_mode": data_storage_mode,'pixelRange':pixelRange}
770 context['template'] = 'webclient/annotations/open_astex_viewer.html'
771 return context
772
842
863
881
886 """
887 The acquisition tab of the right-hand panel. Only loaded for images.
888 TODO: urls regex should make sure that c_type is only 'image' OR 'well'
889 """
890
891
892 index = getIntOrDefault(request, 'index', 0)
893
894 try:
895 if c_type in ("share", "discussion"):
896 template = "webclient/annotations/annotations_share.html"
897 manager = BaseShare(conn, c_id)
898 manager.getAllUsers(c_id)
899 manager.getComments(c_id)
900 else:
901 template = "webclient/annotations/metadata_acquisition.html"
902 manager = BaseContainer(conn, index=index, **{str(c_type): long(c_id)})
903 except AttributeError, x:
904 return handlerInternalError(request, x)
905
906 form_environment = None
907 form_objective = None
908 form_microscope = None
909 form_instrument_objectives = list()
910 form_stageLabel = None
911 form_filters = list()
912 form_dichroics = list()
913 form_detectors = list()
914 form_channels = list()
915 form_lasers = list()
916
917
918 mediums = None
919 immersions = None
920 corrections = None
921
922 if c_type == 'well' or c_type == 'image':
923 if c_type == "well":
924 manager.image = manager.well.getImage(index)
925 if share_id is None:
926 manager.originalMetadata()
927 manager.channelMetadata()
928 for theC, ch in enumerate(manager.channel_metadata):
929 logicalChannel = ch.getLogicalChannel()
930 if logicalChannel is not None:
931 channel = dict()
932 channel['form'] = MetadataChannelForm(initial={'logicalChannel': logicalChannel,
933 'illuminations': list(conn.getEnumerationEntries("IlluminationI")),
934 'contrastMethods': list(conn.getEnumerationEntries("ContrastMethodI")),
935 'modes': list(conn.getEnumerationEntries("AcquisitionModeI"))})
936 if share_id is None:
937 lightPath = logicalChannel.getLightPath()
938 if lightPath is not None:
939 channel['form_dichroic'] = None
940 channel['form_excitation_filters'] = list()
941 channel['form_emission_filters'] = list()
942 lightPathDichroic = lightPath.getDichroic()
943 if lightPathDichroic is not None:
944 channel['form_dichroic'] = MetadataDichroicForm(initial={'dichroic': lightPathDichroic})
945 filterTypes = list(conn.getEnumerationEntries("FilterTypeI"))
946 for f in lightPath.getEmissionFilters():
947 channel['form_emission_filters'].append(MetadataFilterForm(initial={'filter': f,'types':filterTypes}))
948 for f in lightPath.getExcitationFilters():
949 channel['form_excitation_filters'].append(MetadataFilterForm(initial={'filter': f,'types':filterTypes}))
950 if logicalChannel.getDetectorSettings()._obj is not None and logicalChannel.getDetectorSettings().getDetector():
951 channel['form_detector_settings'] = MetadataDetectorForm(initial={'detectorSettings':logicalChannel.getDetectorSettings(),
952 'detector': logicalChannel.getDetectorSettings().getDetector(),
953 'types':list(conn.getEnumerationEntries("DetectorTypeI")),
954 'binnings':list(conn.getEnumerationEntries("Binning"))})
955
956 lightSourceSettings = logicalChannel.getLightSourceSettings()
957 if lightSourceSettings is not None and lightSourceSettings._obj is not None:
958 if lightSourceSettings.getLightSource() is not None:
959 channel['form_light_source'] = MetadataLightSourceForm(initial={'lightSource': lightSourceSettings.getLightSource(),
960 'lstypes': list(conn.getEnumerationEntries("LaserType")),
961 'mediums': list(conn.getEnumerationEntries("LaserMediumI")),
962 'pulses': list(conn.getEnumerationEntries("PulseI"))})
963
964 channel['label'] = ch.getLabel()
965 color = ch.getColor()
966 channel['color'] = color is not None and color.getHtml() or None
967 planeInfo = manager.image and manager.image.getPrimaryPixels().copyPlaneInfo(theC=theC, theZ=0)
968 channel['plane_info'] = list(planeInfo)
969 form_channels.append(channel)
970
971 try:
972 image = manager.well.getWellSample().image()
973 except:
974 image = manager.image
975
976 if share_id is None:
977 if image.getObjectiveSettings() is not None:
978
979 if mediums is None: mediums = list(conn.getEnumerationEntries("MediumI"))
980 if immersions is None: immersions = list(conn.getEnumerationEntries("ImmersionI"))
981 if corrections is None: corrections = list(conn.getEnumerationEntries("CorrectionI"))
982 form_objective = MetadataObjectiveSettingsForm(initial={'objectiveSettings': image.getObjectiveSettings(),
983 'objective': image.getObjectiveSettings().getObjective(),
984 'mediums': mediums, 'immersions': immersions, 'corrections': corrections })
985 if image.getImagingEnvironment() is not None:
986 form_environment = MetadataEnvironmentForm(initial={'image': image})
987 if image.getStageLabel() is not None:
988 form_stageLabel = MetadataStageLabelForm(initial={'image': image })
989
990 instrument = image.getInstrument()
991 if instrument is not None:
992 if instrument.getMicroscope() is not None:
993 form_microscope = MetadataMicroscopeForm(initial={'microscopeTypes':list(conn.getEnumerationEntries("MicroscopeTypeI")), 'microscope': instrument.getMicroscope()})
994
995 objectives = instrument.getObjectives()
996 for o in objectives:
997
998 if mediums is None: mediums = list(conn.getEnumerationEntries("MediumI"))
999 if immersions is None: immersions = list(conn.getEnumerationEntries("ImmersionI"))
1000 if corrections is None: corrections = list(conn.getEnumerationEntries("CorrectionI"))
1001 obj_form = MetadataObjectiveForm(initial={'objective': o,
1002 'mediums': mediums, 'immersions': immersions, 'corrections': corrections })
1003 form_instrument_objectives.append(obj_form)
1004 filters = list(instrument.getFilters())
1005 if len(filters) > 0:
1006 for f in filters:
1007 form_filter = MetadataFilterForm(initial={'filter': f, 'types':list(conn.getEnumerationEntries("FilterTypeI"))})
1008 form_filters.append(form_filter)
1009
1010 dichroics = list(instrument.getDichroics())
1011 for d in dichroics:
1012 form_dichroic = MetadataDichroicForm(initial={'dichroic': d})
1013 form_dichroics.append(form_dichroic)
1014
1015 detectors = list(instrument.getDetectors())
1016 if len(detectors) > 0:
1017 for d in detectors:
1018 form_detector = MetadataDetectorForm(initial={'detectorSettings':None, 'detector': d, 'types':list(conn.getEnumerationEntries("DetectorTypeI"))})
1019 form_detectors.append(form_detector)
1020
1021 lasers = list(instrument.getLightSources())
1022 if len(lasers) > 0:
1023 for l in lasers:
1024 form_laser = MetadataLightSourceForm(initial={'lightSource': l,
1025 'lstypes':list(conn.getEnumerationEntries("LaserType")),
1026 'mediums': list(conn.getEnumerationEntries("LaserMediumI")),
1027 'pulses': list(conn.getEnumerationEntries("PulseI"))})
1028 form_lasers.append(form_laser)
1029
1030
1031 if c_type in ("share", "discussion", "tag"):
1032 context = {'manager':manager}
1033 else:
1034 context = {'manager':manager,
1035 'form_channels':form_channels, 'form_environment':form_environment, 'form_objective':form_objective,
1036 'form_microscope':form_microscope, 'form_instrument_objectives': form_instrument_objectives, 'form_filters':form_filters,
1037 'form_dichroics':form_dichroics, 'form_detectors':form_detectors, 'form_lasers':form_lasers, 'form_stageLabel':form_stageLabel}
1038 context['template'] = template
1039 return context
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 -def getObjects(request, conn=None):
1056 """
1057 Prepare objects for use in the annotation forms.
1058 These objects are required by the form superclass to populate hidden fields, so we know what we're annotating on submission
1059 """
1060 images = len(request.REQUEST.getlist('image')) > 0 and list(conn.getObjects("Image", request.REQUEST.getlist('image'))) or list()
1061 datasets = len(request.REQUEST.getlist('dataset')) > 0 and list(conn.getObjects("Dataset", request.REQUEST.getlist('dataset'))) or list()
1062 projects = len(request.REQUEST.getlist('project')) > 0 and list(conn.getObjects("Project", request.REQUEST.getlist('project'))) or list()
1063 screens = len(request.REQUEST.getlist('screen')) > 0 and list(conn.getObjects("Screen", request.REQUEST.getlist('screen'))) or list()
1064 plates = len(request.REQUEST.getlist('plate')) > 0 and list(conn.getObjects("Plate", request.REQUEST.getlist('plate'))) or list()
1065 acquisitions = len(request.REQUEST.getlist('acquisition')) > 0 and \
1066 list(conn.getObjects("PlateAcquisition", request.REQUEST.getlist('acquisition'))) or list()
1067 shares = len(request.REQUEST.getlist('share')) > 0 and [conn.getShare(request.REQUEST.getlist('share')[0])] or list()
1068 wells = list()
1069 if len(request.REQUEST.getlist('well')) > 0:
1070 index = getIntOrDefault(request, 'index', 0)
1071 for w in conn.getObjects("Well", request.REQUEST.getlist('well')):
1072 w.index=index
1073 wells.append(w)
1074 return {'image':images, 'dataset':datasets, 'project':projects, 'screen':screens,
1075 'plate':plates, 'acquisition':acquisitions, 'well':wells, 'share':shares}
1076
1078 """ Used by forms to indicate the currently selected objects prepared above """
1079 selected = {'images':request.REQUEST.getlist('image'), 'datasets':request.REQUEST.getlist('dataset'), \
1080 'projects':request.REQUEST.getlist('project'), 'screens':request.REQUEST.getlist('screen'), \
1081 'plates':request.REQUEST.getlist('plate'), 'acquisitions':request.REQUEST.getlist('acquisition'), \
1082 'wells':request.REQUEST.getlist('well'), 'shares':request.REQUEST.getlist('share')}
1083 return selected
1084
1085
1086 @login_required(setGroupContext=True)
1087 @render_response()
1088 -def batch_annotate(request, conn=None, **kwargs):
1089 """
1090 This page gives a form for batch annotation.
1091 Local File form and Comment form are loaded. Other forms are loaded via AJAX
1092 """
1093
1094 objs = getObjects(request, conn)
1095 selected = getIds(request)
1096 initial = {'selected':selected, 'images':objs['image'], 'datasets': objs['dataset'], 'projects':objs['project'],
1097 'screens':objs['screen'], 'plates':objs['plate'], 'acquisitions':objs['acquisition'], 'wells':objs['well']}
1098 form_comment = CommentAnnotationForm(initial=initial)
1099 index = getIntOrDefault(request, 'index', 0)
1100
1101 manager = BaseContainer(conn)
1102 batchAnns = manager.loadBatchAnnotations(objs)
1103 figScripts = manager.listFigureScripts(objs)
1104
1105 obj_ids = []
1106 obj_labels = []
1107 for key in objs:
1108 obj_ids += ["%s=%s"%(key,o.id) for o in objs[key]]
1109 for o in objs[key]:
1110 obj_labels.append( {'type':key.title(), 'id':o.id, 'name':o.getName()} )
1111 obj_string = "&".join(obj_ids)
1112 link_string = "|".join(obj_ids).replace("=", "-")
1113
1114 context = {'form_comment':form_comment, 'obj_string':obj_string, 'link_string': link_string,
1115 'obj_labels': obj_labels, 'batchAnns': batchAnns, 'batch_ann':True, 'index': index,
1116 'figScripts':figScripts}
1117 context['template'] = "webclient/annotations/batch_annotate.html"
1118 context['webclient_path'] = request.build_absolute_uri(reverse('webindex'))
1119 return context
1120
1121
1122 @login_required(setGroupContext=True)
1123 @render_response()
1124 -def annotate_file(request, conn=None, **kwargs):
1125 """
1126 On 'POST', This handles attaching an existing file-annotation(s) and/or upload of a new file to one or more objects
1127 Otherwise it generates the form for choosing file-annotations & local files.
1128 """
1129 index = getIntOrDefault(request, 'index', 0)
1130 oids = getObjects(request, conn)
1131 selected = getIds(request)
1132 initial = {'selected':selected, 'images':oids['image'], 'datasets': oids['dataset'], 'projects':oids['project'],
1133 'screens':oids['screen'], 'plates':oids['plate'], 'acquisitions':oids['acquisition'], 'wells':oids['well']}
1134
1135 obj_count = sum( [len(selected[types]) for types in selected] )
1136
1137
1138 manager = None
1139 if obj_count == 1:
1140 for t in selected:
1141 if len(selected[t]) > 0:
1142 o_type = t[:-1]
1143 o_id = selected[t][0]
1144 break
1145 if o_type in ("dataset", "project", "image", "screen", "plate", "acquisition", "well","comment", "file", "tag", "tagset"):
1146 if o_type == 'tagset': o_type = 'tag'
1147 kw = {'index':index}
1148 if o_type is not None and o_id > 0:
1149 kw[str(o_type)] = long(o_id)
1150 try:
1151 manager = BaseContainer(conn, **kw)
1152 except AttributeError, x:
1153 return handlerInternalError(request, x)
1154
1155 if manager is not None:
1156 files = manager.getFilesByObject()
1157 else:
1158 manager = BaseContainer(conn)
1159 for dtype, objs in oids.items():
1160 if len(objs) > 0:
1161
1162 files = manager.getFilesByObject(parent_type=dtype, parent_ids=[o.getId() for o in objs])
1163 break
1164
1165 initial['files'] = files
1166
1167 if request.method == 'POST':
1168
1169 form_file = FilesAnnotationForm(initial=initial, data=request.REQUEST.copy())
1170 if form_file.is_valid():
1171
1172 files = form_file.cleaned_data['files']
1173 added_files = []
1174 if files is not None and len(files)>0:
1175 added_files = manager.createAnnotationsLinks('file', files, oids, well_index=index)
1176
1177 fileupload = 'annotation_file' in request.FILES and request.FILES['annotation_file'] or None
1178 if fileupload is not None and fileupload != "":
1179 newFileId = manager.createFileAnnotations(fileupload, oids, well_index=index)
1180 added_files.append(newFileId)
1181 if len(added_files) == 0:
1182 return HttpResponse("<div>No Files chosen</div>")
1183 template = "webclient/annotations/fileanns.html"
1184 context = {}
1185
1186 batchAnns = manager.loadBatchAnnotations(oids, ann_ids=added_files, addedByMe=(obj_count==1))
1187 if obj_count > 1:
1188 context["batchAnns"] = batchAnns
1189 context['batch_ann'] = True
1190 else:
1191
1192 fileanns = []
1193 for a in batchAnns['File']:
1194 for l in a['links']:
1195 fileanns.append(l.getAnnotation())
1196 context['fileanns'] = fileanns
1197 context['can_remove'] = True
1198 else:
1199 return HttpResponse(form_file.errors)
1200
1201 else:
1202 form_file = FilesAnnotationForm(initial=initial)
1203 context = {'form_file': form_file, 'index': index}
1204 template = "webclient/annotations/files_form.html"
1205 context['template'] = template
1206 return context
1207
1244
1329
1330
1331 @login_required()
1332 @render_response()
1333 -def edit_channel_names(request, imageId, conn=None, **kwargs):
1334 """
1335 Edit and save channel names
1336 """
1337 image = conn.getObject("Image", imageId)
1338 sizeC = image.getSizeC()
1339 channelNames = {}
1340 nameDict = {}
1341 for i in range(sizeC):
1342 cname = request.REQUEST.get("channel%d" % i, None)
1343 if cname is not None:
1344 channelNames["channel%d" % i] = smart_str(cname)
1345 nameDict[i+1] = smart_str(cname)
1346
1347 if request.REQUEST.get('confirm_apply', None) is not None:
1348 parentId = request.REQUEST.get('parentId', None)
1349 if parentId is not None:
1350 ptype = parentId.split("-")[0].title()
1351 pid = long(parentId.split("-")[1])
1352 counts = conn.setChannelNames(ptype, [pid], nameDict, channelCount=sizeC)
1353 else:
1354 counts = conn.setChannelNames("Image", [image.getId()], nameDict)
1355 rv = {"channelNames": channelNames}
1356 if counts:
1357 rv['imageCount'] = counts['imageCount']
1358 rv['updateCount'] = counts['updateCount']
1359 return rv
1360 else:
1361 return {"error": "No parent found to apply Channel Names"}
1362
1363
1364 @login_required(setGroupContext=True)
1365 @render_response()
1366 -def manage_action_containers(request, action, o_type=None, o_id=None, conn=None, **kwargs):
1367 """
1368 Handles many different actions on various objects.
1369
1370 @param action: "addnewcontainer", (creates a new Project, Dataset, Screen)
1371 "editname", "savename", "editdescription", "savedescription", (used as GET and POST for in-line editing)
1372 "paste", "move", "remove", "removefromshare", (tree P/D/I moving etc)
1373 "delete", "deletemany" (delete objects)
1374 @param o_type: "dataset", "project", "image", "screen", "plate", "acquisition", "well","comment", "file", "tag", "tagset","share", "sharecomment"
1375 """
1376 template = None
1377
1378
1379 index = getIntOrDefault(request, 'index', 0)
1380
1381 manager = None
1382 if o_type in ("dataset", "project", "image", "screen", "plate", "acquisition", "well","comment", "file", "tag", "tagset"):
1383 if o_type == 'tagset': o_type = 'tag'
1384 kw = {'index':index}
1385 if o_type is not None and o_id > 0:
1386 kw[str(o_type)] = long(o_id)
1387 try:
1388 manager = BaseContainer(conn, **kw)
1389 except AttributeError, x:
1390 return handlerInternalError(request, x)
1391 elif o_type in ("share", "sharecomment"):
1392 manager = BaseShare(conn, o_id)
1393 else:
1394 manager = BaseContainer(conn)
1395
1396 form = None
1397 if action == 'addnewcontainer':
1398
1399 if not request.method == 'POST':
1400 return HttpResponseRedirect(reverse("manage_action_containers", args=["edit", o_type, o_id]))
1401 if o_type is not None and hasattr(manager, o_type) and o_id > 0:
1402
1403 form = ContainerForm(data=request.REQUEST.copy())
1404 if form.is_valid():
1405 logger.debug("Create new in %s: %s" % (o_type, str(form.cleaned_data)))
1406 name = form.cleaned_data['name']
1407 description = form.cleaned_data['description']
1408 oid = manager.createDataset(name, description)
1409 rdict = {'bad':'false', 'id': oid}
1410 json = simplejson.dumps(rdict, ensure_ascii=False)
1411 return HttpResponse( json, mimetype='application/javascript')
1412 else:
1413 d = dict()
1414 for e in form.errors.iteritems():
1415 d.update({e[0]:unicode(e[1])})
1416 rdict = {'bad':'true','errs': d }
1417 json = simplejson.dumps(rdict, ensure_ascii=False)
1418 return HttpResponse( json, mimetype='application/javascript')
1419 elif request.REQUEST.get('folder_type') in ("project", "screen", "dataset"):
1420
1421 form = ContainerForm(data=request.REQUEST.copy())
1422 if form.is_valid():
1423 logger.debug("Create new: %s" % (str(form.cleaned_data)))
1424 name = form.cleaned_data['name']
1425 description = form.cleaned_data['description']
1426 folder_type = request.REQUEST.get('folder_type')
1427 if folder_type == "dataset":
1428 oid = manager.createDataset(name,description, img_ids=request.REQUEST.get('img_ids', None))
1429 else:
1430 oid = getattr(manager, "create"+folder_type.capitalize())(name, description)
1431 rdict = {'bad':'false', 'id': oid}
1432 json = simplejson.dumps(rdict, ensure_ascii=False)
1433 return HttpResponse( json, mimetype='application/javascript')
1434 else:
1435 d = dict()
1436 for e in form.errors.iteritems():
1437 d.update({e[0]:unicode(e[1])})
1438 rdict = {'bad':'true','errs': d }
1439 json = simplejson.dumps(rdict, ensure_ascii=False)
1440 return HttpResponse( json, mimetype='application/javascript')
1441 else:
1442 return HttpResponseServerError("Object does not exist")
1443 elif action == 'edit':
1444
1445 if o_type == "share" and o_id > 0:
1446 template = "webclient/public/share_form.html"
1447 manager.getMembers(o_id)
1448 manager.getComments(o_id)
1449 experimenters = list(conn.getExperimenters())
1450 experimenters.sort(key=lambda x: x.getOmeName().lower())
1451 initial={'message': manager.share.message, 'expiration': "", \
1452 'shareMembers': manager.membersInShare, 'enable': manager.share.active, \
1453 'experimenters': experimenters}
1454 if manager.share.getExpireDate() is not None:
1455 initial['expiration'] = manager.share.getExpireDate().strftime("%Y-%m-%d")
1456 form = ShareForm(initial=initial)
1457 context = {'share':manager, 'form':form}
1458 elif hasattr(manager, o_type) and o_id > 0:
1459 obj = getattr(manager, o_type)
1460 template = "webclient/data/container_form.html"
1461 form = ContainerForm(initial={'name': obj.name, 'description':obj.description})
1462 context = {'manager':manager, 'form':form}
1463 elif action == 'save':
1464
1465 if not request.method == 'POST':
1466 return HttpResponseRedirect(reverse("manage_action_containers", args=["edit", o_type, o_id]))
1467 if o_type == "share":
1468 experimenters = list(conn.getExperimenters())
1469 experimenters.sort(key=lambda x: x.getOmeName().lower())
1470 form = ShareForm(initial={'experimenters':experimenters}, data=request.REQUEST.copy())
1471 if form.is_valid():
1472 logger.debug("Update share: %s" % (str(form.cleaned_data)))
1473 message = form.cleaned_data['message']
1474 expiration = form.cleaned_data['expiration']
1475 members = form.cleaned_data['members']
1476
1477 enable = toBoolean(form.cleaned_data['enable'])
1478 host = request.build_absolute_uri(reverse("load_template", args=["public"]))
1479 manager.updateShareOrDiscussion(host, conn.server_id, message, members, enable, expiration)
1480 return HttpResponse("DONE")
1481 else:
1482 template = "webclient/public/share_form.html"
1483 context = {'share':manager, 'form':form}
1484 else:
1485 return HttpResponseServerError("Object does not exist")
1486 elif action == 'editname':
1487
1488 if hasattr(manager, o_type) and o_id > 0:
1489 obj = getattr(manager, o_type)
1490 if (o_type == "well"):
1491 obj = obj.getWellSample(index).image()
1492 template = "webclient/ajax_form/container_form_ajax.html"
1493 if o_type == "tag":
1494 txtValue = obj.textValue
1495 else:
1496 txtValue = obj.getName()
1497 form = ContainerNameForm(initial={'name': txtValue})
1498 context = {'manager':manager, 'form':form}
1499 else:
1500 return HttpResponseServerError("Object does not exist")
1501 elif action == 'savename':
1502
1503 if not request.method == 'POST':
1504 return HttpResponseRedirect(reverse("manage_action_containers", args=["edit", o_type, o_id]))
1505 if hasattr(manager, o_type) and o_id > 0:
1506 form = ContainerNameForm(data=request.REQUEST.copy())
1507 if form.is_valid():
1508 logger.debug("Update name form:" + str(form.cleaned_data))
1509 name = form.cleaned_data['name']
1510 rdict = {'bad':'false', 'o_type': o_type}
1511 if (o_type == "well"):
1512 manager.image = manager.well.getWellSample(index).image()
1513 o_type = "image"
1514 manager.updateName(o_type, name)
1515 json = simplejson.dumps(rdict, ensure_ascii=False)
1516 return HttpResponse( json, mimetype='application/javascript')
1517 else:
1518 d = dict()
1519 for e in form.errors.iteritems():
1520 d.update({e[0]:unicode(e[1])})
1521 rdict = {'bad':'true','errs': d }
1522 json = simplejson.dumps(rdict, ensure_ascii=False)
1523 return HttpResponse( json, mimetype='application/javascript')
1524 else:
1525 return HttpResponseServerError("Object does not exist")
1526 elif action == 'editdescription':
1527
1528 if hasattr(manager, o_type) and o_id > 0:
1529 obj = getattr(manager, o_type)
1530 if (o_type == "well"):
1531 obj = obj.getWellSample(index).image()
1532 template = "webclient/ajax_form/container_form_ajax.html"
1533 form = ContainerDescriptionForm(initial={'description': obj.description})
1534 context = {'manager':manager, 'form':form}
1535 else:
1536 return HttpResponseServerError("Object does not exist")
1537 elif action == 'savedescription':
1538
1539 if not request.method == 'POST':
1540 return HttpResponseServerError("Action '%s' on the '%s' id:%s cannot be complited" % (action, o_type, o_id))
1541 if hasattr(manager, o_type) and o_id > 0:
1542 form = ContainerDescriptionForm(data=request.REQUEST.copy())
1543 if form.is_valid():
1544 logger.debug("Update name form:" + str(form.cleaned_data))
1545 description = form.cleaned_data['description']
1546 if (o_type == "well"):
1547 manager.image = manager.well.getWellSample(index).image()
1548 o_type = "image"
1549 manager.updateDescription(o_type, description)
1550 rdict = {'bad':'false' }
1551 json = simplejson.dumps(rdict, ensure_ascii=False)
1552 return HttpResponse( json, mimetype='application/javascript')
1553 else:
1554 d = dict()
1555 for e in form.errors.iteritems():
1556 d.update({e[0]:unicode(e[1])})
1557 rdict = {'bad':'true','errs': d }
1558 json = simplejson.dumps(rdict, ensure_ascii=False)
1559 return HttpResponse( json, mimetype='application/javascript')
1560 else:
1561 return HttpResponseServerError("Object does not exist")
1562 elif action == 'paste':
1563
1564 destination = request.REQUEST['destination'].split('-')
1565 rv = manager.paste(destination)
1566 if rv:
1567 rdict = {'bad':'true','errs': rv }
1568 json = simplejson.dumps(rdict, ensure_ascii=False)
1569 return HttpResponse( json, mimetype='application/javascript')
1570 else:
1571 rdict = {'bad':'false' }
1572 json = simplejson.dumps(rdict, ensure_ascii=False)
1573 return HttpResponse( json, mimetype='application/javascript')
1574 elif action == 'move':
1575
1576
1577 parent = request.REQUEST['parent'].split('-')
1578
1579 destination = request.REQUEST['destination'].split('-')
1580 rv = None
1581 try:
1582 if parent[1] == destination[1]:
1583 rv = "Error: Cannot move to the same place."
1584 except Exception, x:
1585 rdict = {'bad':'true','errs': str(x) }
1586 else:
1587 if rv is None:
1588 rv = manager.move(parent,destination)
1589 if rv:
1590 rdict = {'bad':'true','errs': rv }
1591 else:
1592 rdict = {'bad':'false' }
1593 json = simplejson.dumps(rdict, ensure_ascii=False)
1594 return HttpResponse( json, mimetype='application/javascript')
1595 elif action == 'remove':
1596
1597 parents = request.REQUEST['parent']
1598 try:
1599 manager.remove(parents.split('|'), index)
1600 except Exception, x:
1601 logger.error(traceback.format_exc())
1602 rdict = {'bad':'true','errs': str(x) }
1603 json = simplejson.dumps(rdict, ensure_ascii=False)
1604 return HttpResponse( json, mimetype='application/javascript')
1605
1606 rdict = {'bad':'false' }
1607 json = simplejson.dumps(rdict, ensure_ascii=False)
1608 return HttpResponse( json, mimetype='application/javascript')
1609 elif action == 'removefromshare':
1610 image_id = request.REQUEST.get('source')
1611 try:
1612 manager.removeImage(image_id)
1613 except Exception, x:
1614 logger.error(traceback.format_exc())
1615 rdict = {'bad':'true','errs': str(x) }
1616 json = simplejson.dumps(rdict, ensure_ascii=False)
1617 return HttpResponse( json, mimetype='application/javascript')
1618 rdict = {'bad':'false' }
1619 json = simplejson.dumps(rdict, ensure_ascii=False)
1620 return HttpResponse( json, mimetype='application/javascript')
1621 elif action == 'delete':
1622
1623 child = toBoolean(request.REQUEST.get('child'))
1624 anns = toBoolean(request.REQUEST.get('anns'))
1625 try:
1626 handle = manager.deleteItem(child, anns)
1627 request.session['callback'][str(handle)] = {'job_type': 'delete', 'delmany':False,'did':o_id, 'dtype':o_type, 'status':'in progress',
1628 'derror':0, 'dreport':_formatReport(handle), 'start_time': datetime.datetime.now()}
1629 request.session.modified = True
1630 except Exception, x:
1631 logger.error('Failed to delete: %r' % {'did':o_id, 'dtype':o_type}, exc_info=True)
1632 rdict = {'bad':'true','errs': str(x) }
1633 else:
1634 rdict = {'bad':'false' }
1635 json = simplejson.dumps(rdict, ensure_ascii=False)
1636 return HttpResponse( json, mimetype='application/javascript')
1637 elif action == 'deletemany':
1638
1639 object_ids = {'Image':request.REQUEST.getlist('image'), 'Dataset':request.REQUEST.getlist('dataset'), 'Project':request.REQUEST.getlist('project'), 'Screen':request.REQUEST.getlist('screen'), 'Plate':request.REQUEST.getlist('plate'), 'Well':request.REQUEST.getlist('well'), 'PlateAcquisition':request.REQUEST.getlist('acquisition')}
1640 child = toBoolean(request.REQUEST.get('child'))
1641 anns = toBoolean(request.REQUEST.get('anns'))
1642 logger.debug("Delete many: child? %s anns? %s object_ids %s" % (child, anns, object_ids))
1643 try:
1644 for key,ids in object_ids.iteritems():
1645 if ids is not None and len(ids) > 0:
1646 handle = manager.deleteObjects(key, ids, child, anns)
1647 dMap = {'job_type': 'delete', 'start_time': datetime.datetime.now(),'status':'in progress', 'derrors':0,
1648 'dreport':_formatReport(handle), 'dtype':key}
1649 if len(ids) > 1:
1650 dMap['delmany'] = len(ids)
1651 dMap['did'] = ids
1652 else:
1653 dMap['delmany'] = False
1654 dMap['did'] = ids[0]
1655 request.session['callback'][str(handle)] = dMap
1656 request.session.modified = True
1657 except Exception, x:
1658 logger.error('Failed to delete: %r' % {'did':ids, 'dtype':key}, exc_info=True)
1659 rdict = {'bad':'true','errs': str(x) }
1660 else:
1661 rdict = {'bad':'false' }
1662 json = simplejson.dumps(rdict, ensure_ascii=False)
1663 return HttpResponse( json, mimetype='application/javascript')
1664 context['template'] = template
1665 return context
1666
1667 @login_required(doConnectionCleanup=False)
1668 -def get_original_file(request, fileId, conn=None, **kwargs):
1669 """ Returns the specified original file as an http response. Used for displaying text or png/jpeg etc files in browser """
1670
1671
1672 conn.SERVICE_OPTS.setOmeroGroup(-1)
1673
1674 orig_file = conn.getObject("OriginalFile", fileId)
1675 if orig_file is None:
1676 return handlerInternalError(request, "Original File does not exists (id:%s)." % (fileId))
1677
1678 rsp = ConnCleaningHttpResponse(orig_file.getFileInChunks())
1679 rsp.conn = conn
1680 mimetype = orig_file.mimetype
1681 if mimetype == "text/x-python":
1682 mimetype = "text/plain"
1683 rsp['Content-Type'] = mimetype
1684 rsp['Content-Length'] = orig_file.getSize()
1685
1686 return rsp
1687
1688
1689 @login_required()
1690 -def image_as_map(request, imageId, conn=None, **kwargs):
1691 """ Converts OMERO image into mrc.map file (using tiltpicker utils) and returns the file """
1692
1693 from omero_ext.tiltpicker.pyami import mrc
1694 from numpy import dstack, zeros, int8
1695
1696 image = conn.getObject("Image", imageId)
1697 if image is None:
1698 message = "Image ID %s not found in image_as_map" % imageId
1699 logger.error(message)
1700 return handlerInternalError(request, message)
1701
1702 imageName = image.getName()
1703 downloadName = imageName.endswith(".map") and imageName or "%s.map" % imageName
1704 pixels = image.getPrimaryPixels()
1705
1706
1707 zctList = [(z,0,0) for z in range(image.getSizeZ())]
1708 npList = list(pixels.getPlanes(zctList))
1709 npStack = dstack(npList)
1710 logger.info("Numpy stack for image_as_map: dtype: %s, range %s-%s" % (npStack.dtype.name, npStack.min(), npStack.max()) )
1711
1712
1713 if pixels.getPixelsType().value != 'float' or ('8bit' in kwargs and kwargs['8bit']):
1714
1715 npStack = npStack - npStack.min()
1716 npStack = (npStack * 255.0 / npStack.max()) - 127
1717 a = zeros(npStack.shape, dtype=int8)
1718 npStack = npStack.round(out=a)
1719
1720 if "maxSize" in kwargs and int(kwargs["maxSize"]) > 0:
1721 sz = int(kwargs["maxSize"])
1722 targetSize = sz * sz * sz
1723
1724 if npStack.size > targetSize:
1725 try:
1726 import scipy.ndimage
1727 from numpy import round
1728 factor = float(targetSize)/ npStack.size
1729 factor = pow(factor,1.0/3)
1730 logger.info("Resizing numpy stack %s by factor of %s" % (npStack.shape, factor))
1731 npStack = round(scipy.ndimage.interpolation.zoom(npStack, factor), 1)
1732 except ImportError:
1733 logger.info("Failed to import scipy.ndimage for interpolation of 'image_as_map'. Full size: %s" % str(npStack.shape))
1734 pass
1735
1736 header = {}
1737
1738
1739
1740
1741
1742
1743
1744
1745 import tempfile
1746 temp = tempfile.NamedTemporaryFile(suffix='.map')
1747 try:
1748 mrc.write(npStack, temp.name, header)
1749 logger.debug("download file: %r" % {'name':temp.name, 'size':temp.tell()})
1750 originalFile_data = FileWrapper(temp)
1751 rsp = HttpResponse(originalFile_data)
1752 rsp['Content-Type'] = 'application/force-download'
1753
1754 rsp['Content-Length'] =os.path.getsize(temp.name)
1755 rsp['Content-Disposition'] = 'attachment; filename=%s' % downloadName
1756 temp.seek(0)
1757 except Exception, x:
1758 temp.close()
1759 logger.error(traceback.format_exc())
1760 return handlerInternalError(request, "Cannot generate map (id:%s)." % (imageId))
1761 return rsp
1762
1763
1764 @login_required(doConnectionCleanup=False)
1765 -def download_annotation(request, annId, conn=None, **kwargs):
1777
1801
1802
1803 @login_required()
1804 @render_response()
1805 -def load_public(request, share_id=None, conn=None, **kwargs):
1806 """ Loads data for the tree in the 'public' main page. """
1807
1808
1809 if share_id is None:
1810 share_id = request.REQUEST.get("o_id") is not None and long(request.REQUEST.get("o_id")) or None
1811
1812
1813 view = request.REQUEST.get("view")
1814
1815 if share_id is not None:
1816 if view == 'tree':
1817 template = "webclient/public/share_subtree.html"
1818 elif view == 'icon':
1819 template = "webclient/public/share_content_icon.html"
1820 controller = BaseShare(conn, share_id)
1821 controller.loadShareContent()
1822
1823 else:
1824 template = "webclient/public/share_tree.html"
1825 controller = BaseShare(conn)
1826 controller.getShares()
1827
1828 context = {'share':controller}
1829 context['isLeader'] = conn.isLeader()
1830 context['template'] = template
1831 return context
1832
1833
1834
1835
1836 @login_required(setGroupContext=True)
1837 @render_response()
1838 -def basket_action (request, action=None, conn=None, **kwargs):
1839 """
1840 Various actions for creating a 'share' or 'discussion' (no images).
1841
1842 @param action: 'toshare', 'createshare' (form to create share and handling the action itself)
1843 'todiscuss', 'createdisc' (form to create discussion and handling the action itself)
1844 """
1845
1846 if action == "toshare":
1847 template = "webclient/basket/basket_share_action.html"
1848 basket = BaseBasket(conn)
1849 basket.load_basket(request)
1850 experimenters = list(conn.getExperimenters())
1851 experimenters.sort(key=lambda x: x.getOmeName().lower())
1852 selected = [long(i) for i in request.REQUEST.getlist('image')]
1853 form = BasketShareForm(initial={'experimenters':experimenters, 'images':basket.imageInBasket, 'enable':True, 'selected':selected})
1854 context = {'form':form}
1855 elif action == "createshare":
1856 if not request.method == 'POST':
1857 return HttpResponseRedirect(reverse("basket_action"))
1858 basket = BaseBasket(conn)
1859 basket.load_basket(request)
1860 experimenters = list(conn.getExperimenters())
1861 experimenters.sort(key=lambda x: x.getOmeName().lower())
1862 form = BasketShareForm(initial={'experimenters':experimenters, 'images':basket.imageInBasket}, data=request.REQUEST.copy())
1863 if form.is_valid():
1864 images = form.cleaned_data['image']
1865 message = form.cleaned_data['message']
1866 expiration = form.cleaned_data['expiration']
1867 members = form.cleaned_data['members']
1868
1869 enable = toBoolean(form.cleaned_data['enable'])
1870 host = request.build_absolute_uri(reverse("load_template", args=["public"]))
1871 share = BaseShare(conn)
1872 share.createShare(host, conn.server_id, images, message, members, enable, expiration)
1873 return HttpResponse("success")
1874 else:
1875 template = "webclient/basket/basket_share_action.html"
1876 context = {'form':form}
1877 elif action == "todiscuss":
1878 template = "webclient/basket/basket_discussion_action.html"
1879 basket = BaseBasket(conn)
1880 experimenters = list(conn.getExperimenters())
1881 experimenters.sort(key=lambda x: x.getOmeName().lower())
1882 form = ShareForm(initial={'experimenters':experimenters, 'enable':True})
1883 context = {'form':form}
1884 elif action == "createdisc":
1885 if not request.method == 'POST':
1886 return HttpResponseRedirect(reverse("basket_action"))
1887 basket = BaseBasket(conn)
1888 experimenters = list(conn.getExperimenters())
1889 experimenters.sort(key=lambda x: x.getOmeName().lower())
1890 form = ShareForm(initial={'experimenters':experimenters}, data=request.REQUEST.copy())
1891 if form.is_valid():
1892 message = form.cleaned_data['message']
1893 expiration = form.cleaned_data['expiration']
1894 members = form.cleaned_data['members']
1895
1896 enable = toBoolean(form.cleaned_data['enable'])
1897 host = request.build_absolute_uri(reverse("load_template", args=["public"]))
1898 share = BaseShare(conn)
1899 share.createDiscussion(host, conn.server_id, message, members, enable, expiration)
1900 return HttpResponse("success")
1901 else:
1902 template = "webclient/basket/basket_discussion_action.html"
1903 context = {'form':form}
1904 else:
1905 template = kwargs.get("template", "webclient/basket/basket.html")
1906
1907 basket = BaseBasket(conn)
1908 basket.load_basket(request)
1909
1910 context = {'basket':basket }
1911 context['template'] = template
1912 return context
1913
1914 @login_required()
1915 -def empty_basket(request, **kwargs):
1916 """ Empty the basket of images """
1917
1918 try:
1919 del request.session['imageInBasket']
1920 del request.session['basket_counter']
1921 except KeyError:
1922 logger.error(traceback.format_exc())
1923
1924 return HttpResponseRedirect(reverse("basket_action"))
1925
1928 """ Add or remove images to the set in the basket """
1929
1930 action = None
1931 if request.method == 'POST':
1932 request.session.modified = True
1933 try:
1934 action = request.REQUEST['action']
1935 except Exception, x:
1936 logger.error(traceback.format_exc())
1937 return handlerInternalError(request, "Attribute error: 'action' is missed.")
1938 else:
1939 prod = request.REQUEST.get('productId')
1940 ptype = request.REQUEST.get('productType')
1941 if action == 'add':
1942 images = request.REQUEST.getlist('image')
1943
1944 for i in images:
1945 flag = False
1946 for item in request.session['imageInBasket']:
1947 if item == long(i):
1948 flag = True
1949 break
1950 if not flag:
1951 request.session['imageInBasket'].add(long(i))
1952
1953
1954
1955
1956
1957
1958
1959
1960 elif action == 'del':
1961 if ptype == 'image':
1962 try:
1963 request.session['imageInBasket'].remove(long(prod))
1964 except:
1965 rv = "Error: could not remove image from the basket."
1966 return HttpResponse(rv)
1967
1968
1969
1970
1971
1972
1973 else:
1974 rv = "Error: This action is not available"
1975 return HttpResponse(rv)
1976 elif action == 'delmany':
1977 images = [long(i) for i in request.REQUEST.getlist('image')]
1978 for i in images:
1979 if i in request.session['imageInBasket']:
1980 request.session['imageInBasket'].remove(long(i))
1981 else:
1982 rv = "Error: could not remove image from the basket."
1983 return HttpResponse(rv)
1984
1985 total = len(request.session['imageInBasket'])
1986 request.session['basket_counter'] = total
1987 return HttpResponse(total)
1988 else:
1989 return handlerInternalError(request, "Request method error in Basket.")
1990
1991 @login_required(setGroupContext=True)
1992 @render_response()
1993 -def load_calendar(request, year=None, month=None, conn=None, **kwargs):
2013
2014
2015 @login_required(setGroupContext=True)
2016 @render_response()
2017 -def load_history(request, year, month, day, conn=None, **kwargs):
2018 """ The data for a particular date that is loaded into the center panel """
2019
2020 template = "webclient/history/history_details.html"
2021
2022
2023 page = int(request.REQUEST.get('page', 1))
2024
2025 filter_user_id = request.session.get('user_id')
2026 controller = BaseCalendar(conn=conn, year=year, month=month, day=day, eid=filter_user_id)
2027 controller.get_items(page)
2028
2029 context = {'controller':controller}
2030 context['template'] = template
2031 return context
2032
2035 """
2036 This provides a url to browse to the specified omero.model.ObjectI P/D/I, S/P, FileAnnotation etc.
2037 used to display results from the scripting service
2038 E.g webclient/userdata/?path=image-12601
2039 If the object is a file annotation, try to browse to the parent P/D/I
2040 """
2041 base_url = reverse(viewname="load_template", args=['userdata'])
2042
2043 blitz_obj = None
2044 url = None
2045
2046 if isinstance(obj, omero.model.FileAnnotationI):
2047 fa = conn.getObject("Annotation", obj.id.val)
2048 for ptype in ['project', 'dataset', 'image']:
2049 links = list(fa.getParentLinks(ptype))
2050 if len(links) > 0:
2051 obj = links[0].parent
2052 break
2053
2054 if obj.__class__.__name__ in ("ImageI", "DatasetI", "ProjectI", "ScreenI", "PlateI"):
2055 otype = obj.__class__.__name__[:-1].lower()
2056 base_url += "?show=%s-%s" % (otype, obj.id.val)
2057 return base_url
2058
2059
2060
2061
2062 @login_required()
2063 @render_response()
2064 -def activities(request, conn=None, **kwargs):
2065 """
2066 This refreshes callback handles (delete, scripts, chgrp etc) and provides html to update Activities window & Progressbar.
2067 The returned html contains details for ALL callbacks in web session, regardless of their status.
2068 We also add counts of jobs, failures and 'in progress' to update status bar.
2069 """
2070
2071 in_progress = 0
2072 failure = 0
2073 new_results = []
2074 _purgeCallback(request)
2075
2076
2077
2078 for cbString in request.session.get('callback').keys():
2079 job_type = request.session['callback'][cbString]['job_type']
2080
2081 status = request.session['callback'][cbString]['status']
2082 if status == "failed":
2083 failure+=1
2084
2085 request.session.modified = True
2086
2087
2088 if job_type == 'chgrp':
2089 if status not in ("failed", "finished"):
2090 rsp = None
2091 try:
2092 prx = omero.cmd.HandlePrx.checkedCast(conn.c.ic.stringToProxy(cbString))
2093 rsp = prx.getResponse()
2094 close_handle = False
2095 try:
2096
2097 if rsp is not None:
2098 close_handle = True
2099 new_results.append(cbString)
2100 if isinstance(rsp, omero.cmd.ERR):
2101 request.session['callback'][cbString]['status'] = "failed"
2102 rsp_params = ", ".join(["%s: %s" % (k,v) for k,v in rsp.parameters.items()])
2103 logger.error("chgrp failed with: %s" % rsp_params)
2104 request.session['callback'][cbString]['error'] = "%s %s" % (rsp.name, rsp_params)
2105 elif isinstance(rsp, omero.cmd.OK):
2106 request.session['callback'][cbString]['status'] = "finished"
2107 else:
2108 in_progress+=1
2109 finally:
2110 prx.close(close_handle)
2111 except:
2112 logger.info("Activities chgrp handle not found: %s" % cbString)
2113 continue
2114
2115
2116 elif job_type == 'delete':
2117 if status not in ("failed", "finished"):
2118 try:
2119 handle = omero.cmd.HandlePrx.checkedCast(conn.c.ic.stringToProxy(cbString))
2120 cb = omero.callbacks.CmdCallbackI(conn.c, handle)
2121 close_handle = False
2122 try:
2123 if not cb.block(0):
2124 request.session['callback'][cbString]['derror'] = 0
2125 request.session['callback'][cbString]['status'] = "in progress"
2126 request.session['callback'][cbString]['dreport'] = _formatReport(handle)
2127 in_progress+=1
2128 else:
2129 close_handle = True
2130 err = isinstance(cb.getResponse(), omero.cmd.ERR)
2131 new_results.append(cbString)
2132 if err:
2133 request.session['callback'][cbString]['derror'] = 1
2134 request.session['callback'][cbString]['status'] = "failed"
2135 request.session['callback'][cbString]['dreport'] = _formatReport(handle)
2136 failure+=1
2137 else:
2138 request.session['callback'][cbString]['derror'] = 0
2139 request.session['callback'][cbString]['status'] = "finished"
2140 request.session['callback'][cbString]['dreport'] = _formatReport(handle)
2141 finally:
2142 cb.close(close_handle)
2143 except Ice.ObjectNotExistException, e:
2144 request.session['callback'][cbString]['derror'] = 0
2145 request.session['callback'][cbString]['status'] = "finished"
2146 request.session['callback'][cbString]['dreport'] = None
2147 except Exception, x:
2148 logger.error(traceback.format_exc())
2149 logger.error("Status job '%s'error:" % cbString)
2150 request.session['callback'][cbString]['derror'] = 1
2151 request.session['callback'][cbString]['status'] = "failed"
2152 request.session['callback'][cbString]['dreport'] = str(x)
2153 failure+=1
2154
2155
2156 elif job_type == 'script':
2157
2158 if not cbString.startswith('ProcessCallback'): continue
2159 if status not in ("failed", "finished"):
2160 logger.info("Check callback on script: %s" % cbString)
2161 proc = omero.grid.ScriptProcessPrx.checkedCast(conn.c.ic.stringToProxy(cbString))
2162 cb = omero.scripts.ProcessCallbackI(conn.c, proc)
2163
2164 if cb.block(0):
2165 cb.close()
2166 try:
2167 results = proc.getResults(0, conn.SERVICE_OPTS)
2168 request.session['callback'][cbString]['status'] = "finished"
2169 new_results.append(cbString)
2170 except Exception, x:
2171 logger.error(traceback.format_exc())
2172 continue
2173
2174 rMap = {}
2175 for key, value in results.items():
2176 v = value.getValue()
2177 if key in ("stdout", "stderr", "Message"):
2178 if key in ('stderr', 'stdout'):
2179 v = v.id.val
2180 request.session['callback'][cbString][key] = v
2181 else:
2182 if hasattr(v, "id"):
2183 obj_data = {'id': v.id.val, 'type': v.__class__.__name__[:-1]}
2184 obj_data['browse_url'] = getObjectUrl(conn, v)
2185 if v.isLoaded() and hasattr(v, "file"):
2186
2187 mimetypes = {'image/png':'png', 'image/jpeg':'jpeg', 'text/plain': 'text'}
2188 if v.file.mimetype.val in mimetypes:
2189 obj_data['fileType'] = mimetypes[v.file.mimetype.val]
2190 obj_data['fileId'] = v.file.id.val
2191 obj_data['name'] = v.file.name.val
2192
2193
2194 if v.isLoaded() and hasattr(v, "name"):
2195 obj_data['name'] = v.name.val
2196 rMap[key] = obj_data
2197 else:
2198 rMap[key] = v
2199 request.session['callback'][cbString]['results'] = rMap
2200 else:
2201 in_progress+=1
2202
2203
2204 rv = {}
2205 for cbString in request.session.get('callback').keys():
2206
2207 rv[cbString] = copy.copy(request.session['callback'][cbString])
2208
2209
2210 if 'template' in kwargs and kwargs['template'] == 'json':
2211 for cbString in request.session.get('callback').keys():
2212 rv[cbString]['start_time'] = str(request.session['callback'][cbString]['start_time'])
2213 rv['inprogress'] = in_progress
2214 rv['failure'] = failure
2215 rv['jobs'] = len(request.session['callback'])
2216 return HttpResponse(simplejson.dumps(rv),mimetype='application/javascript')
2217
2218 jobs = []
2219 for key, data in rv.items():
2220
2221
2222 if len(key.split(" ")) > 0:
2223 htmlId = key.split(" ")[0]
2224 if len(htmlId.split("/")) > 1:
2225 htmlId = htmlId.split("/")[1]
2226 rv[key]['id'] = htmlId
2227 rv[key]['key'] = key
2228 if key in new_results:
2229 rv[key]['new'] = True
2230 jobs.append(rv[key])
2231
2232 jobs.sort(key=lambda x:x['start_time'], reverse=True)
2233 context = {'sizeOfJobs':len(request.session['callback']),
2234 'jobs':jobs,
2235 'inprogress':in_progress,
2236 'new_results':len(new_results),
2237 'failure':failure}
2238
2239 context['template'] = "webclient/activities/activitiesContent.html"
2240 return context
2241
2245 """
2246 If the above 'action' == 'clean' then we clear jobs from request.session['callback']
2247 either a single job (if 'jobKey' is specified in POST) or all jobs (apart from those in progress)
2248 """
2249
2250 request.session.modified = True
2251
2252 if action == "clean":
2253 if 'jobKey' in request.POST:
2254 jobId = request.POST.get('jobKey')
2255 rv = {}
2256 if jobId in request.session['callback']:
2257 del request.session['callback'][jobId]
2258 request.session.modified = True
2259 rv['removed'] = True
2260 else:
2261 rv['removed'] = False
2262 return HttpResponse(simplejson.dumps(rv),mimetype='application/javascript')
2263 else:
2264 for key, data in request.session['callback'].items():
2265 if data['status'] != "in progress":
2266 del request.session['callback'][key]
2267 return HttpResponse("OK")
2268
2269
2270
2271
2272 @login_required()
2273 -def avatar(request, oid=None, conn=None, **kwargs):
2277
2278
2279
2280
2281 @login_required()
2282 -def image_viewer (request, iid, share_id=None, **kwargs):
2283 """ Delegates to webgateway, using share connection if appropriate """
2284 kwargs['viewport_server'] = share_id is not None and reverse("webindex")+share_id or reverse("webindex")
2285 kwargs['viewport_server'] = kwargs['viewport_server'].rstrip('/')
2286 return webgateway_views.full_viewer(request, iid, **kwargs)
2287
2288
2289
2290
2291 @login_required()
2292 @render_response()
2293 -def list_scripts (request, conn=None, **kwargs):
2294 """ List the available scripts - Just officical scripts for now """
2295 scriptService = conn.getScriptService()
2296 scripts = scriptService.getScripts()
2297
2298
2299 scriptMenu = {}
2300 for s in scripts:
2301 scriptId = s.id.val
2302 path = s.path.val
2303 name = s.name.val
2304 fullpath = os.path.join(path, name)
2305 if fullpath in settings.SCRIPTS_TO_IGNORE:
2306 logger.info('Ignoring script %r' % fullpath)
2307 continue
2308
2309
2310
2311
2312 ul = scriptMenu
2313 dirs = fullpath.split("/");
2314 for l, d in enumerate(dirs):
2315 if len(d) == 0:
2316 continue
2317 if d not in ul:
2318
2319 if l+1 == len(dirs):
2320 ul[d] = scriptId
2321 else:
2322 ul[d] = {}
2323 ul = ul[d]
2324
2325
2326
2327 def ul_to_list(ul):
2328 dir_list = []
2329 for name, value in ul.items():
2330 if isinstance(value, dict):
2331
2332 dir_list.append({'name': name, 'ul': ul_to_list(value)})
2333 else:
2334 dir_list.append({'name': name, 'id':value})
2335 dir_list.sort(key=lambda x:x['name'].lower())
2336 return dir_list
2337
2338 scriptList = ul_to_list(scriptMenu)
2339
2340
2341 if len(scriptList) == 1:
2342 scriptList = scriptList[0]['ul']
2343
2344 return scriptList
2345
2346
2347 @login_required()
2348 @render_response()
2349 -def script_ui(request, scriptId, conn=None, **kwargs):
2350 """
2351 Generates an html form for the parameters of a defined script.
2352 """
2353 scriptService = conn.getScriptService()
2354
2355 try:
2356 params = scriptService.getParams(long(scriptId))
2357 except Exception, ex:
2358 if ex.message.lower().startswith("no processor available"):
2359 return {'template':'webclient/scripts/no_processor.html', 'scriptId': scriptId}
2360 raise ex
2361 if params == None:
2362 return HttpResponse()
2363
2364 paramData = {}
2365
2366 paramData["id"] = long(scriptId)
2367 paramData["name"] = params.name.replace("_", " ")
2368 paramData["description"] = params.description
2369 paramData["authors"] = ", ".join([a for a in params.authors])
2370 paramData["contact"] = params.contact
2371 paramData["version"] = params.version
2372 paramData["institutions"] = ", ".join([i for i in params.institutions])
2373
2374 inputs = []
2375 Data_TypeParam = None
2376 IDsParam = None
2377 for key, param in params.inputs.items():
2378 i = {}
2379 i["name"] = key.replace("_", " ")
2380 i["key"] = key
2381 if not param.optional:
2382 i["required"] = True
2383 i["description"] = param.description
2384 if param.min:
2385 i["min"] = str(param.min.getValue())
2386 if param.max:
2387 i["max"] = str(param.max.getValue())
2388 if param.values:
2389 i["options"] = [v.getValue() for v in param.values.getValue()]
2390 if param.useDefault:
2391 i["default"] = unwrap(param.prototype)
2392 if isinstance(i["default"], omero.model.IObject):
2393 i["default"] = None
2394 pt = unwrap(param.prototype)
2395 if pt.__class__.__name__ == 'dict':
2396 i["map"] = True
2397 elif pt.__class__.__name__ == 'list':
2398 i["list"] = True
2399 if "default" in i: i["default"] = i["default"][0]
2400 elif pt.__class__ == type(True):
2401 i["boolean"] = True
2402 elif pt.__class__ == type(0) or pt.__class__ == type(long(0)):
2403 i["number"] = "number"
2404 elif pt.__class__ == type(float(0.0)):
2405 i["number"] = "float"
2406
2407
2408 if request.REQUEST.get(key, None) is not None:
2409 i["default"] = request.REQUEST.get(key, None)
2410
2411 i["prototype"] = unwrap(param.prototype)
2412 i["grouping"] = param.grouping
2413 inputs.append(i)
2414
2415 if key == "IDs": IDsParam = i
2416 if key == "Data_Type": Data_TypeParam = i
2417 inputs.sort(key=lambda i: i["grouping"])
2418
2419
2420 if Data_TypeParam is not None and IDsParam is not None and "options" in Data_TypeParam:
2421 IDsParam["default"] = ""
2422 for dtype in Data_TypeParam["options"]:
2423 if request.REQUEST.get(dtype, None) is not None:
2424 Data_TypeParam["default"] = dtype
2425 IDsParam["default"] = request.REQUEST.get(dtype, "")
2426 break
2427
2428
2429 for i in range(len(inputs)):
2430 if len(inputs) <= i:
2431 break
2432 param = inputs[i]
2433 grouping = param["grouping"]
2434 param['children'] = list()
2435 c = 1
2436 while len(inputs) > i+1:
2437 nextParam = inputs[i+1]
2438 nextGrp = inputs[i+1]["grouping"]
2439 if nextGrp.split(".")[0] == grouping:
2440 param['children'].append(inputs[i+1])
2441 inputs.pop(i+1)
2442 else:
2443 break
2444
2445 paramData["inputs"] = inputs
2446
2447 return {'template':'webclient/scripts/script_ui.html', 'paramData': paramData, 'scriptId': scriptId}
2448
2471
2472 context = {}
2473
2474 if imageIds is not None:
2475 imageIds, validImages = validateIds("Image", imageIds)
2476 context['idString'] = ",".join( [str(i) for i in imageIds] )
2477 context['dtype'] = "Image"
2478 if datasetIds is not None:
2479 datasetIds, validDatasets = validateIds("Dataset", datasetIds)
2480 context['idString'] = ",".join( [str(i) for i in datasetIds] )
2481 context['dtype'] = "Dataset"
2482
2483 if scriptName == "SplitView":
2484 scriptPath = "/omero/figure_scripts/Split_View_Figure.py"
2485 template = "webclient/scripts/split_view_figure.html"
2486
2487 imgDict = []
2488 for iId in imageIds:
2489 data = {'id':iId}
2490 img = validImages[iId]
2491 data['name'] = img.getName()
2492 tags = [ann.getTextValue() for ann in img.listAnnotations() if ann._obj.__class__ == omero.model.TagAnnotationI]
2493 data['tags'] = tags
2494 data['datasets'] = [d.getName() for d in img.listParents()]
2495 imgDict.append(data)
2496
2497
2498 image = validImages[imageIds[0]]
2499 context['imgDict'] = imgDict
2500 context['image'] = image
2501 context['channels'] = image.getChannels()
2502
2503 elif scriptName == "Thumbnail":
2504 scriptPath = "/omero/figure_scripts/Thumbnail_Figure.py"
2505 template = "webclient/scripts/thumbnail_figure.html"
2506
2507
2508 def loadImageTags(imageIds):
2509 tagLinks = conn.getAnnotationLinks("Image", parent_ids=imageIds)
2510 linkMap = {}
2511 tagMap = {}
2512 for iId in imageIds:
2513 linkMap[iId] = []
2514 for l in tagLinks:
2515 c = l.getChild()
2516 if c._obj.__class__ == omero.model.TagAnnotationI:
2517 tagMap[c.id] = c
2518 linkMap[l.getParent().id].append(c)
2519 imageTags = []
2520 for iId in imageIds:
2521 imageTags.append({'id':iId, 'tags':linkMap[iId]})
2522 tags = []
2523 for tId, t in tagMap.items():
2524 tags.append(t)
2525 return imageTags, tags
2526
2527 thumbSets = []
2528 tags = []
2529 figureName = "Thumbnail_Figure"
2530 if datasetIds is not None:
2531 for d in conn.getObjects("Dataset", datasetIds):
2532 figureName = d.getName()
2533 imgIds = [i.id for i in d.listChildren()]
2534 imageTags, ts = loadImageTags(imgIds)
2535 thumbSets.append({'name':d.getName(), 'imageTags': imageTags})
2536 tags.extend(ts)
2537 else:
2538 imageTags, ts = loadImageTags(imageIds)
2539 thumbSets.append({'name':'images', 'imageTags': imageTags})
2540 tags.extend(ts)
2541 figureName = conn.getObject("Image", imageIds[0]).getParent().getName()
2542 uniqueTagIds = set()
2543 uniqueTags = []
2544 for t in tags:
2545 if t.id not in uniqueTagIds:
2546 uniqueTags.append(t)
2547 uniqueTagIds.add(t.id)
2548 uniqueTags.sort(key=lambda x: x.getTextValue().lower())
2549 context['thumbSets'] = thumbSets
2550 context['tags'] = uniqueTags
2551 context['figureName'] = figureName.replace(" ", "_")
2552
2553 scriptService = conn.getScriptService()
2554 scriptId = scriptService.getScriptID(scriptPath);
2555 if (scriptId < 0):
2556 raise AttributeError("No script found for path '%s'" % scriptPath)
2557
2558 context['template'] = template
2559 context['scriptId'] = scriptId
2560 return context
2561
2562
2563 @login_required()
2564 -def chgrp(request, conn=None, **kwargs):
2565 """
2566 Moves data to a new group, using the chgrp queue.
2567 Handles submission of chgrp form: all data in POST.
2568 Adds the callback handle to the request.session['callback']['jobId']
2569 """
2570
2571 group_id = request.REQUEST.get('group_id', None)
2572 if group_id is None:
2573 raise AttributeError("chgrp: No group_id specified")
2574 group_id = long(group_id)
2575
2576 group = conn.getObject("ExperimenterGroup", group_id)
2577 target_id = request.REQUEST.get('target_id', None)
2578 container_id = target_id is not None and target_id.split("-")[1] or None
2579 dtypes = ["Project", "Dataset", "Image", "Screen", "Plate"]
2580 for dtype in dtypes:
2581 oids = request.REQUEST.get(dtype, None)
2582 if oids is not None:
2583 obj_ids = oids.split(",")
2584 logger.debug("chgrp to group:%s %s-%s" % (group_id, dtype, obj_ids))
2585 handle = conn.chgrpObjects(dtype, obj_ids, group_id, container_id)
2586 jobId = str(handle)
2587 request.session['callback'][jobId] = {
2588 'job_type': "chgrp",
2589 'group': group.getName(),
2590 'dtype': dtype,
2591 'obj_ids': obj_ids,
2592 'job_name': "Change group",
2593 'start_time': datetime.datetime.now(),
2594 'status':'in progress'}
2595 request.session.modified = True
2596
2597 return HttpResponse("OK")
2598
2599
2600 @login_required(setGroupContext=True)
2601 -def script_run(request, scriptId, conn=None, **kwargs):
2602 """
2603 Runs a script using values in a POST
2604 """
2605 scriptService = conn.getScriptService()
2606
2607 inputMap = {}
2608
2609 sId = long(scriptId)
2610
2611 try:
2612 params = scriptService.getParams(sId)
2613 except Exception, x:
2614 if x.message and x.message.startswith("No processor available"):
2615
2616 rsp = run_script(request, conn, sId, inputMap, scriptName='Script')
2617 return HttpResponse(simplejson.dumps(rsp), mimetype='json')
2618 else:
2619 raise
2620 params = scriptService.getParams(sId)
2621 scriptName = params.name.replace("_", " ").replace(".py", "")
2622
2623 logger.debug("Script: run with request.POST: %s" % request.POST)
2624
2625 for key, param in params.inputs.items():
2626 prototype = param.prototype
2627 pclass = prototype.__class__
2628
2629
2630 if pclass == omero.rtypes.RBoolI:
2631 value = key in request.POST
2632 inputMap[key] = pclass(value)
2633 continue
2634
2635 if pclass.__name__ == 'RMapI':
2636 keyName = "%s_key0" % key
2637 valueName = "%s_value0" % key
2638 row = 0
2639 paramMap = {}
2640 while keyName in request.POST:
2641
2642 k = str(request.POST[keyName])
2643 v = str(request.POST[valueName])
2644 if len(k) > 0 and len(v) > 0:
2645 paramMap[str(k)] = str(v)
2646 row +=1
2647 keyName = "%s_key%d" % (key, row)
2648 valueName = "%s_value%d" % (key, row)
2649 if len(paramMap) > 0:
2650 inputMap[key] = wrap(paramMap)
2651 continue
2652
2653 if key in request.POST:
2654 if pclass == omero.rtypes.RListI:
2655 values = request.POST.getlist(key)
2656 if len(values) == 0: continue
2657 if len(values) == 1:
2658 if len(values[0]) == 0: continue
2659 values = values[0].split(",")
2660
2661
2662 listClass = omero.rtypes.rstring
2663 l = prototype.val
2664 if len(l) > 0:
2665 listClass = l[0].__class__
2666 if listClass == int(1).__class__:
2667 listClass = omero.rtypes.rint
2668 if listClass == long(1).__class__:
2669 listClass = omero.rtypes.rlong
2670
2671
2672 valueList = []
2673 for v in values:
2674 try:
2675 obj = listClass(str(v.strip()))
2676 except:
2677 logger.debug("Invalid entry for '%s' : %s" % (key, v))
2678 continue
2679 if isinstance(obj, omero.model.IObject):
2680 valueList.append(omero.rtypes.robject(obj))
2681 else:
2682 valueList.append(obj)
2683 inputMap[key] = omero.rtypes.rlist(valueList)
2684
2685
2686 else:
2687 value = request.POST[key]
2688 if len(value) == 0: continue
2689 try:
2690 inputMap[key] = pclass(value)
2691 except:
2692 logger.debug("Invalid entry for '%s' : %s" % (key, value))
2693 continue
2694
2695 logger.debug("Running script %s with params %s" % (scriptName, inputMap))
2696 rsp = run_script(request, conn, sId, inputMap, scriptName)
2697 return HttpResponse(simplejson.dumps(rsp), mimetype='json')
2698
2699
2700 @login_required(setGroupContext=True)
2701 -def ome_tiff_script(request, imageId, conn=None, **kwargs):
2702 """
2703 Uses the scripting service (Batch Image Export script) to generate OME-TIFF for an
2704 image and attach this as a file annotation to the image.
2705 Script will show up in the 'Activities' for users to monitor and download result etc.
2706 """
2707
2708
2709
2710 scriptService = conn.getScriptService()
2711 sId = scriptService.getScriptID("/omero/export_scripts/Batch_Image_Export.py")
2712
2713 imageIds = [long(imageId)]
2714 inputMap = {'Data_Type': wrap('Image'), 'IDs': wrap(imageIds)}
2715 inputMap['Format'] = wrap('OME-TIFF')
2716 rsp = run_script(request, conn, sId, inputMap, scriptName='Create OME-TIFF')
2717 return HttpResponse(simplejson.dumps(rsp), mimetype='json')
2718
2719
2720 -def run_script(request, conn, sId, inputMap, scriptName='Script'):
2721 """
2722 Starts running a script, adding details to the request.session so that it shows up
2723 in the webclient Activities panel and results are available there etc.
2724 """
2725 request.session.modified = True
2726 scriptService = conn.getScriptService()
2727 try:
2728 handle = scriptService.runScript(sId, inputMap, None, conn.SERVICE_OPTS)
2729
2730 jobId = str(handle)
2731 status = 'in progress'
2732 request.session['callback'][jobId] = {
2733 'job_type': "script",
2734 'job_name': scriptName,
2735 'start_time': datetime.datetime.now(),
2736 'status':status}
2737 request.session.modified = True
2738 except Exception, x:
2739 jobId = str(time())
2740 if x.message and x.message.startswith("No processor available"):
2741 logger.info(traceback.format_exc())
2742 error = "No Processor Available"
2743 status = 'no processor available'
2744 message = ""
2745 else:
2746 logger.error(traceback.format_exc())
2747 error = traceback.format_exc()
2748 status = 'failed'
2749 message = x.message
2750
2751 request.session['callback'][jobId] = {
2752 'job_type': "script",
2753 'job_name': scriptName,
2754 'start_time': datetime.datetime.now(),
2755 'status':status,
2756 'Message': message,
2757 'error':error}
2758 return {'status': status, 'error': error}
2759
2760 return {'jobId': jobId, 'status': status}
2761
2762 @login_required()
2763 @render_response()
2764 -def ome_tiff_info(request, imageId, conn=None, **kwargs):
2780