def get_seq_id_from_link(cls, share_link: str): parsed = cls._parseURL(share_link) seq_id = parsed.get("seq_id", None) if seq_id: return seq_id else: try: text = cls._opensharelink(share_link) search_pattern = r"seq_\w+" possible_ids = re.findall(search_pattern, text) if len(possible_ids) == 0: raise BenchlingAPIException( "No sequence ids found in sharelink html using search pattern {}".format( search_pattern ) ) uniq_ids = list(set(possible_ids)) if len(uniq_ids) > 1: raise BenchlingAPIException( "More than one possible sequence id found in sharelink html using" " search " "pattern {}".format(search_pattern) ) seq_id = uniq_ids[0] return seq_id except (BenchlingAPIException, urllib.error.HTTPError): raise BenchlingAPIException( "Could not find seqid in sharelink body or url." )
def from_share_link(cls, share_link): """Return a DNASequence based on its weblink. Weblink may be a share link or a url """ try: text = cls._opensharelink(share_link) search_pattern = r"seq_\w+" possible_ids = re.findall(search_pattern, text) if len(possible_ids) == 0: raise BenchlingAPIException( "No sequence ids found in sharelink html using search pattern {}".format( search_pattern ) ) uniq_ids = list(set(possible_ids)) if len(uniq_ids) > 1: raise BenchlingAPIException( "More than one possible sequence id found in sharelink html using" " search " "pattern {}".format(search_pattern) ) seq = uniq_ids[0] except (BenchlingAPIException, urllib.error.HTTPError): d = cls._parseURL(share_link) seq = d["seq_id"] if seq is None: raise BenchlingAPIException( "Could not find seqid in sharelink body or url." ) return cls.get(seq)
def update(self) -> ModelBase: """Update the model instance.""" if not self.id: raise BenchlingAPIException( "Cannot update. Model has not yet been saved.") data = self.update_json() return self._update_from_data(data)
def set_schema(self, schema_name): """Set the schema for this model instance.""" schema = None valid_schemas = self.valid_schemas() if not valid_schemas: raise BenchlingAPIException("No valid schemas found.") for r, s in valid_schemas: if s["name"] == schema_name: schema = s if not schema: raise BenchlingAPIException( 'No schema "{}" found. Select from {}'.format( schema_name, [s["name"] for r, s in self.valid_schemas()])) self.new_registry_id = schema["registry_id"] self.schema_id = schema["id"] self.update()
def merge(self, on: dict = None) -> ModelBase: """ Provided with a list of fields to search, merge the model with an existing model on the server (if it exists), else creates a new model. If no models found using the fields, a new model is created. If more than one model found, an exception is raised. If one model is found, that model is updated with the data from this model. .. versionadded: 2.1.4 :param on: list of fields to search the server. :return: the model """ if hasattr(self, "DEFAULT_MERGE_FIELDS") and on is None: on = self.DEFAULT_MERGE_FIELDS if not on: raise BenchlingAPIException("Cannot merge. Must specify fields" " to merge with") params = {} for key in on: if hasattr(self, key): params[key] = getattr(self, key) else: raise BenchlingAPIException( "Cannot merge. Model is missing {} attribute".format(key)) models = self.list(**params) if len(models) == 1: self.id = models[0].id self.update() return self elif len(models) == 0: self.save() return self else: raise BenchlingAPIException( "Cannot merge. More than one" " {} found with merge fields {}".format( self.__class__.__name__, on))
def wrapped_f(*args, **kwargs): r = f(*args, **kwargs) if r.status_code not in self.code: http_codes = { 400: "BAD REQUEST", 403: "FORBIDDEN", 404: "NOT FOUND", 500: "INTERNAL SERVER ERROR", 503: "SERVICE UNAVAILABLE", 504: "SERVER TIMEOUT", } msg = "" if r.status_code in http_codes: msg = http_codes[r.status_code] msg += "\nrequest: {}".format(r.request) msg += "\nurl: {}".format(r.request.path_url) msg += "\nresponse: {}".format(r.text) e = BenchlingAPIException("HTTP Response Failed {} {}".format( r.status_code, msg)) e.response = r exception_dispatch(e) return r.json()
def register(self, naming_strategy=NAMING_STRATEGY.DEFAULT): """Register this instance. :param naming_strategy: naming strategy for registry. - NEW_IDS (default): Generates new registry IDs, and leaves entity name as-is - IDS_FROM_NAMES: Converts names into registry IDs - DELETE_NAMES: Generates new registry IDs and sets entity name to new registry ID, and does not keep old names as aliases - SET_FROM_NAME_PARTS: Generates new registry IDs and generates new entity name based on the entity schema's name template, if it has one. :return: model instance """ if not hasattr(self, "new_registry_id"): raise BenchlingAPIException( "No schema set. Please use '{}' to set a schema. You may use '{}' " "to look at available schemas.".format( self.set_schema.__name__, self.print_valid_schemas.__name__)) self.session.Registry.register(self.new_registry_id, [self.id], naming_strategy=naming_strategy) self.reload() return self
def create_consensus( cls, algorithm: str, name: str, template: Union[str, DNASequence], consensus_sequence: Union[str, DNASequence], filepaths: List[str] = None, sequences: List[Union[str, DNASequence]] = None, rawfiles: List[str] = None, ): """Create a consensus sequence and save the sequence to the Benchling Server. .. versionadded:: 2.1.3 Added `create_consensus` .. code-block:: python task = session.DNAAlignment.create_consensus( algorithm='mafft', name='my sequence alignment', filepaths=[ 'data/13dfg34.ab1' # filepath to ab1 files ], sequences=[ 'seq_1erv452', # a benchling sequence id session.DNASequence.one(), # ...or a DNASequence instance ], consensus_sequence=session.DNASequence( folder_id="lib_23gv4r2", name="my consensus", ) rawfiles=None # only use if you have base64 data handy ) # wait until the alignment is finished task.wait() # print the alignment print(task.response) :param algorithm: either 'mafft' or 'clustalo' :param name: name of the sequence alignment :param template: template of the alignment. Either a benchling sequence id (str) or a :class:`DNASequence` instance containing a id. :param consensus_sequence: either a DNASequence object (with folder_id) or a sequence id to save the consensus sequence. :param filepaths: optional list of sequencing filepaths to align to the template. For example, a list of .ab1 file paths. :param sequences: optional list of :class:`DNASequence` instances or benchling sequence ids to align to the template sequence. :param rawfiles: optional list of raw entries to send to align to the template. Use this if you have base64 bytes encoded data. Take a look at Benchling V2 API for more information. :return: Task """ post_data = { "name": name, "algorithm": algorithm, "templateSequenceId": cls.model_to_id(template), "files": cls._resolve_files(sequences, filepaths, rawfiles), } if isinstance(consensus_sequence, str): post_data["sequenceId"] = consensus_sequence else: post_data["newSequence"] = consensus_sequence.dump() if not post_data["files"]: raise BenchlingAPIException("No sequences or filepaths provided.") result = cls.session.http.post( "dna-alignments", action="create-consensus-alignment", json=post_data ) inst = cls.session.Task.find(result["taskId"]) return inst
def submit_alignment( cls, algorithm: str, name: str, template: Union[str, DNASequence], filepaths: List[str] = None, sequences: List[Union[str, DNASequence]] = None, rawfiles: List[str] = None, ) -> Task: """Submit an sequence alignment task. Usage: .. code-block:: python task = session.DNAAlignment.submit_alignment( algorithm='mafft', name='my sequence alignment', template='seq_yi2kdf2', # the sequence id of the template filepaths=[ 'data/13dfg34.ab1' # filepath to ab1 files ], sequences=[ 'seq_1erv452', # a benchling sequence id session.DNASequence.one(), # ...or a DNASequence instance ], rawfiles=None # only use if you have base64 data handy ) # wait until the alignment is finished task.wait() # print the alignment print(task.response) # or grab the alignment alignment = task.response_class # from there, you can delete the alignment alignment.delete() :param algorithm: either 'mafft' or 'clustalo' :param name: name of the sequence alignment :param template: template of the alignment. Either a benchling sequence id (str) or a :class:`DNASequence` instance containing a id. :param filepaths: optional list of sequencing filepaths to align to the template. For example, a list of .ab1 file paths. :param sequences: optional list of :class:`DNASequence` instances or benchling sequence ids to align to the template sequence. :param rawfiles: optional list of raw entries to send to align to the template. Use this if you have base64 bytes encoded data. Take a look at Benchling V2 API for more information. :return: Task """ post_data = { "name": name, "algorithm": algorithm, "templateSequenceId": cls.model_to_id(template), "files": cls._resolve_files(sequences, filepaths, rawfiles), } if not post_data["files"]: raise BenchlingAPIException("No sequences or filepaths provided.") result = cls.session.http.post( "dna-alignments", action="create-template-alignment", json=post_data ) inst = cls.session.Task.find(result["taskId"]) inst.expected_class = cls return inst