Ejemplo n.º 1
0
 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."
             )
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
    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()
Ejemplo n.º 5
0
    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))
Ejemplo n.º 6
0
        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()
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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