Package omeroweb :: Package webclient :: Package controller :: Module container
[hide private]
[frames] | no frames]

Source Code for Module omeroweb.webclient.controller.container

   1  #!/usr/bin/env python 
   2  # -*- coding: utf-8 -*- 
   3  #  
   4  # container 
   5  #  
   6  # Copyright (c) 2008-2011 University of Dundee. 
   7  #  
   8  # This program is free software: you can redistribute it and/or modify 
   9  # it under the terms of the GNU Affero General Public License as 
  10  # published by the Free Software Foundation, either version 3 of the 
  11  # License, or (at your option) any later version. 
  12  #  
  13  # This program is distributed in the hope that it will be useful, 
  14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  16  # GNU Affero General Public License for more details. 
  17  #  
  18  # You should have received a copy of the GNU Affero General Public License 
  19  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  20  #  
  21  # Author: Aleksandra Tarkowska <A(dot)Tarkowska(at)dundee(dot)ac(dot)uk>, 2008. 
  22  #  
  23  # Version: 1.0 
  24  # 
  25   
  26  import omero 
  27  from omero.rtypes import * 
  28  from django.conf import settings 
  29  from django.core.urlresolvers import reverse 
  30  from django.utils.encoding import smart_str 
  31  import logging 
  32   
  33  logger = logging.getLogger(__name__) 
  34   
  35  from webclient.controller import BaseController 
  36   
