Exemplo n.º 1
0
    def testFindTwoMatchingInSameSubject(self):
        """
        Two matching hashes in the subject must be found correctly.
        """
        sequence = 'FRRRFRRRFASAASA'
        subject = AARead('subject', sequence)
        query = AARead('query', sequence)
        dbParams = DatabaseParameters(landmarks=[AlphaHelix],
                                      trigPoints=[Peaks])
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual(
            {
                '0': [{
                    'queryLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'queryTrigPoint': TrigPoint('Peaks', 'P', 10),
                    'subjectLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'subjectTrigPoint': TrigPoint('Peaks', 'P', 10),
                },
                    {
                    'queryLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'queryTrigPoint': TrigPoint('Peaks', 'P', 13),
                    'subjectLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'subjectTrigPoint': TrigPoint('Peaks', 'P', 13),
                }]
            }, matches)
        self.assertEqual(2, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 2
0
    def testFindWithIdenticalNonMatchingHashes(self):
        """
        Identical non-matching hashes must be found correctly when
        storeFullAnalysis is passed to find() as True.
        """
        subject = AARead('subject', 'F')
        query = AARead('query', 'AFRRRFRRRFASAAAAAAAAAAAFRRRFRRRFASA')
        dbParams = DatabaseParameters(landmarks=[AlphaHelix, BetaStrand],
                                      trigPoints=[Peaks], maxDistance=10)
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query, True)

        self.assertEqual({}, matches)
        self.assertEqual(2, hashCount)
        self.assertEqual(
            {
                'A2:P:10': [
                    [Landmark(AlphaHelix.NAME, AlphaHelix.SYMBOL, 1, 9, 2),
                     TrigPoint(Peaks.NAME, Peaks.SYMBOL, 11)],
                    [Landmark(AlphaHelix.NAME, AlphaHelix.SYMBOL, 23, 9, 2),
                     TrigPoint(Peaks.NAME, Peaks.SYMBOL, 33)]
                ],
            },
            nonMatchingHashes)
