class LabIsoMember(IsoMember): relation = "%s/lab-iso" % RELATION_BASE_URL iso_request = member_attribute(ILabIsoRequest, 'iso_request') library_plates = collection_attribute(ILibraryPlate, 'library_plates', backref='lab_iso') requested_library_plates = terminal_attribute(str, 'requested_library_plates')
class RackMember(Member): relation = "%s/rack" % RELATION_BASE_URL label = terminal_attribute(str, 'label') barcode = terminal_attribute(str, 'barcode') comment = terminal_attribute(str, 'comment') creation_date = terminal_attribute(datetime, 'creation_date') status = member_attribute(IItemStatus, 'status') location = member_attribute(ILocation, 'location') total_containers = terminal_attribute(int, 'total_containers') specs = member_attribute(IRackSpecs, 'specs') @property def title(self): entity = self.get_entity() return '%s - %s' % (entity.barcode, entity.label or 'NO LABEL') @property def type(self): return self.get_entity().rack_type
class StockRackMember(Member): relation = "%s/stock-rack" % RELATION_BASE_URL @property def title(self): entity = self.get_entity() return '%s: %s' % (entity.__class__, entity.id) label = terminal_attribute(int, 'label') rack = member_attribute(IRack, 'rack')
class StockSampleCreationIsoRequestMember(IsoRequestMember): relation = "%s/stock-sample-creation-iso-request" % RELATION_BASE_URL stock_volume = terminal_attribute(float, 'stock_volume') stock_concentration = terminal_attribute(float, 'stock_concentration') number_designs = terminal_attribute(int, 'number_designs') molecule_design_library = member_attribute(IMoleculeDesignLibrary, 'molecule_design_library') @property def title(self): return 'Stock Sample Generation ISO Request'
class JobMember(Member): relation = '%s/job' % RELATION_BASE_URL job_type = terminal_attribute(str, 'job_type') label = terminal_attribute(str, 'label') user = member_attribute(IUser, 'user') creation_time = terminal_attribute(datetime, 'creation_time') @property def title(self): entity = self.get_entity() return '%s: %s' % (entity.job_type, entity.label)
class MoleculeDesignPoolMember(MoleculeDesignSetMember): """ """ relation = "%s/molecule-design-pool" % RELATION_BASE_URL molecule_type = member_attribute(IMoleculeType, 'molecule_type') # stock_samples = collection_attribute(IStockSample, 'stock_samples') # member_hash = terminal_attribute(str, 'member_hash') number_designs = terminal_attribute(int, 'number_designs') genes = collection_attribute(IGene, 'genes') supplier_molecule_designs = \ collection_attribute(ISupplierMoleculeDesign, 'supplier_molecule_designs')
class MyEntityChildMember(Member): relation = 'http://test.org/myentity-child' # Member. parent = member_attribute(IMyEntity, 'parent', backref='children') # Collection accessed as entity attribute and represented as # "parent equal to parent member" (backreferencing) specification. children = collection_attribute(IMyEntityGrandchild, entity_attr='children', backref='parent') # String terminal. text = terminal_attribute(str, 'text') # String terminal with different name in entity. text_rc = terminal_attribute(str, 'text_ent')
class IsoMember(Member): relation = "%s/iso" % RELATION_BASE_URL title = attribute_alias('label') iso_type = terminal_attribute(str, 'label') label = terminal_attribute(str, 'label') status = terminal_attribute(str, 'status') rack_layout = member_attribute(IRackLayout, 'rack_layout') iso_job = member_attribute(IIsoJob, 'iso_job') number_stock_racks = terminal_attribute(int, 'number_stock_racks') molecule_design_pool_set = member_attribute(IMoleculeDesignPoolSet, 'molecule_design_pool_set') optimizer_excluded_racks = terminal_attribute(str, 'optimizer_excluded_racks') optimizer_requested_tubes = terminal_attribute( str, 'optimizer_requested_tubes') preparation_plates = collection_attribute(IPlate, 'preparation_plates') aliquot_plates = collection_attribute(IPlate, 'aliquot_plates') stock_racks = collection_attribute(IStockRack, 'stock_racks') def update(self, data): if IDataElement.providedBy(data): # pylint: disable=E1101 raise SyntaxError('Should not get here.') else: Member.update(self, data)
class MoleculeDesignMember(Member): relation = "%s/molecule-design" % RELATION_BASE_URL @property def title(self): return str(self.id) molecule_type = member_attribute(IMoleculeType, 'molecule_type') chemical_structures = collection_attribute(IChemicalStructure, 'chemical_structures') genes = collection_attribute(IGene, 'genes', cardinality=CARDINALITIES.MANYTOMANY) supplier_molecule_designs = \ collection_attribute(ISupplierMoleculeDesign, 'supplier_molecule_designs')
class GeneMember(Member): relation = "%s/gene" % RELATION_BASE_URL title = attribute_alias('locus_name') accession = terminal_attribute(str, 'accession') locus_name = terminal_attribute(str, 'locus_name') # nice_name = terminal_attribute('nice_name') species = member_attribute(ISpecies, 'species') molecule_designs = \ collection_attribute(IMoleculeDesign, 'molecule_designs', cardinality=CARDINALITIES.MANYTOMANY) molecule_design_pools = \ collection_attribute(IMoleculeDesignPool, 'molecule_design_pools', cardinality=CARDINALITIES.MANYTOMANY)
class SubprojectMember(Member): relation = "%s/subproject" % RELATION_BASE_URL label = terminal_attribute(str, 'label') project = member_attribute(IProject, 'project') creation_date = terminal_attribute(datetime, 'creation_date') active = terminal_attribute(bool, 'active') @property def title(self): # The title is formed from the project's and the subproject's label # and does not need to be persisted or exposed. return self.get_entity().title def update(self, member): super(SubprojectMember, self).update(member) self.label = member.label self.active = member.active
class MyEntityMember(Member): relation = 'http://test.org/myentity' # Member. parent = member_attribute(IMyEntityParent, 'parent', cardinality=CARDINALITIES.ONETOONE, backref='child') # Collection. children = collection_attribute(IMyEntityChild, 'children', backref='parent') # String terminal. text = terminal_attribute(str, 'text') # String terminal with different name in entity. text_rc = terminal_attribute(str, 'text_ent') # Number terminal. number = terminal_attribute(int, 'number') # Date time terminal. date_time = terminal_attribute(datetime.datetime, 'date_time') # Dotted attribute. parent_text = terminal_attribute(str, 'parent.text_ent')
class RackLayoutMember(Member): relation = '%s/rack-layout' % RELATION_BASE_URL @property def title(self): return str(self.id) shape = member_attribute(IRackShape, 'shape') tagged_rack_position_sets = \ collection_attribute(ITaggedRackPositionSet, 'tagged_rack_position_sets') # @property # def rack(self): # tags = [] # for tp in self.tagged_rack_position_sets: # tags.append(tp) # return tags def __getitem__(self, name): if name == 'tagged-rack-position-sets': return self.tagged_rack_position_sets else: raise KeyError(name)
class MoleculeDesignPoolRegistrationItemMember(Member): relation = "%s/molecule-design-pool-registration-item" % RELATION_BASE_URL molecule_type = member_attribute(IMoleculeType, 'molecule_type') molecule_design_registration_items = \ collection_attribute(IMoleculeDesignRegistrationItem, 'molecule_design_registration_items')
class MoleculeDesignRegistrationItemMember(Member): relation = "%s/molecule-design-registration-item" % RELATION_BASE_URL molecule_type = member_attribute(IMoleculeType, 'molecule_type') chemical_structures = collection_attribute(IChemicalStructure, 'chemical_structures')
class IncidenceMember(Member): relation = 'http://plantscribe.org/relations/incidence' species = member_attribute(ISpecies, 'species') site = member_attribute(ISite, 'site') quantity = terminal_attribute(float, 'quantity')
class LabIsoRequestMember(IsoRequestMember): relation = "%s/lab-iso-request" % RELATION_BASE_URL isos = collection_attribute(ILabIso, 'isos') delivery_date = terminal_attribute(datetime, 'delivery_date') requester = member_attribute(IUser, 'requester') experiment_metadata = member_attribute(IExperimentMetadata, 'experiment_metadata') # experiment_metadata_type = \ # member_attribute(IExperimentMetadataType, # 'experiment_metadata.experiment_metadata_type') rack_layout = member_attribute(IRackLayout, 'rack_layout') process_job_first = terminal_attribute(bool, 'process_job_first') ticket_number = terminal_attribute(int, 'experiment_metadata.ticket_number') @property def title(self): return 'Lab ISO Request' def __getitem__(self, name): if name == 'completed-iso-plates' and self.iso_type == ISO_TYPES.LAB: # These are the plates that can be used as input for experiment # job scheduling. iso_plate_bcs = [] for iso in self.isos: if iso.status == ISO_STATUS.DONE: for plt in self.__get_completed_iso_plates_for_iso(iso): iso_plate_bcs.append(plt.barcode) iso_plates = get_root_collection(IPlate) iso_plates.filter = cntd(barcode=iso_plate_bcs) result = iso_plates else: result = Member.__getitem__(self, name) return result def update(self, data): if not IEntity.providedBy(data): # pylint: disable=E1101 prx = DataElementAttributeProxy(data) try: new_owner = prx.owner except AttributeError: pass else: if new_owner != self.owner: # current_owner = None if self.owner == '' else self.owner self.__process_change_owner(new_owner) try: new_delivery_date = prx.delivery_date except AttributeError: pass else: self.delivery_date = new_delivery_date try: jobs = prx.jobs except AttributeError: pass else: self.__process_iso_jobs(jobs) try: isos = prx.isos except AttributeError: pass else: self.__process_isos(isos) def create_xl20_worklist(self, entity, rack_barcodes, optimizer_excluded_racks=None, optimizer_requested_tubes=None, include_dummy_output=False): assembler = lab.get_stock_rack_assembler( entity=entity, rack_barcodes=rack_barcodes, excluded_racks=optimizer_excluded_racks, requested_tubes=optimizer_requested_tubes, include_dummy_output=include_dummy_output) return run_tool(assembler) def create_pipetting_worklist(self): writer = lab.get_worklist_writer(self.get_entity()) return run_tool(writer) def __process_change_owner(self, new_owner): trac_tool = None if new_owner is None: # Reassign to requester for editing the experiment # metadata. trac_tool = IsoRequestTicketReassigner( iso_request=self.get_entity(), completed=False) new_owner = '' elif new_owner == self.requester.directory_user_id: # Close iso request and reassign to requester. trac_tool = IsoRequestTicketReassigner( iso_request=self.get_entity(), completed=True) elif new_owner == STOCKMANAGEMENT_USER: if self.owner == '': # Activate this ISO request for the first time. trac_tool = IsoRequestTicketAccepter( iso_request=self.get_entity(), username=new_owner) else: user_id = get_current_user().directory_user_id new_owner = user_id + ", " + STOCKMANAGEMENT_USER trac_tool = IsoRequestTicketReopener( iso_request=self.get_entity(), username=user_id) else: # Accept this ISO request. tkt_user = new_owner.split(',')[0] trac_tool = IsoRequestTicketAccepter(iso_request=self.get_entity(), username=tkt_user) if not trac_tool is None: run_trac_tool(trac_tool) self.owner = new_owner def __process_iso_jobs(self, iso_jobs_prx): for iso_job_prx in iso_jobs_prx: status = iso_job_prx.status iso_job_id = iso_job_prx.id iso_job = self.__find_iso_job(iso_job_id) if status.startswith('UPDATE_STOCK_RACKS'): self.__update_stock_racks(iso_job, status) elif status == 'PIPETTING': # Transfer from job stock racks. self.__pipetting_iso_or_iso_job(iso_job) else: raise ValueError('Unknown ISO job status "%s".' % status) def __process_isos(self, isos_prx): number_of_new_isos = 0 optimizer_excluded_racks = None optimizer_requested_tubes = None requested_library_plates = None for iso_prx in isos_prx: status = iso_prx.status iso_id = getattr(iso_prx, 'id', None) if status == 'NEW': number_of_new_isos += 1 optimizer_excluded_racks = \ getattr(iso_prx, 'optimizer_excluded_racks', None) optimizer_requested_tubes = \ getattr(iso_prx, 'optimizer_requested_tubes', None) requested_library_plates = \ getattr(iso_prx, 'requested_library_plates', None) else: # Retrieve the ISO entity and perform an operation on it. iso = self.__find_iso(iso_id) if status.startswith('UPDATE_STOCK_RACKS'): self.__update_stock_racks(iso, status) elif status == 'PIPETTING': self.__pipetting_iso_or_iso_job(iso) elif status == 'CLOSE_ISO': self.__update_iso_status(iso, ISO_STATUS.DONE) elif status == 'CANCEL_ISO': self.__update_iso_status(iso, ISO_STATUS.CANCELLED) elif status == 'REOPEN_ISO': self.__update_iso_status(iso, ISO_STATUS.REOPENED) elif status == 'COPY_ISO': self.__copy_iso(iso) else: raise ValueError('Unknown ISO status "%s".' % status) if number_of_new_isos > 0: self.__generate_isos(number_of_new_isos, optimizer_excluded_racks, optimizer_requested_tubes, requested_library_plates) def __update_iso_status(self, iso, new_status): iso.status = new_status if new_status == ISO_STATUS.CANCELLED \ and iso.status != ISO_STATUS.DONE: # Release the reserved library plates again. while iso.library_plates: lp = iso.library_plates.pop() lp.has_been_used = False def __copy_iso(self, iso): # FIXME: We need to figure out what to do her (#563). raise NotImplementedError('Not implemented.') # future = get_item_status_future() # new_iso = LabIso(iso.label + '_copy', # iso.number_stock_racks, # iso.rack_layout, # iso_request=iso.iso_request, # molecule_design_pool_set=\ # iso.molecule_design_pool_set, # optimizer_excluded_racks= # iso.optimizer_excluded_racks, # optimizer_requested_tubes= # iso.optimizer_requested_tubes, # ) # prep_label = 'p_%s' % (new_iso.label) # prep_plate = iso.preparation_plate.specs.create_rack( # label=prep_label, # status=future) # # FIXME: Using side effect of instantiation. # IsoPreparationPlate(iso=new_iso, plate=prep_plate) # new_isos = [new_iso] # job_num = len(iso.iso_request.iso_jobs) + 1 # # FIXME: Using side effect of instantiation. # IsoJob(label='ISO Job %d' % job_num, user=get_current_user(), # isos=new_isos) def __pipetting_iso_or_iso_job(self, iso_or_iso_job): user = get_current_user() executor = get_worklist_executor(iso_or_iso_job, user) result = run_tool(executor, error_prefix='Errors during pipetting. --') trac_updater = LabIsoStockTransferReporter(executor=executor) run_trac_tool(trac_updater) return result def __update_stock_racks(self, iso_or_iso_job, status): stock_rack_barcodes = status[len('UPDATE_STOCK_RACKS'):].split(';') recycler = get_stock_rack_recyler(iso_or_iso_job, stock_rack_barcodes) return run_tool(recycler, error_prefix='Invalid stock rack(s)! --') def __generate_isos(self, number_of_new_isos, optimizer_excluded_racks, optimizer_requested_tubes, requested_library_plates): if not optimizer_excluded_racks is None: optimizer_excluded_racks = optimizer_excluded_racks.split(',') if not optimizer_requested_tubes is None: optimizer_requested_tubes = optimizer_requested_tubes.split(',') if not requested_library_plates is None: requested_library_plates = requested_library_plates.split(',') iso_request = self.get_entity() user = get_current_user() if iso_request.iso_type == ISO_TYPES.LAB: creator = \ get_job_creator(iso_request, user, number_of_new_isos, excluded_racks=optimizer_excluded_racks, requested_tubes=optimizer_requested_tubes, requested_library_plates= requested_library_plates) else: raise NotImplementedError('POOL CREATION ISOs not implemented.') return run_tool(creator) def __find_iso(self, iso_id): # Enforce exactly one matching ISO. result, = [iso for iso in self.get_entity().isos if iso.id == iso_id] return result def __find_iso_job(self, iso_job_id): # Enforce exactly one matching ISO. result, = [ iso_job for iso_job in self.get_entity().iso_jobs if iso_job.id == iso_job_id ] return result def __get_completed_iso_plates_for_iso(self, iso): if self.experiment_metadata.experiment_metadata_type.id == \ EXPERIMENT_METADATA_TYPES.LIBRARY: racks = [lp.rack for lp in iso.library_plates] else: # If we have aliquot plates, use them; if not, use the # preparation plates. if len(iso.aliquot_plates) > 0: racks = [ap for ap in iso.aliquot_plates] else: racks = [pp for pp in iso.preparation_plates] return racks
class ExperimentMetadataMember(Member): relation = '%s/experiment-metadata' % RELATION_BASE_URL label = terminal_attribute(str, 'label') title = attribute_alias('label') ticket_number = terminal_attribute(int, 'ticket_number') subproject = member_attribute(ISubproject, 'subproject') number_replicates = terminal_attribute(int, 'number_replicates') molecule_design_pool_set = member_attribute(IMoleculeDesignPoolSet, 'molecule_design_pool_set') experiment_design = member_attribute(IExperimentDesign, 'experiment_design') iso_request = member_attribute(ILabIsoRequest, 'lab_iso_request') creation_date = terminal_attribute(datetime, 'creation_date') experiment_metadata_type = member_attribute(IExperimentMetadataType, 'experiment_metadata_type') def __getitem__(self, name): if name == 'tags': tags_dict = {} design_racks = self.__get_design_racks() for rack in design_racks: for tp in rack.rack_layout.tagged_rack_position_sets: for tag in tp.tags: tags_dict[tag.get_entity().slug] = tag tag_coll = get_root_collection(ITag) tag_coll.filter = cntd(id=[tag.id for tag in tags_dict.values()]) result = tag_coll elif name == 'experiment-design-racks': result = self.__get_design_racks() else: result = Member.__getitem__(self, name) return result @classmethod def create_from_entity(cls, entity): if entity.ticket_number is None: # Create a new ticket and attach the ticket number. user = get_current_user() ticket_creator = \ IsoRequestTicketCreator(requester=user, experiment_metadata=entity) entity.ticket_number = \ cls.__run_trac_tool(ticket_creator, 'Could not update the ticket: %s.') return cls(entity) def update(self, data): if IDataElement.providedBy(data): # pylint: disable=E1101 # FIXME: This really should be a PATCH operation. prx = DataElementAttributeProxy(data) self_entity = self.get_entity() changed_num_reps = prx.number_replicates != self.number_replicates emt_id = prx.experiment_metadata_type.get('id') changed_em_type = emt_id != self.experiment_metadata_type.id if changed_em_type or changed_num_reps: if changed_num_reps: self_entity.number_replicates = prx.number_replicates if changed_em_type: self_entity.experiment_metadata_type = \ get_experiment_metadata_type(emt_id) if not self_entity.experiment_design is None: # invalidate data to force a fresh upload of the XLS file self_entity.experiment_design.experiment_design_racks = [] self_entity.experiment_design.worklist_series = None if not self_entity.lab_iso_request is None: shape = self_entity.lab_iso_request.rack_layout.shape new_layout = RackLayout(shape=shape) self_entity.lab_iso_request.rack_layout = new_layout self_entity.lab_iso_request.owner = '' changed_sp = self_entity.subproject.id != prx.subproject.get('id') if changed_sp: new_sp = \ url_to_resource(prx.subproject.get('href')).get_entity() self_entity.subproject = new_sp self_entity.label = prx.label # Perform appropriate Trac updates. if not self_entity.lab_iso_request is None: if self.iso_request.owner == STOCKMANAGEMENT_USER: ticket_activator = IsoRequestTicketActivator( experiment_metadata=self_entity) self.__run_trac_tool(ticket_activator, 'Could not update the ticket: %s.') else: if changed_em_type or changed_num_reps: trac_updater = IsoRequestTicketDescriptionRemover( experiment_metadata=self_entity, changed_num_replicates=changed_num_reps, changed_em_type=changed_em_type) else: url = 'http://thelma/public//LOUICe.html#' \ + self.path iso_url = 'http://thelma/public//LOUICe.html#' \ + self.iso_request.path trac_updater = IsoRequestTicketDescriptionUpdater( experiment_metadata=self_entity, experiment_metadata_link=url, iso_request_link=iso_url) self.__run_trac_tool(trac_updater, 'Could not update the ticket: %s.') else: Member.update(self, data) @classmethod def __run_trac_tool(cls, tool, error_msg_text): tool.run() if not tool.transaction_completed(): exc_msg = str(tool.get_messages(logging_level=logging.ERROR)) raise HTTPBadRequest(error_msg_text % exc_msg).exception return tool.return_value def __get_design_racks(self): if self.experiment_design is not None: design_racks = self.experiment_design.experiment_design_racks else: # order only type design_racks = [] return design_racks
def set_up(self): self.member_attr = member_attribute(Member, 'attr') self.terminal_attr = terminal_attribute(int, 'attr')
class PlateSpecsMember(RackSpecsMember): relation = "%s/plate-specs" % RELATION_BASE_URL well_specs = member_attribute(IWellSpecs, 'well_specs')
class PlannedSampleDilutionMember(PlannedLiquidTransferMember): relation = '%s/planned_sample_dilution' % RELATION_BASE_URL diluent_info = terminal_attribute(str, 'diluent_info') target_position = member_attribute(IRackPosition, 'target_position')
class ReservoirSpecsMember(Member): relation = '%s/reservoirspecs' % RELATION_BASE_URL name = terminal_attribute(str, 'name') rack_shape = member_attribute(IRackShape, 'rack_shape') max_volume = terminal_attribute(float, 'max_volume') dead_volume = terminal_attribute(float, 'dead_volume')
def test_is_terminal_attribute(self, attr_name): mb_attr = member_attribute(Member, attr_name) assert is_terminal_attribute(mb_attr) is False t_attr = terminal_attribute(int, attr_name) assert is_terminal_attribute(t_attr) is True
def test_get_attribute_cardinality(self, attr_name): mb_attr = member_attribute(Member, attr_name) assert get_attribute_cardinality(mb_attr) == CARDINALITY_CONSTANTS.ONE t_attr = terminal_attribute(int, attr_name) with pytest.raises(ValueError): get_attribute_cardinality(t_attr)
def test_entity_backref(self): attr = member_attribute(IMyEntityParent, entity_attr='foo') self.assert_is_none(attr.resource_backref) self.assert_is_none(attr.entity_backref)
class LibraryPlateMember(Member): relation = "%s/library-plate" % RELATION_BASE_URL rack = member_attribute(IRack, 'rack') layout_number = terminal_attribute(int, 'layout_number') has_been_used = terminal_attribute(bool, 'has_been_used') lab_iso = member_attribute(ILabIso, 'lab_iso')
class ProjectMember(Member): relation = 'http://plantscribe.org/relations/project' title = attribute_alias('name') name = terminal_attribute(str, 'name') customer = member_attribute(ICustomer, 'customer') sites = collection_attribute(ISite, backref='project', is_nested=True)
class SupplierMoleculeDesignMember(Member): relation = "%s/supplier-molecule-design" % RELATION_BASE_URL product_id = terminal_attribute(str, 'product_id') supplier = member_attribute(IOrganization, 'supplier') is_current = terminal_attribute(bool, 'is_current')
class ExperimentRackMember(Member): relation = '%s/experiment-rack' % RELATION_BASE_URL experiment = member_attribute(IExperiment, 'experiment') design_rack = member_attribute(IExperimentDesignRack, 'design_rack') plate = member_attribute(IPlate, 'rack') source_rack = member_attribute(IRack, 'source_rack')
class SingleSupplierMoleculeDesignMember(SupplierMoleculeDesignMember): relation = "%s/single-supplier-molecule-design" % RELATION_BASE_URL molecule_design = member_attribute(IMoleculeDesign, 'molecule_design')
class StockSampleMember(SampleMember): relation = "%s/stocksample" % RELATION_BASE_URL supplier = member_attribute(IOrganization, 'supplier') molecule_type = member_attribute(IMoleculeType, 'molecule_type') concentration = terminal_attribute(float, 'concentration') product_id = terminal_attribute(str, 'product_id')
class PooledSupplierMoleculeDesignMember(SupplierMoleculeDesignMember): relation = "%s/pooled-supplier-molecule-design" % RELATION_BASE_URL molecule_design_pool = member_attribute(IMoleculeDesignPool, 'molecule_design_pool')
class PlannedSampleTransferMember(PlannedLiquidTransferMember): relation = '%s/planned_sample_transfer' % RELATION_BASE_URL source_position = member_attribute(IRackPosition, 'source_position') target_position = member_attribute(IRackPosition, 'target_position')