37 -class BaseContainer(BaseController):
38 39 project = None 40 screen = None 41 dataset = None 42 plate = None 43 acquisition = None 44 well = None 45 image = None 46 tag = None 47 file = None 48 comment = None 49 tags = None 50 51 index = None 52 containers = None 53 experimenter = None 54 55 c_size = 0 56 57 text_annotations = None 58 txannSize = 0 59 long_annotations = None 60 file_annotations = None 61 62 orphaned = False 63
64 - def __init__(self, conn, project=None, dataset=None, image=None, screen=None, plate=None, acquisition=None, well=None, tag=None, tagset=None, file=None, comment=None, annotation=None, index=None, orphaned=None, **kw):
65 BaseController.__init__(self, conn) 66 if project is not None: 67 self.project = self.conn.getObject("Project", project) 68 self.assertNotNone(self.project, project, "Project") 69 self.assertNotNone(self.project._obj, project, "Project") 70 if dataset is not None: 71 self.dataset = self.conn.getObject("Dataset", dataset) 72 self.assertNotNone(self.dataset, dataset, "Dataset") 73 self.assertNotNone(self.dataset._obj, dataset, "Dataset") 74 if screen is not None: 75 self.screen = self.conn.getObject("Screen", screen) 76 self.assertNotNone(self.screen, screen, "Screen") 77 self.assertNotNone(self.screen._obj, screen, "Screen") 78 if plate is not None: 79 self.plate = self.conn.getObject("Plate", plate) 80 self.assertNotNone(self.plate, plate, "Plate") 81 self.assertNotNone(self.plate._obj, plate, "Plate") 82 if acquisition is not None: 83 self.acquisition = self.conn.getObject("PlateAcquisition", acquisition) 84 self.assertNotNone(self.acquisition, acquisition, "Plate Acquisition") 85 self.assertNotNone(self.acquisition._obj, acquisition, "Plate Acquisition") 86 if image is not None: 87 self.image = self.conn.getObject("Image", image) 88 self.assertNotNone(self.image, image, "Image") 89 self.assertNotNone(self.image._obj, image, "Image") 90 if well is not None: 91 self.well = self.conn.getObject("Well", well) 92 self.assertNotNone(self.well, well, "Well") 93 self.assertNotNone(self.well._obj, well, "Well") 94 if index is not None: 95 self.well.index = index 96 if tag is not None: 97 self.tag = self.conn.getObject("Annotation", tag) 98 self.assertNotNone(self.tag, tag, "Tag") 99 self.assertNotNone(self.tag._obj, tag, "Tag") 100 if tagset is not None: 101 self.tag = self.conn.getObject("Annotation", tagset) 102 self.assertNotNone(self.tag, tagset, "Tag") 103 self.assertNotNone(self.tag._obj, tagset, "Tag") 104 if comment is not None: 105 self.comment = self.conn.getObject("Annotation", comment) 106 self.assertNotNone(self.comment, comment, "Comment") 107 self.assertNotNone(self.comment._obj, comment, "Comment") 108 if file is not None: 109 self.file = self.conn.getObject("Annotation", file) 110 self.assertNotNone(self.file, file, "File") 111 self.assertNotNone(self.file._obj, file, "File") 112 if annotation is not None: 113 self.annotation = self.conn.getObject("Annotation", annotation) 114 self.assertNotNone(self.annotation, annotation, "Annotation") 115 self.assertNotNone(self.annotation._obj, annotation, "Annotation") 116 if orphaned: 117 self.orphaned = True
118
119 - def assertNotNone(self, obj, obj_id, obj_name):
120 if obj is None: 121 raise AttributeError("We are sorry, but that %s (id:%s) does not exist, or if it does, you have no permission to see it." % (obj_name, obj_id))
122
123 - def _get_object(self):
124 """ 125 Since the container is often used to wrap a single Project, Dataset etc, several methods need access to 126 the underlying object. E.g. obj_type(), obj_id(), canAnnotate(), canEdit(). 127 This removes many if statements from the metadata_general.html template for places that are displaying 128 data for a single Object. E.g. Edit Name etc. 129 """ 130 if self.project is not None: return self.project 131 if self.dataset is not None: return self.dataset 132 if self.image is not None: return self.image 133 if self.screen is not None: return self.screen 134 if self.acquisition is not None: return self.acquisition 135 if self.plate is not None: return self.plate 136 if self.well is not None: return self.well 137 if self.tag is not None: return self.tag 138 if self.file is not None: return self.file
139
140 - def obj_type(self):
141 if self.project is not None: return "project" 142 if self.dataset is not None: return "dataset" 143 if self.image is not None: return "image" 144 if self.screen is not None: return "screen" 145 if self.acquisition is not None: return "acquisition" 146 if self.plate is not None: return "plate" 147 if self.well is not None: return "well" 148 if self.tag is not None: return "tag" 149 if self.file is not None: return "file"
150
151 - def obj_id(self):
152 obj = self._get_object() 153 return obj is not None and obj.id or None
154
155 - def canAnnotate(self):
156 obj = self._get_object() 157 return obj is not None and obj.canAnnotate() or False
158
159 - def canEdit(self):
160 obj = self._get_object() 161 return obj is not None and obj.canEdit() or None
162
163 - def getPermsCss(self):
164 """ Shortcut to get permissions flags, E.g. for css """ 165 return self._get_object().getPermsCss()
166
167 - def getNumberOfFields(self):
168 """ Applies to Plates (all fields) or PlateAcquisitions""" 169 if self.plate is not None: 170 return self.plate.getNumberOfFields() 171 elif self.acquisition: 172 p = self.conn.getObject("Plate", self.acquisition._obj.plate.id.val) 173 return p.getNumberOfFields(self.acquisition.getId())
174
175 - def getPlateId(self):
176 """ Used by templates that display Plates or PlateAcquisitions """ 177 if self.plate is not None: 178 return self.plate.getId() 179 elif self.acquisition: 180 return self.acquisition._obj.plate.id.val
181 182
183 - def listFigureScripts(self, objDict=None):
184 """ 185 This configures all the Figure Scripts, setting their enabled status given the 186 currently selected object (self.image etc) or batch objects (uses objDict). 187 """ 188 figureScripts = [] 189 # id is used in url and is mapped to full script path by views.figure_script() 190 splitView = {'id': 'SplitView', 'name':'Split View Figure', 'enabled': False, 191 'tooltip': "Create a figure of images, splitting their channels into separate views"} 192 # Split View Figure is enabled if we have at least one image with SizeC > 1 193 if self.image: 194 splitView['enabled'] = (self.image.getSizeC() > 1) 195 elif objDict is not None: 196 if 'image' in objDict: 197 for i in objDict['image']: 198 if i.getSizeC() > 1: 199 splitView['enabled'] = True 200 break 201 thumbnailFig = {'id': 'Thumbnail', 'name': 'Thumbnail Figure', 'enabled': False, 202 'tooltip': "Export a figure of thumbnails, optionally sorted by tag"} 203 # Thumbnail figure is enabled if we have Datasets or Images selected 204 if self.image or self.dataset: 205 thumbnailFig['enabled'] = True 206 elif objDict is not None: 207 if 'image' in objDict or 'dataset' in objDict: 208 thumbnailFig['enabled'] = True 209 figureScripts.append(splitView) 210 figureScripts.append(thumbnailFig) 211 return figureScripts
212 213
214 - def openAstexViewerCompatible(self):
215 """ 216 Is the image suitable to be viewed with the Volume viewer 'Open Astex Viewer' applet? 217 Image must be a 'volume' of suitable dimensions and not too big. 218 """ 219 MAX_SIDE = settings.OPEN_ASTEX_MAX_SIDE # default is 400 220 MIN_SIDE = settings.OPEN_ASTEX_MIN_SIDE # default is 20 221 MAX_VOXELS = settings.OPEN_ASTEX_MAX_VOXELS # default is 15625000 (250 * 250 * 250) 222 223 if self.image is None: 224 return False 225 sizeZ = self.image.getSizeZ() 226 if self.image.getSizeC() > 1: return False 227 sizeX = self.image.getSizeX() 228 sizeY = self.image.getSizeY() 229 if sizeZ < MIN_SIDE or sizeX < MIN_SIDE or sizeY < MIN_SIDE: return False 230 if sizeX > MAX_SIDE or sizeY > MAX_SIDE or sizeZ > MAX_SIDE: return False 231 voxelCount = (sizeX * sizeY * sizeZ) 232 if voxelCount > MAX_VOXELS: return False 233 234 try: # if scipy ndimage is not available for interpolation, can only handle smaller images 235 import scipy.ndimage 236 except ImportError: 237 logger.debug("Failed to import scipy.ndimage - Open Astex Viewer limited to display of smaller images.") 238 MAX_VOXELS = (160 * 160 * 160) 239 if voxelCount > MAX_VOXELS: return False 240 241 return True
242
243 - def formatMetadataLine(self, l):
244 if len(l) < 1: 245 return None 246 return l.split("=")
247
248 - def originalMetadata(self):
249 # TODO: hardcoded values. 250 self.global_metadata = list() 251 self.series_metadata = list() 252 self.companion_files = list() 253 if self.image is not None: 254 om = self.image.loadOriginalMetadata() 255 elif self.well.getWellSample().image is not None: 256 om = self.well.getWellSample().image().loadOriginalMetadata() 257 if om is not None: 258 self.original_metadata = om[0] 259 self.global_metadata = om[1] 260 self.series_metadata = om[2] 261 # Look for companion files on the Image 262 if self.image is not None: 263 comp_obj = self.image 264 p = self.image.getPlate() 265 # in SPW model, companion files can be found on Plate 266 if p is not None: 267 comp_obj = p 268 for ann in comp_obj.listAnnotations(): 269 if hasattr(ann._obj, "file") and ann.ns == omero.constants.namespaces.NSCOMPANIONFILE: 270 if ann.getFileName() != omero.constants.annotation.file.ORIGINALMETADATA: 271 self.companion_files.append(ann)
272 273
274 - def channelMetadata(self):
275 self.channel_metadata = None 276 try: 277 if self.image is not None: 278 self.channel_metadata = self.image.getChannels() 279 elif self.well is not None: 280 self.channel_metadata = self.well.getWellSample().image().getChannels() 281 except: 282 pass 283 284 if self.channel_metadata is None: 285 self.channel_metadata = list()
286
287 - def loadTags(self, eid=None):
288 if eid is not None: 289 if eid == -1: # Load data for all users 290 eid = None 291 else: 292 self.experimenter = self.conn.getObject("Experimenter", eid) 293 else: 294 eid = self.conn.getEventContext().userId 295 self.tags = list(self.conn.listTags(eid)) 296 self.t_size = len(self.tags)
297
298 - def loadDataByTag(self):
299 pr_list = list(self.conn.getObjectsByAnnotations('Project',[self.tag.id])) 300 ds_list = list(self.conn.getObjectsByAnnotations('Dataset',[self.tag.id])) 301 im_list = list(self.conn.getObjectsByAnnotations('Image',[self.tag.id])) 302 sc_list = list(self.conn.getObjectsByAnnotations('Screen',[self.tag.id])) 303 pl_list = list(self.conn.getObjectsByAnnotations('Plate',[self.tag.id])) 304 pa_list = list(self.conn.getObjectsByAnnotations('PlateAcquisition',[self.tag.id])) 305 306 pr_list.sort(key=lambda x: x.getName() and x.getName().lower()) 307 ds_list.sort(key=lambda x: x.getName() and x.getName().lower()) 308 im_list.sort(key=lambda x: x.getName() and x.getName().lower()) 309 sc_list.sort(key=lambda x: x.getName() and x.getName().lower()) 310 pl_list.sort(key=lambda x: x.getName() and x.getName().lower()) 311 pa_list.sort(key=lambda x: x.getName() and x.getName().lower()) 312 313 self.containers={'projects': pr_list, 'datasets': ds_list, 'images': im_list, 314 'screens':sc_list, 'plates':pl_list, 'aquisitions': pa_list} 315 self.c_size = len(pr_list)+len(ds_list)+len(im_list)+len(sc_list)+len(pl_list)+len(pa_list)
316
317 - def listImagesInDataset(self, did, eid=None, page=None, load_pixels=False):
318 if eid is not None: 319 if eid == -1: # Load data for all users 320 eid = None 321 else: 322 self.experimenter = self.conn.getObject("Experimenter", eid) 323 im_list = list(self.conn.listImagesInDataset(oid=did, eid=eid, page=page, load_pixels=load_pixels)) 324 im_list.sort(key=lambda x: x.getName().lower()) 325 self.containers = {'images': im_list} 326 self.c_size = self.conn.getCollectionCount("Dataset", "imageLinks", [long(did)])[long(did)] 327 328 if page is not None: 329 self.paging = self.doPaging(page, len(im_list), self.c_size)
330
331 - def listContainerHierarchy(self, eid=None):
332 if eid is not None: 333 if eid == -1: 334 eid = None 335 else: 336 self.experimenter = self.conn.getObject("Experimenter", eid) 337 else: 338 eid = self.conn.getEventContext().userId 339 pr_list = list(self.conn.listProjects(eid)) 340 ds_list = list(self.conn.listOrphans("Dataset", eid)) 341 sc_list = list(self.conn.listScreens(eid)) 342 pl_list = list(self.conn.listOrphans("Plate", eid)) 343 344 pr_list.sort(key=lambda x: x.getName() and x.getName().lower()) 345 ds_list.sort(key=lambda x: x.getName() and x.getName().lower()) 346 sc_list.sort(key=lambda x: x.getName() and x.getName().lower()) 347 pl_list.sort(key=lambda x: x.getName() and x.getName().lower()) 348 349 self.orphans = self.conn.countOrphans("Image", eid) 350 351 self.containers={'projects': pr_list, 'datasets': ds_list, 'screens': sc_list, 'plates': pl_list} 352 self.c_size = len(pr_list)+len(ds_list)+len(sc_list)+len(pl_list)
353
354 - def listOrphanedImages(self, eid=None, page=None):
355 if eid is not None: 356 if eid == -1: 357 eid = None 358 else: 359 self.experimenter = self.conn.getObject("Experimenter", eid) 360 else: 361 eid = self.conn.getEventContext().userId 362 363 params = omero.sys.ParametersI() 364 if page is not None: 365 params.page((int(page)-1)*settings.PAGE, settings.PAGE) 366 im_list = list(self.conn.listOrphans("Image", eid=eid, params=params)) 367 im_list.sort(key=lambda x: x.getName().lower()) 368 self.containers = {'orphaned': True, 'images': im_list} 369 self.c_size = self.conn.countOrphans("Image", eid=eid) 370 371 if page is not None: 372 self.paging = self.doPaging(page, len(im_list), self.c_size)
373 374 # Annotation list
375 - def annotationList(self):
376 self.text_annotations = list() 377 self.rating_annotations = list() 378 self.file_annotations = list() 379 self.tag_annotations = list() 380 self.xml_annotations = list() 381 self.boolean_annotations = list() 382 self.double_annotations = list() 383 self.long_annotations = list() 384 self.term_annotations = list() 385 self.time_annotations = list() 386 self.companion_files = list() 387 388 annTypes = {omero.model.CommentAnnotationI: self.text_annotations, 389 omero.model.LongAnnotationI: self.long_annotations, 390 omero.model.FileAnnotationI: self.file_annotations, 391 omero.model.TagAnnotationI: self.tag_annotations, 392 omero.model.XmlAnnotationI: self.xml_annotations, 393 omero.model.BooleanAnnotationI: self.boolean_annotations, 394 omero.model.DoubleAnnotationI: self.double_annotations, 395 omero.model.TermAnnotationI: self.term_annotations, 396 omero.model.TimestampAnnotationI: self.time_annotations} 397 398 aList = list() 399 if self.image is not None: 400 aList = list(self.image.listAnnotations()) 401 elif self.dataset is not None: 402 aList = list(self.dataset.listAnnotations()) 403 elif self.project is not None: 404 aList = list(self.project.listAnnotations()) 405 elif self.screen is not None: 406 aList = list(self.screen.listAnnotations()) 407 elif self.plate is not None: 408 aList = list(self.plate.listAnnotations()) 409 elif self.acquisition is not None: 410 aList = list(self.acquisition.listAnnotations()) 411 elif self.well is not None: 412 aList = list(self.well.getWellSample().image().listAnnotations()) 413 414 for ann in aList: 415 annClass = ann._obj.__class__ 416 if annClass in annTypes: 417 if ann.ns == omero.constants.metadata.NSINSIGHTRATING: 418 self.rating_annotations.append(ann) 419 elif ann.ns == omero.constants.namespaces.NSCOMPANIONFILE: 420 if ann.getFileName() != omero.constants.annotation.file.ORIGINALMETADATA: 421 self.companion_files.append(ann) 422 else: 423 annTypes[annClass].append(ann) 424 425 self.text_annotations.sort(key=lambda x: x.creationEventDate(), reverse=True) 426 self.file_annotations.sort(key=lambda x: x.creationEventDate()) 427 self.rating_annotations.sort(key=lambda x: x.creationEventDate()) 428 self.tag_annotations.sort(key=lambda x: x.textValue) 429 430 self.txannSize = len(self.text_annotations) 431 self.fileannSize = len(self.file_annotations) 432 self.tgannSize = len(self.tag_annotations)
433
434 - def canUseOthersAnns(self):
435 """ 436 Test to see whether other user's Tags, Files etc should be provided for annotating. 437 Used to ensure that E.g. Group Admins / Owners don't try to link other user's Annotations 438 when in a private group (even though they could retrieve those annotations) 439 """ 440 gid = self.conn.SERVICE_OPTS.getOmeroGroup() 441 if gid is None: 442 return False 443 try: 444 group = self.conn.getObject("ExperimenterGroup", long(gid)) 445 except: 446 return False 447 if group is None: 448 return False 449 perms = str(group.getDetails().getPermissions()) 450 rv = False 451 if perms in ("rwrw--", "rwra--"): 452 return True 453 if perms == "rwr---" and (self.conn.isAdmin() or self.conn.isLeader(group.id)): 454 return True 455 return False
456 457
458 - def loadBatchAnnotations(self, objDict, ann_ids=None, addedByMe=False):
459 """ 460 Look up the Tags, Files, Comments, Ratings etc that are on one or more of 461 the objects in objDect. 462 463 """ 464 465 batchAnns = { 466 omero.model.CommentAnnotationI: 'Comment', 467 omero.model.LongAnnotationI: 'Long', 468 omero.model.FileAnnotationI: 'File', 469 omero.model.TagAnnotationI: 'Tag', 470 omero.model.XmlAnnotationI: 'Xml', 471 omero.model.BooleanAnnotationI: 'Boolean', 472 omero.model.DoubleAnnotationI: 'Double', 473 omero.model.TermAnnotationI: 'Term', 474 omero.model.TimestampAnnotationI: 'TimeStamp' 475 } 476 477 # return, E.g {"Tag": {AnnId: {'ann': ObjWrapper, 'parents': [ImageWrapper, etc] } }, etc...} 478 rv = {} 479 # populate empty return map 480 for key, value in batchAnns.items(): 481 rv[value] = {} 482 483 params = omero.sys.Parameters() 484 params.theFilter = omero.sys.Filter() 485 if addedByMe: 486 params.theFilter.ownerId = omero.rtypes.rlong(self.conn.getUserId()) 487 for objType, objList in objDict.items(): 488 if len(objList) == 0: 489 continue 490 parent_ids = [o.getId() for o in objList] 491 # If we're working with a 'well', we're actually annotating the image 492 for i in range(len(objList)): 493 o = objList[i] 494 if isinstance(o._obj, omero.model.WellI): 495 objType = "Image" 496 parent_ids[i] = o.getWellSample().image().getId() # index has already been set 497 if isinstance(objList[0]._obj, omero.model.PlateAcquisitionI): 498 objType = 'PlateAcquisition' 499 for annLink in self.conn.getAnnotationLinks(objType, parent_ids=parent_ids, ann_ids=ann_ids, params=params): 500 ann = annLink.getAnnotation() 501 if ann.ns == omero.constants.metadata.NSINSIGHTRATING: 502 continue # TODO: Handle ratings 503 if ann.ns == omero.constants.namespaces.NSCOMPANIONFILE: 504 continue 505 annClass = ann._obj.__class__ 506 if annClass in batchAnns: 507 annotationsMap = rv[ batchAnns[annClass] ] # E.g. map for 'Tags' 508 if ann.getId() not in annotationsMap: 509 annotationsMap[ann.getId()] = { 510 'ann': ann, 511 'links': [annLink], 512 'unlink': 0} 513 else: 514 annotationsMap[ann.getId()]['links'].append( annLink ) 515 if annLink.canDelete(): 516 annotationsMap[ann.getId()]['unlink'] += 1 517 518 # bit more preparation for display... 519 batchAnns = {} 520 for key, annMap in rv.items(): 521 # E.g. key = 'Tag', 'Comment', 'File' etc 522 annList = [] 523 for annId, annDict in annMap.items(): 524 # ann is {'ann':AnnWrapper, 'links'[AnnotationLinkWrapper, ..]} 525 annDict['links'].sort(key=lambda x: x.parent.id.val) # Each ann has links to several objects 526 annDict['can_remove'] = annDict['unlink'] > 0 527 annList.append(annDict) 528 batchAnns[key] = annList 529 return batchAnns
530 531
532 - def getTagsByObject(self, parent_type=None, parent_ids=None):
533 eid = (not self.canUseOthersAnns()) and self.conn.getEventContext().userId or None 534 535 def sort_tags(tag_gen): 536 tag_anns = list(tag_gen) 537 try: 538 tag_anns.sort(key=lambda x: x.getValue().lower()) 539 except: pass 540 return tag_anns
541 542 if self.image is not None: 543 return sort_tags(self.image.listOrphanedAnnotations(eid=eid, anntype='Tag')) 544 elif self.dataset is not None: 545 return sort_tags(self.dataset.listOrphanedAnnotations(eid=eid, anntype='Tag')) 546 elif self.project is not None: 547 return sort_tags(self.project.listOrphanedAnnotations(eid=eid, anntype='Tag')) 548 elif self.well is not None: 549 return sort_tags(self.well.getWellSample().image().listOrphanedAnnotations(eid=eid, anntype='Tag')) 550 elif self.plate is not None: 551 return sort_tags(self.plate.listOrphanedAnnotations(eid=eid, anntype='Tag')) 552 elif self.screen is not None: 553 return sort_tags(self.screen.listOrphanedAnnotations(eid=eid, anntype='Tag')) 554 elif self.acquisition is not None: 555 return sort_tags(self.acquisition.listOrphanedAnnotations(eid=eid, anntype='Tag')) 556 elif parent_type and parent_ids: 557 parent_type = parent_type.title() 558 if parent_type == "Acquisition": 559 parent_type = "PlateAcquisition" 560 return sort_tags(self.conn.listOrphanedAnnotations(parent_type, parent_ids, eid=eid, anntype='Tag')) 561 else: 562 if eid is not None: 563 params = omero.sys.Parameters() 564 params.theFilter = omero.sys.Filter() 565 params.theFilter.ownerId = omero.rtypes.rlong(eid) 566 return sort_tags(self.conn.getObjects("TagAnnotation", params=params)) 567 return sort_tags(self.conn.getObjects("TagAnnotation"))
568
569 - def getFilesByObject(self, parent_type=None, parent_ids=None):
570 eid = (not self.canUseOthersAnns()) and self.conn.getEventContext().userId or None 571 ns = [omero.constants.namespaces.NSCOMPANIONFILE, omero.constants.namespaces.NSEXPERIMENTERPHOTO] 572 573 def sort_file_anns(file_ann_gen): 574 file_anns = list(file_ann_gen) 575 try: 576 file_anns.sort(key=lambda x: x.getFile().getName().lower()) 577 except: pass 578 return file_anns
579 580 if self.image is not None: 581 return sort_file_anns(self.image.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 582 elif self.dataset is not None: 583 return sort_file_anns(self.dataset.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 584 elif self.project is not None: 585 return sort_file_anns(self.project.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 586 elif self.well is not None: 587 return sort_file_anns(self.well.getWellSample().image().listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 588 elif self.plate is not None: 589 return sort_file_anns(self.plate.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 590 elif self.screen is not None: 591 return sort_file_anns(self.screen.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 592 elif self.acquisition is not None: 593 return sort_file_anns(self.acquisition.listOrphanedAnnotations(eid=eid, ns=ns, anntype='File')) 594 elif parent_type and parent_ids: 595 parent_type = parent_type.title() 596 if parent_type == "Acquisition": 597 parent_type = "PlateAcquisition" 598 return sort_file_anns(self.conn.listOrphanedAnnotations(parent_type, parent_ids, eid=eid, ns=ns, anntype='File')) 599 else: 600 return sort_file_anns(self.conn.listFileAnnotations(eid=eid)) 601 #################################################################### 602 # Creation 603
604 - def createDataset(self, name, description=None, img_ids=None):
605 ds = omero.model.DatasetI() 606 ds.name = rstring(str(name)) 607 if description is not None and description != "" : 608 ds.description = rstring(str(description)) 609 if self.project is not None: 610 l_ds = omero.model.ProjectDatasetLinkI() 611 l_ds.setParent(self.project._obj) 612 l_ds.setChild(ds) 613 ds.addProjectDatasetLink(l_ds) 614 dsid = self.conn.saveAndReturnId(ds) 615 if img_ids is not None: 616 iids = [int(i) for i in img_ids.split(",")] 617 links = [] 618 for iid in iids: 619 link = omero.model.DatasetImageLinkI() 620 link.setParent(omero.model.DatasetI(dsid, False)) 621 link.setChild(omero.model.ImageI(iid, False)) 622 links.append(link) 623 self.conn.saveArray(links) 624 return dsid
625
626 - def createProject(self, name, description=None):
627 pr = omero.model.ProjectI() 628 pr.name = rstring(str(name)) 629 if description is not None and description != "" : 630 pr.description = rstring(str(description)) 631 return self.conn.saveAndReturnId(pr)
632
633 - def createScreen(self, name, description=None):
634 sc = omero.model.ScreenI() 635 sc.name = rstring(str(name)) 636 if description is not None and description != "" : 637 sc.description = rstring(str(description)) 638 return self.conn.saveAndReturnId(sc)
639 640
641 - def checkMimetype(self, file_type):
642 if file_type is None or len(file_type) == 0: 643 file_type = "application/octet-stream" 644 return file_type
645 646
647 - def createCommentAnnotations(self, content, oids, well_index=0):
648 ann = omero.model.CommentAnnotationI() 649 ann.textValue = rstring(str(content)) 650 ann = self.conn.saveAndReturnObject(ann) 651 652 new_links = list() 653 for k in oids.keys(): 654 if len(oids[k]) > 0: 655 for ob in oids[k]: 656 if isinstance(ob._obj, omero.model.WellI): 657 t = 'Image' 658 obj = ob.getWellSample(well_index).image() 659 elif isinstance(ob._obj, omero.model.PlateAcquisitionI): 660 t = 'PlateAcquisition' 661 obj = ob 662 else: 663 t = k.lower().title() 664 obj = ob 665 l_ann = getattr(omero.model, t+"AnnotationLinkI")() 666 l_ann.setParent(obj._obj) 667 l_ann.setChild(ann._obj) 668 new_links.append(l_ann) 669 670 if len(new_links) > 0 : 671 self.conn.saveArray(new_links) 672 return self.conn.getObject("CommentAnnotation", ann.getId())
673 674
675 - def createTagAnnotations(self, tag, desc, oids, well_index=0):
676 """ 677 Creates a new tag (with description) OR uses existing tag with the specified name if found. 678 Links the tag to the specified objects. 679 @param tag: Tag text/name 680 @param desc: Tag description 681 @param oids: Dict of Objects and IDs. E.g. {"Image": [1,2,3], "Dataset", [6]} 682 """ 683 ann = None 684 try: 685 ann = self.conn.findTag(tag, desc) 686 except: 687 pass 688 if ann is None: 689 ann = omero.model.TagAnnotationI() 690 ann.textValue = rstring(str(tag)) 691 ann.setDescription(rstring(str(desc))) 692 ann = self.conn.saveAndReturnObject(ann) 693 694 new_links = list() 695 parent_objs = [] 696 for k in oids: 697 if len(oids[k]) > 0: 698 for ob in oids[k]: 699 if isinstance(ob._obj, omero.model.WellI): 700 t = 'Image' 701 obj = ob.getWellSample(well_index).image() 702 elif isinstance(ob._obj, omero.model.PlateAcquisitionI): 703 t = 'PlateAcquisition' 704 obj = ob 705 else: 706 t = k.lower().title() 707 obj = ob 708 parent_objs.append(obj) 709 l_ann = getattr(omero.model, t+"AnnotationLinkI")() 710 l_ann.setParent(obj._obj) 711 l_ann.setChild(ann._obj) 712 new_links.append(l_ann) 713 714 if len(new_links) > 0 : 715 # If we retrieved an existing Tag above, link may already exist... 716 try: 717 self.conn.saveArray(new_links) 718 except omero.ValidationException, x: 719 for l in new_links: 720 try: 721 self.conn.saveObject(l) 722 except: 723 pass 724 return ann.getId()
725 726
727 - def createFileAnnotations(self, newFile, oids, well_index=0):
728 format = self.checkMimetype(newFile.content_type) 729 730 oFile = omero.model.OriginalFileI() 731 oFile.setName(rstring(smart_str(newFile.name))); 732 oFile.setPath(rstring(smart_str(newFile.name))); 733 oFile.setSize(rlong(long(newFile.size))); 734 oFile.setSha1(rstring("pending")); 735 oFile.setMimetype(rstring(str(format))); 736 737 ofid = self.conn.saveAndReturnId(oFile); 738 of = self.conn.saveAndReturnFile(newFile, ofid) 739 740 fa = omero.model.FileAnnotationI() 741 fa.setFile(of) 742 fa = self.conn.saveAndReturnObject(fa) 743 744 new_links = list() 745 otype = None # needed if we only have a single Object 746 for k in oids: 747 if len(oids[k]) > 0: 748 for ob in oids[k]: 749 if isinstance(ob._obj, omero.model.WellI): 750 t = 'Image' 751 obj = ob.getWellSample(well_index).image() 752 elif isinstance(ob._obj, omero.model.PlateAcquisitionI): 753 t = 'PlateAcquisition' 754 obj = ob 755 else: 756 t = k.lower().title() 757 obj = ob 758 otype = t 759 l_ann = getattr(omero.model, t+"AnnotationLinkI")() 760 l_ann.setParent(obj._obj) 761 l_ann.setChild(fa._obj) 762 new_links.append(l_ann) 763 if len(new_links) > 0 : 764 new_links = self.conn.getUpdateService().saveAndReturnArray(new_links, self.conn.SERVICE_OPTS) 765 return fa.getId()
766 767 824 825 ################################################################ 826 # Update 827
828 - def updateDescription(self, o_type, description=None):
829 obj = getattr(self, o_type)._obj 830 if description is not None and description != "" : 831 obj.description = rstring(str(description)) 832 else: 833 obj.description = None 834 self.conn.saveObject(obj)
835
836 - def updateName(self, o_type, name):
837 obj = getattr(self, o_type)._obj 838 if o_type not in ('tag', 'tagset'): 839 obj.name = rstring(str(name)) 840 else: 841 obj.textValue = rstring(str(name)) 842 self.conn.saveObject(obj)
843
844 - def updateImage(self, name, description=None):
845 img = self.image._obj 846 img.name = rstring(str(name)) 847 if description is not None and description != "" : 848 img.description = rstring(str(description)) 849 else: 850 img.description = None 851 self.conn.saveObject(img)
852
853 - def updateDataset(self, name, description=None):
854 container = self.dataset._obj 855 container.name = rstring(str(name)) 856 if description is not None and description != "" : 857 container.description = rstring(str(description)) 858 else: 859 container.description = None 860 self.conn.saveObject(container)
861
862 - def updatePlate(self, name, description=None):
863 container = self.plate._obj 864 container.name = rstring(str(name)) 865 if description is not None and description != "" : 866 container.description = rstring(str(description)) 867 else: 868 container.description = None 869 self.conn.saveObject(container)
870
871 - def updateProject(self, name, description=None):
872 container = self.project._obj 873 container.name = rstring(str(name)) 874 if description is not None and description != "" : 875 container.description = rstring(str(description)) 876 else: 877 container.description = None 878 self.conn.saveObject(container)
879
880 - def updateScreen(self, name, description=None):
881 container = self.screen._obj 882 container.name = rstring(str(name)) 883 if description is not None and description != "" : 884 container.description = rstring(str(description)) 885 else: 886 container.description = None 887 self.conn.saveObject(container)
888 889
890 - def move(self, parent, destination):
891 if self.project is not None: 892 return 'Cannot move project.' 893 elif self.dataset is not None: 894 if destination[0] == 'dataset': 895 return 'Cannot move dataset to dataset' 896 elif destination[0] == 'project': 897 up_pdl = None 898 pdls = self.dataset.getParentLinks() 899 already_there = None 900 901 for pdl in pdls: 902 if pdl.parent.id.val == long(destination[1]): 903 already_there = True 904 if pdl.parent.id.val == long(parent[1]): 905 up_pdl = pdl 906 if already_there: 907 if long(parent[1]) != long(destination[1]): 908 self.conn.deleteObjectDirect(up_pdl._obj) 909 else: 910 new_pr = self.conn.getObject("Project", destination[1]) 911 if parent[0] not in ('experimenter', 'orphaned'): 912 up_pdl.setParent(new_pr._obj) 913 self.conn.saveObject(up_pdl._obj) 914 else: 915 up_pdl = omero.model.ProjectDatasetLinkI() 916 up_pdl.setChild(self.dataset._obj) 917 up_pdl.setParent(new_pr._obj) 918 self.conn.saveObject(up_pdl) 919 elif destination[0] == 'experimenter': 920 up_pdl = None 921 for p in self.dataset.getParentLinks(): 922 if p.parent.id.val == long(parent[1]): 923 up_pdl = p 924 self.conn.deleteObjectDirect(up_pdl._obj) 925 elif destination[0] == 'orphaned': 926 return 'Cannot move dataset to orphaned images.' 927 else: 928 return 'Destination not supported.' 929 elif self.image is not None: 930 if destination[0] == 'dataset': 931 up_dsl = None 932 dsls = self.image.getParentLinks() #gets every links for child 933 already_there = None 934 935 #checks links 936 for dsl in dsls: 937 #if is already linked to destination 938 if dsl.parent.id.val == long(destination[1]): 939 already_there = True 940 # gets old parent to update or delete 941 if dsl.parent.id.val == long(parent[1]): 942 up_dsl = dsl 943 if already_there: 944 # delete link to not duplicate 945 if long(parent[1]) != long(destination[1]): 946 self.conn.deleteObjectDirect(up_dsl._obj) 947 else: 948 # update link to new destination 949 new_ds = self.conn.getObject("Dataset", destination[1]) 950 if parent[0] not in ('experimenter', 'orphaned'): 951 up_dsl.setParent(new_ds._obj) 952 self.conn.saveObject(up_dsl._obj) 953 else: 954 up_dsl = omero.model.DatasetImageLinkI() 955 up_dsl.setChild(self.image._obj) 956 up_dsl.setParent(new_ds._obj) 957 self.conn.saveObject(up_dsl) 958 elif destination[0] == 'project': 959 return 'Cannot move image to project.' 960 elif destination[0] == 'experimenter' or destination[0] == 'orphaned': 961 if parent[0] != destination[0]: 962 up_dsl = None 963 dsls = list(self.image.getParentLinks()) #gets every links for child 964 if len(dsls) == 1: 965 # gets old parent to delete 966 if dsls[0].parent.id.val == long(parent[1]): 967 up_dsl = dsls[0] 968 self.conn.deleteObjectDirect(up_dsl._obj) 969 else: 970 return 'This image is linked in multiple places. Please unlink the image first.' 971 else: 972 return 'Destination not supported.' 973 elif self.screen is not None: 974 return 'Cannot move screen.' 975 elif self.plate is not None: 976 if destination[0] == 'plate': 977 return 'Cannot move plate to plate' 978 elif destination[0] == 'screen': 979 up_spl = None 980 spls = self.plate.getParentLinks() 981 already_there = None 982 983 for spl in spls: 984 if spl.parent.id.val == long(destination[1]): 985 already_there = True 986 if spl.parent.id.val == long(parent[1]): 987 up_spl = spl 988 if already_there: 989 if long(parent[1]) != long(destination[1]): 990 self.conn.deleteObjectDirect(up_spl._obj) 991 else: 992 new_sc = self.conn.getObject("Screen", destination[1]) 993 if parent[0] not in ('experimenter', 'orphaned'): 994 up_spl.setParent(new_sc._obj) 995 self.conn.saveObject(up_spl._obj) 996 else: 997 up_spl = omero.model.ScreenPlateLinkI() 998 up_spl.setChild(self.plate._obj) 999 up_spl.setParent(new_sc._obj) 1000 self.conn.saveObject(up_spl) 1001 elif destination[0] == 'experimenter' or destination[0] == 'orphaned': 1002 if parent[0] != destination[0]: 1003 up_spl = None 1004 spls = list(self.plate.getParentLinks()) #gets every links for child 1005 for spl in spls: 1006 if spl.parent.id.val == long(parent[1]): 1007 self.conn.deleteObjectDirect(spl._obj) 1008 break 1009 else: 1010 return 'Destination not supported.' 1011 else: 1012 return 'No data was choosen.' 1013 return
1014
1015 - def remove( self, parents, index):
1016 """ 1017 Removes the current object (file, tag, comment, dataset, plate, image) from it's parents by 1018 manually deleting the link. 1019 For Comments, we check whether it becomes an orphan & delete if true 1020 1021 @param parents: List of parent IDs, E.g. ['image-123'] 1022 """ 1023 for p in parents: 1024 parent = p.split('-') 1025 dtype = str(parent[0]) 1026 parentId = long(parent[1]) 1027 if dtype == "acquisition": 1028 dtype = "PlateAcquisition" 1029 if dtype == "well": 1030 dtype = "Image" 1031 w = self.conn.getObject("Well", parentId) 1032 parentId = w.getWellSample(index=index).image().getId() 1033 if self.tag: 1034 for al in self.tag.getParentLinks(dtype, [parentId]): 1035 if al is not None and al.canDelete(): 1036 self.conn.deleteObjectDirect(al._obj) 1037 elif self.file: 1038 for al in self.file.getParentLinks(dtype, [parentId]): 1039 if al is not None and al.canDelete(): 1040 self.conn.deleteObjectDirect(al._obj) 1041 elif self.comment: 1042 # remove the comment from specified parent 1043 for al in self.comment.getParentLinks(dtype, [parentId]): 1044 if al is not None and al.canDelete(): 1045 self.conn.deleteObjectDirect(al._obj) 1046 # if comment is orphan, delete it directly 1047 orphan = True 1048 for parentType in ["Project", "Dataset", "Image", "Screen", "Plate", "PlateAcquisition", "Well"]: 1049 annLinks = list(self.conn.getAnnotationLinks(parentType, ann_ids=[self.comment.id])) 1050 if len(annLinks) > 0: 1051 orphan = False 1052 break 1053 if orphan: 1054 self.conn.deleteObjectDirect(self.comment._obj) 1055 1056 elif self.dataset is not None: 1057 if dtype == 'project': 1058 for pdl in self.dataset.getParentLinks([parentId]): 1059 if pdl is not None: 1060 self.conn.deleteObjectDirect(pdl._obj) 1061 elif self.plate is not None: 1062 if dtype == 'screen': 1063 for spl in self.plate.getParentLinks([parentId]): 1064 if spl is not None: 1065 self.conn.deleteObjectDirect(spl._obj) 1066 elif self.image is not None: 1067 if dtype == 'dataset': 1068 for dil in self.image.getParentLinks([parentId]): 1069 if dil is not None: 1070 self.conn.deleteObjectDirect(dil._obj) 1071 else: 1072 raise AttributeError("Attribute not specified. Cannot be removed.")
1073
1074 - def removemany(self, images):
1075 if self.dataset is not None: 1076 dil = self.dataset.getParentLinks('image', images) 1077 if dil is not None: 1078 self.conn.deleteObjectDirect(dil._obj) 1079 else: 1080 raise AttributeError("Attribute not specified. Cannot be removed.")
1081 1082 ########################################################## 1083 # Copy 1084
1085 - def paste(self, destination):
1086 if self.project is not None: 1087 return 'Cannot paste project.' 1088 elif self.dataset is not None: 1089 if destination[0] == 'dataset': 1090 return 'Cannot paste dataset to dataset' 1091 elif destination[0] == 'project': 1092 pdls = self.dataset.getParentLinks() 1093 already_there = None 1094 1095 for pdl in pdls: 1096 if pdl.parent.id.val == long(destination[1]): 1097 already_there = True 1098 if already_there: 1099 return 'Dataset is already there.' 1100 else: 1101 new_pr = self.conn.getObject("Project", destination[1]) 1102 up_pdl = omero.model.ProjectDatasetLinkI() 1103 up_pdl.setChild(self.dataset._obj) 1104 up_pdl.setParent(new_pr._obj) 1105 self.conn.saveObject(up_pdl) 1106 else: 1107 return 'Destination not supported.' 1108 elif self.image is not None: 1109 if destination[0] == 'dataset': 1110 dsls = self.image.getParentLinks() #gets every links for child 1111 already_there = None 1112 1113 #checks links 1114 for dsl in dsls: 1115 #if is already linked to destination 1116 if dsl.parent.id.val == long(destination[1]): 1117 already_there = True 1118 if already_there: 1119 return 'Image is already there.' 1120 else: 1121 # update link to new destination 1122 new_ds = self.conn.getObject("Dataset", destination[1]) 1123 up_dsl = omero.model.DatasetImageLinkI() 1124 up_dsl.setChild(self.image._obj) 1125 up_dsl.setParent(new_ds._obj) 1126 self.conn.saveObject(up_dsl) 1127 elif destination[0] == 'project': 1128 return 'Cannot copy image to project.' 1129 else: 1130 return 'Destination not supported.' 1131 elif self.screen is not None: 1132 return 'Cannot paste screen.' 1133 elif self.plate is not None: 1134 if destination[0] == 'plate': 1135 return 'Cannot move plate to plate' 1136 elif destination[0] == 'screen': 1137 spls = self.plate.getParentLinks() 1138 already_there = None 1139 1140 for spl in spls: 1141 if spl.parent.id.val == long(destination[1]): 1142 already_there = True 1143 if already_there: 1144 return 'Plate is already there.' 1145 else: 1146 new_sc = self.conn.getObject("Screen", destination[1]) 1147 up_spl = omero.model.ScreenPlateLinkI() 1148 up_spl.setChild(self.plate._obj) 1149 up_spl.setParent(new_sc._obj) 1150 self.conn.saveObject(up_spl) 1151 else: 1152 return 'Destination not supported.' 1153 else: 1154 return 'No data was choosen.'
1155
1156 - def copyImageToDataset(self, source, destination=None):
1157 if destination is None: 1158 dsls = self.conn.getDatasetImageLinks(source[1]) #gets every links for child 1159 for dsl in dsls: 1160 self.conn.deleteObjectDirect(dsl._obj) 1161 else: 1162 im = self.conn.getObject("Image", source[1]) 1163 ds = self.conn.getObject("Dataset", destination[1]) 1164 new_dsl = omero.model.DatasetImageLinkI() 1165 new_dsl.setChild(im._obj) 1166 new_dsl.setParent(ds._obj) 1167 self.conn.saveObject(new_dsl)
1168
1169 - def copyImagesToDataset(self, images, dataset):
1170 if dataset is not None and dataset[0] is not "dataset": 1171 ims = self.conn.getObjects("Image", images) 1172 ds = self.conn.getObject("Dataset", dataset[1]) 1173 link_array = list() 1174 for im in ims: 1175 new_dsl = omero.model.DatasetImageLinkI() 1176 new_dsl.setChild(im._obj) 1177 new_dsl.setParent(ds._obj) 1178 link_array.append(new_dsl) 1179 self.conn.saveArray(link_array) 1180 raise AttributeError("Destination not supported")
1181
1182 - def copyDatasetToProject(self, source, destination=None):
1183 if destination is not None and destination[0] is not "project": 1184 ds = self.conn.getObject("Dataset", source[1]) 1185 pr = self.conn.getObject("Project", destination[1]) 1186 new_pdl = omero.model.ProjectDatasetLinkI() 1187 new_pdl.setChild(ds._obj) 1188 new_pdl.setParent(pr._obj) 1189 self.conn.saveObject(new_pdl) 1190 raise AttributeError("Destination not supported")
1191
1192 - def copyDatasetsToProject(self, datasets, project):
1193 if project is not None and project[0] is not "project": 1194 dss = self.conn.getObjects("Dataset", datasets) 1195 pr = self.conn.getObject("Project", project[1]) 1196 link_array = list() 1197 for ds in dss: 1198 new_pdl = omero.model.ProjectDatasetLinkI() 1199 new_pdl.setChild(ds._obj) 1200 new_pdl.setParent(pr._obj) 1201 link_array.append(new_pdl) 1202 self.conn.saveArray(link_array) 1203 raise AttributeError("Destination not supported")
1204
1205 - def copyPlateToScreen(self, source, destination=None):
1206 if destination is not None and destination[0] is not "screen": 1207 pl = self.conn.getObject("Plate", source[1]) 1208 sc = self.conn.getObject("Screen", destination[1]) 1209 new_spl = omero.model.ScreenPlateLinkI() 1210 new_spl.setChild(pl._obj) 1211 new_spl.setParent(sc._obj) 1212 self.conn.saveObject(new_spl) 1213 raise AttributeError("Destination not supported")
1214
1215 - def copyPlatesToScreen(self, plates, screen):
1216 if screen is not None and screen[0] is not "screen": 1217 pls = self.conn.getObjects("Plate", plates) 1218 sc = self.conn.getObject("Screen", screen[1]) 1219 link_array = list() 1220 for pl in pls: 1221 new_spl = omero.model.ScreenPlateLinkI() 1222 new_spl.setChild(pl._obj) 1223 new_spl.setParent(sc._obj) 1224 link_array.append(new_spl) 1225 self.conn.saveArray(link_array) 1226 raise AttributeError("Destination not supported")
1227 1228 1229 ########################################################## 1230 # Delete 1231
1232 - def deleteItem(self, child=False, anns=False):
1233 handle = None 1234 if self.image: 1235 handle = self.conn.deleteObjects("Image", [self.image.id], deleteAnns=anns) 1236 elif self.dataset: 1237 handle = self.conn.deleteObjects("Dataset", [self.dataset.id], deleteChildren=child, deleteAnns=anns) 1238 elif self.project: 1239 handle = self.conn.deleteObjects("Project", [self.project.id], deleteChildren=child, deleteAnns=anns) 1240 elif self.screen: 1241 handle = self.conn.deleteObjects("Screen", [self.screen.id], deleteChildren=child, deleteAnns=anns) 1242 elif self.plate: 1243 handle = self.conn.deleteObjects("Plate", [self.plate.id], deleteAnns=anns) 1244 elif self.comment: 1245 handle = self.conn.deleteObjects("Annotation", [self.comment.id], deleteAnns=anns) 1246 elif self.tag: 1247 handle = self.conn.deleteObjects("Annotation", [self.tag.id], deleteAnns=anns) 1248 elif self.file: 1249 handle = self.conn.deleteObjects("Annotation", [self.file.id], deleteAnns=anns) 1250 return handle
1251
1252 - def deleteObjects(self, otype, ids, child=False, anns=False):
1253 return self.conn.deleteObjects(otype, ids, deleteChildren=child, deleteAnns=anns)
1254