Exemplo n.º 3
0
    def testFindOneMatchingHashInTwoLocations(self):
        """
        One matching subject with one matching hash (that occurs in two
        locations) must be found correctly.
        """
        subject = AARead('subject', 'AFRRRFRRRFASAASAVVVVVVASAVVVASA')
        query = AARead('query', 'FRRRFRRRFASAASAFRRRFRRRFFRRRFRRRFFRRRFRRRF')
        dbParams = DatabaseParameters(landmarks=[AlphaHelix, BetaStrand],
                                      trigPoints=[Peaks])
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual(
            {
                '0': [{
                    'queryLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'queryTrigPoint': TrigPoint('Peaks', 'P', 10),
                    'subjectLandmark': Landmark('AlphaHelix', 'A', 1, 9, 2),
                    'subjectTrigPoint': TrigPoint('Peaks', 'P', 11),
                }, {
                    'queryLandmark': Landmark('AlphaHelix', 'A', 0, 9, 2),
                    'queryTrigPoint': TrigPoint('Peaks', 'P', 13),
                    'subjectLandmark': Landmark('AlphaHelix', 'A', 1, 9, 2),
                    'subjectTrigPoint': TrigPoint('Peaks', 'P', 14),
                }]
            }, matches)

        self.assertEqual(14, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 4
0
    def testFindOneMatchingHashInOneLocation(self):
        """
        One matching subject with one matching hash (that occurs in one
        location) must be found correctly.
        """
        sequence = 'AFRRRFRRRFASAASA'
        subject = AARead('subject', sequence)
        query = AARead('query', sequence)
        dbParams = DatabaseParameters(landmarks=[AlphaHelix],
                                      trigPoints=[Peaks], maxDistance=11)
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual(
            {
                '0': [{
                    'queryLandmark': Landmark('AlphaHelix', 'A', 1, 9, 2),
                    'queryTrigPoint': TrigPoint('Peaks', 'P', 11),
                    'subjectLandmark': Landmark('AlphaHelix', 'A', 1, 9, 2),
                    'subjectTrigPoint': TrigPoint('Peaks', 'P', 11),
                }]
            },
            matches)
        self.assertEqual(1, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 5
0
    def testFindNoMatch(self):
        """
        A query against an empty backend must produce no results.
        """
        subject = AARead('subject', 'FRRRFRRRFASAASA')
        query = AARead('query', 'FRRR')
        dbParams = DatabaseParameters(landmarks=[AlphaHelix],
                                      trigPoints=[Peaks])
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual({}, matches)
        self.assertEqual(0, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 6
0
    def testFindNoneMatchingNoTrigPoint(self):
        """
        No matches should be found if there is only one landmark and there are
        no trig point finders.
        """
        sequence = 'AFRRRFRRRFASAASA'
        subject = AARead('subject', sequence)
        query = AARead('query', sequence)
        dbParams = DatabaseParameters(landmarks=[AlphaHelix], trigPoints=[])
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual({}, matches)
        self.assertEqual(0, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 7
0
    def testFindNoneMatchingTooSmallDistance(self):
        """
        No matches should be found if the max distance is too small.
        """
        sequence = 'AFRRRFRRRFASAASA'
        subject = AARead('subject', sequence)
        query = AARead('query', sequence)
        dbParams = DatabaseParameters(landmarks=[AlphaHelix],
                                      trigPoints=[Peaks], maxDistance=1)
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query)

        self.assertEqual({}, matches)
        self.assertEqual(0, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 8
0
    def testFindOneMatchingHashButSubjectExcluded(self):
        """
        One matching subject with one matching hash (that occurs in one
        location) must not be returned if a subjectIndices argument that
        excludes it is passed to find.
        """
        sequence = 'AFRRRFRRRFASAASA'
        subject = AARead('subject', sequence)
        query = AARead('query', sequence)
        dbParams = DatabaseParameters(landmarks=[AlphaHelix],
                                      trigPoints=[Peaks], maxDistance=11)
        be = Backend()
        be.configure(dbParams)
        be.addSubject(subject, '0')
        matches, hashCount, nonMatchingHashes = be.find(query,
                                                        subjectIndices=set())

        self.assertEqual({}, matches)
        self.assertEqual(1, hashCount)
        self.assertEqual({}, nonMatchingHashes)
Exemplo n.º 9
0
class SimpleConnector(object):
    """
    Provide a simple in-memory connection between a Database and a single
    Backend.

    @param dbParams: A C{DatabaseParameters} instance.
    @param backend: A C{Backend} instance, or C{None} if a backend should be
        created.
    @param filePrefix: Either a C{str} file name prefix to use as a default
        when saving or C{None} if no default save file is needed.
    """

    SAVE_SUFFIX = '.lmco'

    def __init__(self, dbParams, backend=None, filePrefix=None):
        self.dbParams = dbParams
        if backend:
            self._backend = backend
        else:
            self._backend = Backend(filePrefix=filePrefix)
            self._backend.configure(dbParams)
        self._filePrefix = filePrefix

        # Most of our implementation comes directly from our backend.
        for method in ('addSubject', 'getIndexBySubject', 'getSubjectByIndex',
                       'getSubjects', 'subjectCount', 'hashCount',
                       'totalResidues', 'totalCoveredResidues', 'checksum'):
            setattr(self, method, getattr(self._backend, method))

    def find(self,
             read,
             findParams=None,
             storeFullAnalysis=False,
             subjectIndices=None):
        """
        Check which database sequences a read matches.

        @param read: A C{dark.read.AARead} instance.
        @param findParams: An instance of C{light.parameters.FindParameters} or
            C{None} to use default find parameters.
        @param storeFullAnalysis: A C{bool}. If C{True} the intermediate
            significance analysis computed in the Result will be stored.
        @param subjectIndices: A C{set} of subject indices, or C{None}. If a
            set is passed, only subject indices in the set will be returned
            in the results. If C{None}, all matching subject indices are
            returned.
        @return: A C{light.result.Result} instance.
        """
        matches, hashCount, nonMatchingHashes = self._backend.find(
            read,
            storeFullAnalysis=storeFullAnalysis,
            subjectIndices=subjectIndices)

        return Result(read,
                      self,
                      matches,
                      hashCount,
                      findParams,
                      nonMatchingHashes=nonMatchingHashes,
                      storeFullAnalysis=storeFullAnalysis)

    def shutdown(self, save, filePrefix):
        """
        Shut down the connector.

        @param save: If C{True}, save the connector state.
        @param filePrefix: When saving, use this C{str} as a file name prefix.
        """
        self._backend.shutdown(save, filePrefix)

        if save:
            self.save(filePrefix)

    def save(self, fpOrFilePrefix=None):
        """
        Save state to a file.

        @param fpOrFilePrefix: A file pointer, or the C{str} prefix of a file
            name, or C{None}. If a C{str}, self.SAVE_SUFFIX is appended to get
            the full file name. If C{None}, self._filePrefix will be used as a
            file prefix unless it is also C{None}.
        @raises ValueError: If C{fpOrFilePrefix} and C{self._filePrefix} are
            both C{None}
        """
        if isinstance(fpOrFilePrefix, str):
            saveFile = fpOrFilePrefix + self.SAVE_SUFFIX
        elif fpOrFilePrefix is None:
            if self._filePrefix is None:
                raise ValueError('save must be given an argument (or the '
                                 'database must have been restored from a '
                                 'file).')
            else:
                saveFile = self._filePrefix + self.SAVE_SUFFIX
        else:
            saveFile = fpOrFilePrefix

        with as_handle(saveFile, 'w') as fp:
            self.dbParams.save(fp)

        self._backend.save(fpOrFilePrefix)

    @classmethod
    def restore(cls, fpOrFilePrefix):
        """
        Restore state from a file.

        @param fpOrFilePrefix: A file pointer or the C{str} prefix of a file
            name. If a C{str}, self.SAVE_SUFFIX is appended to get the full
            file name.
        @return: An instance of L{SimpleConnector}.
        @raises ValueError: If valid JSON cannot be loaded from C{fp}.
        """
        if isinstance(fpOrFilePrefix, str):
            saveFile = fpOrFilePrefix + cls.SAVE_SUFFIX
            filePrefix = fpOrFilePrefix
        else:
            saveFile = fpOrFilePrefix
            filePrefix = None

        with as_handle(saveFile) as fp:
            dbParams = DatabaseParameters.restore(fp)

        return cls(dbParams,
                   backend=Backend.restore(fpOrFilePrefix),
                   filePrefix=filePrefix)

    def print_(self, printHashes=False, margin='', result=None):
        """
        Print information about this connector.

        @param printHashes: If C{True}, print all hashes and associated
            subjects from the backend.
        @param margin: A C{str} that should be inserted at the start of each
            line of output.
        @param result: A C{MultilineString} instance, or C{None} if a new
            C{MultilineString} should be created.
        @return: If C{result} was C{None}, return a C{str} representation of
            the connector, else C{None}.
        """
        if result is None:
            result = MultilineString(margin=margin)
            returnNone = False
        else:
            returnNone = True

        if printHashes:
            result.append('Backends:')
            result.indent()
            self._backend.print_(margin=margin, result=result)
            result.outdent()

        if not returnNone:
            return str(result)
Exemplo n.º 10
0
class BackendComponent(Component):

    NAME = 'backend'

    @asyncio.coroutine
    def onJoin(self, details):
        self._sessionId = details.session
        logging.info('Joined with WAMP server, session id %s', self._sessionId)
        self._ApiConfigured = False
        args = self.config.extra['args']

        if args.filePrefix:
            saveFile = args.filePrefix + Backend.SAVE_SUFFIX
            if os.path.exists(saveFile):
                self._backend = Backend.restore(saveFile)
        else:
            # Note that the backend will be configured by the WAMP connector
            # via a call to our configure method (below).
            self._backend = Backend()

        @asyncio.coroutine
        def configure(paramsStr, suggestedName, suggestedChecksum):
            """
            Configure our backend and register its API methods.

            @param paramsStr: The C{str} 'save' of a C{DatabaseParameters}
                instance.
            @param suggestedName: The C{str} suggested name for this backend.
                If the backend has already been configured (from a file
                restore) with a different name, the suggested name is ignored.
            @param suggestedChecksum: The C{int} suggested checksum for the
                backend, or C{None} if there is no initial value.
            @return: A 2-tuple consisting of the C{str} name of the backend and
                its checksum. These will either be the suggested values or
                those that were already in use (if the backend was already
                configured).
            """
            fp = StringIO(paramsStr)
            dbParams = DatabaseParameters.restore(fp)
            name, checksum, subjectCount = self._backend.configure(
                dbParams, suggestedName, suggestedChecksum)

            if not self._ApiConfigured:
                self._ApiConfigured = True
                yield from self.registerAPIMethods()

            return name, checksum, subjectCount

        # Register our configure command.
        yield from self.register(configure, 'configure-%s' % self._sessionId)
        logging.info('Registered configure method.')

        # Register our shutdown command.
        def shutdown(save, filePrefix):
            """
            Shut down the backend.

            @param save: If C{True}, save the backend state.
            @param filePrefix: When saving, use this C{str} as a file name
                prefix.
            """
            logging.info('Shutdown called.')
            self._backend.shutdown(save, filePrefix)
            self.leave('goodbye!')

        yield from self.register(shutdown, 'shutdown-%s' % self._sessionId)
        logging.info('Registered shutdown method.')

    @asyncio.coroutine
    def registerAPIMethods(self):
        """
        Register our API methods with the router.
        """
        yield from self.register(self.find, 'find-%s' % self._sessionId)
        logging.info('Registered find method.')

        # Most of our implementation comes directly from our backend.
        for method in ('addSubject', 'getIndexBySubject', 'getSubjectByIndex',
                       'getSubjects', 'subjectCount', 'hashCount',
                       'totalResidues', 'totalCoveredResidues', 'checksum'):
            yield from self.register(getattr(self._backend, method),
                                     '%s-%s' % (method, self._sessionId))
            logging.info('Registered %s method.', method)

    def find(self, read, storeFullAnalysis=False, subjectIndices=None):
        """
        Check which database sequences a read matches.

        @param read: A C{dark.read.AARead} instance.
        @param storeFullAnalysis: A C{bool}. If C{True} the intermediate
            significance analysis computed in the Result will be stored.
        @param subjectIndices: A C{set} of subject indices, or C{None}. If a
            set is passed, only subject indices in the set will be returned
            in the results. If C{None}, all matching subject indices are
            returned.
        @return: The result of calling 'find' on our backend.
        """
        return self._backend.find(read, storeFullAnalysis, subjectIndices)