def _remote_survey_url_polyphenol_ffq(transaction, account_id, source_id,
                                      language_tag):
    st_repo = SurveyTemplateRepo(transaction)

    # right now, ID won't exist
    # future plans to allow surveys to behave more flexibly will use this
    # functionality to allow participants to re-join in-progress surveys
    polyphenol_ffq_id, study = \
        st_repo.get_polyphenol_ffq_id_if_exists(account_id, source_id)

    if polyphenol_ffq_id is None:
        # The Polyphenol FFQ belongs to Danone and they're interested in
        # tracking results that come from their sponsored studies
        # separately from other samples. We pass 'THDMI' as the study for
        # THDMI samples and 'Microsetta' for all other samples. Therefore,
        # we need to determine if the source has any THDMI-associated samples.
        # Without investing significant developer effort to build a category
        # system around projects, a basic text search is the best compromise.
        study = 'Microsetta'
        sample_repo = SampleRepo(transaction)
        samples = sample_repo.get_samples_by_source(account_id, source_id)
        for s in samples:
            for s_p in s.sample_projects:
                if s_p.startswith('THDMI'):
                    study = 'THDMI'
                    break

        polyphenol_ffq_id = st_repo.create_polyphenol_ffq_entry(
            account_id, source_id, language_tag, study)

    return polyphenol_ffq.gen_ffq_url(polyphenol_ffq_id, study, language_tag)
def update_sample_association(account_id, source_id, sample_id, body,
                              token_info):
    _validate_account_access(token_info, account_id)

    # TODO: API layer doesn't understand that BadRequest can be thrown,
    #  but that looks to be the right result if sample_site bad.
    #  Need to update the api layer if we want to specify 400s.
    #  (Or we leave api as is and say 400's can always be thrown if your
    #  request is bad)
    with Transaction() as t:
        sample_repo = SampleRepo(t)
        source_repo = SourceRepo(t)

        source = source_repo.get_source(account_id, source_id)
        if source is None:
            return jsonify(code=404, message="No such source"), 404

        needs_sample_site = source.source_type in [
            Source.SOURCE_TYPE_HUMAN, Source.SOURCE_TYPE_ANIMAL
        ]

        precludes_sample_site = source.source_type == \
            Source.SOURCE_TYPE_ENVIRONMENT

        sample_site_present = "sample_site" in body and \
                              body["sample_site"] is not None

        if needs_sample_site and not sample_site_present:
            # Human/Animal sources require sample_site to be set
            raise BadRequest("human/animal samples require sample_site")
        if precludes_sample_site and sample_site_present:
            raise BadRequest("environmental samples cannot specify "
                             "sample_site")

        sample_datetime = body['sample_datetime']
        try:
            sample_datetime = fromisotime(sample_datetime)
        except ValueError:
            raise BadRequest("Invalid sample_datetime")
        curdate = datetime.now(sample_datetime.tzinfo)
        lower_limit = curdate + relativedelta(years=-10)
        upper_limit = curdate + relativedelta(months=+1)
        if sample_datetime < lower_limit or sample_datetime > upper_limit:
            raise BadRequest('Invalid sample date')
        # sample_site will not be present if its environmental. this will
        # default to None if the key is not present
        sample_site = body.get('sample_site')
        sample_info = SampleInfo(sample_id, sample_datetime, sample_site,
                                 body["sample_notes"])

        is_admin = token_grants_admin_access(token_info)
        sample_repo.update_info(account_id,
                                source_id,
                                sample_info,
                                override_locked=is_admin)

        final_sample = sample_repo.get_sample(account_id, source_id, sample_id)
        t.commit()
    return jsonify(final_sample), 200
Exemple #3
0
    def test_scrub_bad_sample(self):
        samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401'  # 000001766
        with Transaction() as t:
            acct1, src1 = self._get_source_from_sample(t, samp1)
            sr = SampleRepo(t)

            with self.assertRaises(RepoException):
                sr.scrub(acct1, src1, src1)
Exemple #4
0
def dissociate_sample(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample_repo.dissociate_sample(account_id, source_id, sample_id)
        t.commit()
        return '', 204
def read_sample_associations(account_id, source_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        samples = sample_repo.get_samples_by_source(account_id, source_id)

    api_samples = [x.to_api() for x in samples]
    return jsonify(api_samples), 200
def read_sample_association(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample = sample_repo.get_sample(account_id, source_id, sample_id)
        if sample is None:
            return jsonify(code=404, message="Sample not found"), 404

        return jsonify(sample.to_api()), 200
def delete_account(account_id, token_info):
    validate_admin_access(token_info)

    with Transaction() as t:
        acct_repo = AccountRepo(t)
        src_repo = SourceRepo(t)
        samp_repo = SampleRepo(t)
        sar_repo = SurveyAnswersRepo(t)

        acct = acct_repo.get_account(account_id)
        if acct is None:
            return jsonify(message="Account not found", code=404), 404
        else:
            # the account is already scrubbed so let's stop early
            if acct.account_type == 'deleted':
                return None, 204

        sample_count = 0
        sources = src_repo.get_sources_in_account(account_id)

        for source in sources:
            samples = samp_repo.get_samples_by_source(account_id, source.id)

            has_samples = len(samples) > 0
            sample_count += len(samples)

            for sample in samples:
                # we scrub rather than disassociate in the event that the
                # sample is in our freezers but not with an up-to-date scan
                samp_repo.scrub(account_id, source.id, sample.id)

            surveys = sar_repo.list_answered_surveys(account_id, source.id)
            if has_samples:
                # if we have samples, we need to scrub survey / source
                # free text
                for survey_id in surveys:
                    sar_repo.scrub(account_id, source.id, survey_id)
                src_repo.scrub(account_id, source.id)
            else:
                # if we do not have associated samples, then the source
                # is safe to delete
                for survey_id in surveys:
                    sar_repo.delete_answered_survey(account_id, survey_id)
                src_repo.delete_source(account_id, source.id)

        # an account is safe to delete if there are no associated samples
        if sample_count > 0:
            acct_repo.scrub(account_id)
        else:
            acct_repo.delete_account(account_id)

        t.commit()

    return None, 204
Exemple #8
0
def associate_sample(account_id, source_id, body, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample_repo.associate_sample(account_id, source_id, body['sample_id'])
        t.commit()
    response = flask.Response()
    response.status_code = 201
    response.headers['Location'] = '/api/accounts/%s/sources/%s/samples/%s' % \
                                   (account_id, source_id, body['sample_id'])
    return response
def delete_dummy_accts():
    all_sample_ids = []
    acct_ids = [ACCT_ID_1, ACCT_ID_2]

    with Transaction() as t:
        acct_repo = AccountRepo(t)
        source_repo = SourceRepo(t)
        survey_answers_repo = SurveyAnswersRepo(t)
        sample_repo = SampleRepo(t)

        for curr_acct_id in acct_ids:
            sources = source_repo.get_sources_in_account(curr_acct_id)
            for curr_source in sources:
                source_samples = sample_repo.get_samples_by_source(
                    curr_acct_id, curr_source.id)
                sample_ids = [x.id for x in source_samples]
                all_sample_ids.extend(sample_ids)

                # Dissociate all samples linked to this source from all
                # answered surveys linked to this source, then delete all
                # answered surveys
                delete_dummy_answered_surveys_from_source_with_t(
                    t, curr_acct_id, curr_source.id, sample_ids,
                    survey_answers_repo)

                # Now dissociate all the samples from this source
                for curr_sample_id in sample_ids:
                    sample_repo.dissociate_sample(curr_acct_id, curr_source.id,
                                                  curr_sample_id)

                # Finally, delete the source
                source_repo.delete_source(curr_acct_id, curr_source.id)

            # Delete the account
            acct_repo.delete_account(curr_acct_id)

        # Belt and suspenders: these test emails are used by some tests outside
        # of this module as well, so can't be sure they are paired with the
        # above dummy account ids
        acct_repo.delete_account_by_email(TEST_EMAIL)
        acct_repo.delete_account_by_email(TEST_EMAIL_2)

        # Delete the kit and all samples that were attached to any sources
        # NB: This won't clean up any samples that were created but NOT
        # attached to any sources ...
        if len(all_sample_ids) == 0:
            all_sample_ids = None
        _remove_mock_kit(t, mock_sample_ids=all_sample_ids)

        t.commit()
Exemple #10
0
    def test_scrub(self):
        samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401'  # 000001766
        with Transaction() as t:
            acct1, src1 = self._get_source_from_sample(t, samp1)

            sr = SampleRepo(t)

            # get original source associations
            src1_sample = sr.get_samples_by_source(acct1, src1)[0]

            res = sr.scrub(acct1, src1, src1_sample.id)
            self.assertTrue(res)

            obs_sample = sr.get_samples_by_source(acct1, src1)[0]
            self.assertNotEqual(src1_sample.notes, obs_sample.notes)
def create_dummy_kit(account_id=None, source_id=None):
    with Transaction() as t:
        _create_mock_kit(t, barcodes=[BARCODE],
                         mock_sample_ids=[MOCK_SAMPLE_ID])

        # if an account and source were provided, put some dummy
        # collection info into the sample and associate it to this source
        if account_id is not None and source_id is not None:

            sample_info, _ = create_dummy_sample_objects(True)
            sample_repo = SampleRepo(t)
            sample_repo.associate_sample(account_id, source_id, MOCK_SAMPLE_ID)
            sample_repo.update_info(account_id, source_id, sample_info)

        t.commit()
    def dissociate_answered_survey_from_sample(self, account_id, source_id,
                                               sample_id, survey_id):
        sample_repo = SampleRepo(self._transaction)

        if not self._acct_source_owns_survey(account_id, source_id, survey_id):
            raise werkzeug.exceptions.NotFound("No survey ID: %s" % survey_id)

        s = sample_repo.get_sample(account_id, source_id, sample_id)

        if s is None:
            raise werkzeug.exceptions.NotFound("No sample ID: %s" % sample_id)

        with self._transaction.cursor() as cur:
            cur.execute(
                "DELETE FROM source_barcodes_surveys "
                "WHERE "
                "barcode = %s AND "
                "survey_id = %s", (s.barcode, survey_id))
            # Also delete from vioscreen, myfoodrepo and polyphenol registries
            cur.execute(
                "UPDATE vioscreen_registry "
                "SET deleted=true "
                "WHERE "
                "account_id = %s AND "
                "source_id = %s AND "
                "sample_id = %s AND "
                "vio_id = %s", (account_id, source_id, sample_id, survey_id))
            cur.execute(
                "UPDATE myfoodrepo_registry "
                "SET deleted=true "
                "WHERE "
                "account_id = %s AND "
                "source_id = %s AND "
                "myfoodrepo_id = %s", (account_id, source_id, survey_id))
            try:
                uuid.UUID(survey_id)
                cur.execute(
                    "UPDATE polyphenol_ffq_registry "
                    "SET deleted = true "
                    "WHERE "
                    "account_id = %s AND "
                    "source_id = %s AND "
                    "polyphenol_ffq_id = %s",
                    (account_id, source_id, survey_id))
            except ValueError:
                # Note: we don't care about the error, just means it's not
                # the Polyphenol FFQ
                pass
    def associate_answered_survey_with_sample(self, account_id, source_id,
                                              sample_id, survey_id):
        sample_repo = SampleRepo(self._transaction)

        if not self._acct_owns_survey(account_id, survey_id):
            raise werkzeug.exceptions.NotFound("No survey ID: %s" % survey_id)

        s = sample_repo.get_sample(account_id, source_id, sample_id)

        if s is None:
            raise werkzeug.exceptions.NotFound("No sample ID: %s" % sample_id)

        with self._transaction.cursor() as cur:
            cur.execute("INSERT INTO source_barcodes_surveys "
                        "(barcode, survey_id) "
                        "VALUES(%s, %s)", (s.barcode, survey_id))
 def get_kit_all_samples_by_kit_id(self, kit_id):
     sample_repo = SampleRepo(self._transaction)
     with self._transaction.cursor() as cur:
         cur.execute("SELECT "
                     "ag_kit.ag_kit_id, "
                     "ag_kit_barcodes.ag_kit_barcode_id "
                     "FROM ag_kit LEFT JOIN ag_kit_barcodes ON "
                     "ag_kit.ag_kit_id = ag_kit_barcodes.ag_kit_id "
                     "WHERE "
                     "ag_kit.ag_kit_id = %s",
                     (kit_id,))
         rows = cur.fetchall()
         if len(rows) == 0:
             return None
         else:
             samples = [sample_repo._get_sample_by_id(r[1]) for r in rows]
             return Kit(rows[0][0], samples)
    def dissociate_answered_survey_from_sample(self, account_id, source_id,
                                               sample_id, survey_id):
        sample_repo = SampleRepo(self._transaction)

        if not self._acct_source_owns_survey(account_id, source_id, survey_id):
            raise werkzeug.exceptions.NotFound("No survey ID: %s" % survey_id)

        s = sample_repo.get_sample(account_id, source_id, sample_id)

        if s is None:
            raise werkzeug.exceptions.NotFound("No sample ID: %s" % sample_id)

        with self._transaction.cursor() as cur:
            cur.execute(
                "DELETE FROM source_barcodes_surveys "
                "WHERE "
                "barcode = %s AND "
                "survey_id = %s", (s.barcode, survey_id))
Exemple #16
0
def dissociate_sample(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        answers_repo = SurveyAnswersRepo(t)
        answered_survey_ids = answers_repo.list_answered_surveys_by_sample(
            account_id, source_id, sample_id)

        for curr_answered_survey_id in answered_survey_ids:
            answers_repo.dissociate_answered_survey_from_sample(
                account_id, source_id, sample_id, curr_answered_survey_id)

        sample_repo = SampleRepo(t)
        sample_repo.dissociate_sample(account_id, source_id, sample_id)

        t.commit()

        return '', 204
Exemple #17
0
    def test_migrate_sample(self):
        samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401'  # 000001766
        samp2 = 'ceaa6fd6-0861-4335-aa35-da1857bd5294'  # 000067789

        with Transaction() as t:
            acct1, src1 = self._get_source_from_sample(t, samp1)
            acct2, src2 = self._get_source_from_sample(t, samp2)
            sr = SampleRepo(t)

            sr.migrate_sample(samp1, src1, src2, True)

            # get new samples by source
            src1_samples = sr.get_samples_by_source(acct1, src1)
            src2_samples = sr.get_samples_by_source(acct2, src2)

            # verify samples are part of the new source
            self.assertFalse(self._sample_in_source(src1_samples, samp1))
            self.assertTrue(self._sample_in_source(src2_samples, samp1))
    def test_dissociate_sample_from_source_success(self):
        dummy_acct_id, dummy_source_id = create_dummy_source(
            "Bo", Source.SOURCE_TYPE_HUMAN, DUMMY_HUMAN_SOURCE,
            create_dummy_1=True)
        create_dummy_kit(dummy_acct_id, dummy_source_id)
        dummy_answered_survey_id = create_dummy_answered_survey(
            dummy_acct_id, dummy_source_id, dummy_sample_id=MOCK_SAMPLE_ID)

        base_url = '/api/accounts/{0}/sources/{1}/samples'.format(
            dummy_acct_id, dummy_source_id)
        sample_url = "{0}/{1}".format(base_url, MOCK_SAMPLE_ID)
        delete_resp = self.client.delete(
            '%s?%s' % (sample_url, self.default_lang_querystring),
            headers=self.dummy_auth
        )

        # check response code
        self.assertEqual(204, delete_resp.status_code)

        # load the samples associated to this source
        get_response = self.client.get(
            '%s?%s' % (base_url, self.default_lang_querystring),
            headers=self.dummy_auth)

        # check response code
        self.assertEqual(200, get_response.status_code)

        # ensure there are zero samples associated with this source
        get_resp_obj = json.loads(get_response.data)
        self.assertEqual(get_resp_obj, [])

        # load the sample info
        _, expected_sample = create_dummy_sample_objects(False)
        with Transaction() as t:
            # make sure the sample's collection info is wiped
            sample_repo = SampleRepo(t)
            obs_sample = sample_repo._get_sample_by_id(MOCK_SAMPLE_ID)
            self.assertEqual(expected_sample.__dict__, obs_sample.__dict__)

            # make sure answered survey no longer associated with any samples
            answered_survey_repo = SurveyAnswersRepo(t)
            answered_survey_ids = answered_survey_repo.\
                _get_survey_sample_associations(dummy_answered_survey_id)
            self.assertEqual([], answered_survey_ids)
Exemple #19
0
    def test_update_sample_assocation_with_migration(self):
        samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401'  # 000001766
        samp2 = 'ceaa6fd6-0861-4335-aa35-da1857bd5294'  # 000067789

        with Transaction() as t:
            acct1, src1 = self._get_source_from_sample(t, samp1)
            acct2, src2 = self._get_source_from_sample(t, samp2)

            sr = SampleRepo(t)

            # get original source associations
            src1_samples = sr.get_samples_by_source(acct1, src1)
            src2_samples = sr.get_samples_by_source(acct2, src2)

            # verify samples are part of the original source
            self.assertTrue(self._sample_in_source(src1_samples, samp1))
            self.assertTrue(self._sample_in_source(src2_samples, samp2))

            # swap associations
            sr._update_sample_association(samp1, src2, True)
            sr._update_sample_association(samp2, src1, True)

            # get new samples by source
            src1_samples = sr.get_samples_by_source(acct1, src1)
            src2_samples = sr.get_samples_by_source(acct2, src2)

            # verify samples are part of the new source
            self.assertTrue(self._sample_in_source(src1_samples, samp2))
            self.assertTrue(self._sample_in_source(src2_samples, samp1))

            # verify samples are not part of the original source
            self.assertFalse(self._sample_in_source(src1_samples, samp1))
            self.assertFalse(self._sample_in_source(src2_samples, samp2))

            # fix associations
            sr._update_sample_association(samp1, src1, True)
            sr._update_sample_association(samp2, src2, True)

            # ...and verify assocations are fixed
            src1_samples = sr.get_samples_by_source(acct1, src1)
            src2_samples = sr.get_samples_by_source(acct2, src2)
            self.assertTrue(self._sample_in_source(src1_samples, samp1))
            self.assertTrue(self._sample_in_source(src2_samples, samp2))
    def get_kit_unused_samples(self, supplied_kit_id):
        sample_repo = SampleRepo(self._transaction)

        # Business Logic: We now define an unclaimed sample as a sample with
        # a null source_id in ag_kit_barcodes
        with self._transaction.cursor() as cur:
            cur.execute("SELECT "
                        "ag_kit.ag_kit_id, "
                        "ag_kit_barcodes.ag_kit_barcode_id "
                        "FROM ag_kit LEFT JOIN ag_kit_barcodes ON "
                        "ag_kit.ag_kit_id = ag_kit_barcodes.ag_kit_id "
                        "WHERE "
                        "ag_kit.supplied_kit_id = %s AND "
                        "ag_kit_barcodes.source_id is null",
                        (supplied_kit_id,))
            rows = cur.fetchall()
            if len(rows) == 0:
                return None
            else:
                samples = [sample_repo._get_sample_by_id(r[1]) for r in rows]
                return Kit(rows[0][0], samples)
Exemple #21
0
    def list_answered_surveys_by_sample(self, account_id, source_id,
                                        sample_id):
        sample_repo = SampleRepo(self._transaction)

        # Note: Retrieving sample in this way validates permissions.
        sample = sample_repo.get_sample(account_id, source_id, sample_id)
        if sample is None:
            raise werkzeug.exceptions.NotFound("No sample ID: %s" % sample.id)

        with self._transaction.cursor() as cur:
            cur.execute(
                "SELECT "
                "survey_id "
                "FROM "
                "ag_kit_barcodes "
                "LEFT JOIN source_barcodes_surveys "
                "USING (barcode)"
                "WHERE "
                "ag_kit_barcode_id = %s", (sample_id, ))
            rows = cur.fetchall()
            answered_surveys = [r[0] for r in rows if r[0] is not None]
        return answered_surveys
Exemple #22
0
    def test_migrate_sample_exceptions(self):
        samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401'  # 000001766
        samp2 = 'ceaa6fd6-0861-4335-aa35-da1857bd5294'  # 000067789
        bad = 'ffffffff-ffff-ffff-aaaa-aaaaaaaaaaaa'

        with Transaction() as t:
            _, src1 = self._get_source_from_sample(t, samp1)
            _, src2 = self._get_source_from_sample(t, samp2)
            sr = SampleRepo(t)

            with self.assertRaises(RepoException):
                # verify we dont do something unless we are intentional
                sr.migrate_sample(samp1, src1, src2, False)

            with self.assertRaises(RepoException):
                # the sample must be associated witht the source (src)
                # to move
                sr.migrate_sample(samp2, src1, src2, True)

            with self.assertRaises(RepoException):
                # the destination must exist
                sr.migrate_sample(samp1, src1, bad, True)
def read_sample_association(account_id, source_id, sample_id, token_info):
    _validate_account_access(token_info, account_id)

    with Transaction() as t:
        sample_repo = SampleRepo(t)
        sample = sample_repo.get_sample(account_id, source_id, sample_id)
        if sample is None:
            return jsonify(code=404, message="Sample not found"), 404

    qiita_body = {'sample_ids': ["10317." + str(sample.barcode)]}

    try:
        qiita_data = qclient.post('/api/v1/study/10317/samples/status',
                                  json=qiita_body)
        accession_urls = []
        for barcode_info in qiita_data:
            experiment_accession = barcode_info.get("ebi_experiment_accession")
            if experiment_accession is None:
                continue
            accession_urls.append("https://www.ebi.ac.uk/ena/browser/view/" +
                                  experiment_accession + "?show=reads")
        sample.set_accession_urls(accession_urls)
    except NotFoundError:
        # I guess qiita doesn't know about this barcode,
        # so probably no ebi accession info
        pass
    except BadRequestError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
    except ForbiddenError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
    except RuntimeError:
        # How do I log these to gunicorn??
        app.logger.warning("Couldn't communicate with qiita", exc_info=True)
        raise

    return jsonify(sample.to_api()),
    def associate_answered_survey_with_sample(self, account_id, source_id,
                                              sample_id, survey_id):
        sample_repo = SampleRepo(self._transaction)

        if not self._acct_owns_survey(account_id, survey_id):
            raise werkzeug.exceptions.NotFound("No survey ID: %s" % survey_id)

        s = sample_repo.get_sample(account_id, source_id, sample_id)

        if s is None:
            raise werkzeug.exceptions.NotFound("No sample ID: %s" % sample_id)

        # Switching to insert if not exists semantics since vioscreen IDs will
        # be associated with samples prior to being filled out.
        with self._transaction.cursor() as cur:
            cur.execute(
                "SELECT * FROM source_barcodes_surveys "
                "WHERE barcode=%s AND survey_id=%s", (s.barcode, survey_id))
            if cur.fetchone() is None:
                cur.execute(
                    "INSERT INTO source_barcodes_surveys "
                    "(barcode, survey_id) "
                    "VALUES(%s, %s)", (s.barcode, survey_id))
Exemple #25
0
    def get_survey_metadata(self, sample_barcode, survey_template_id=None):
        ids = self._get_ids_relevant_to_barcode(sample_barcode)

        if ids is None:
            raise NotFound("No such barcode")

        account_id = ids.get('account_id')
        source_id = ids.get('source_id')
        sample_id = ids.get('sample_id')

        account = None
        source = None
        sample = None
        if sample_id is not None:
            sample_repo = SampleRepo(self._transaction)
            sample = sample_repo._get_sample_by_id(sample_id)

        if source_id is not None and account_id is not None:
            source_repo = SourceRepo(self._transaction)
            account_repo = AccountRepo(self._transaction)
            account = account_repo.get_account(account_id)
            source = source_repo.get_source(account_id, source_id)

        if source is None:
            raise RepoException("Barcode is not associated with a source")

        # TODO: This is my best understanding of how the data must be
        #  transformed to get the host_subject_id, needs verification that it
        #  generates the expected values for preexisting samples.
        prehash = account_id + source.name.lower()
        host_subject_id = sha512(prehash.encode()).hexdigest()

        survey_answers_repo = SurveyAnswersRepo(self._transaction)
        answer_ids = survey_answers_repo.list_answered_surveys_by_sample(
            account_id, source_id, sample_id)

        answer_to_template_map = {}
        for answer_id in answer_ids:
            template_id = survey_answers_repo.find_survey_template_id(
                answer_id)
            answer_to_template_map[answer_id] = template_id

        # if a survey template is specified, filter the returned surveys
        if survey_template_id is not None:
            # TODO: This schema is so awkward for this type of query...
            answers = []
            for answer_id in answer_ids:
                if answer_to_template_map[answer_id] == survey_template_id:
                    answers.append(answer_id)

            if len(answers) == 0:
                raise NotFound("This barcode is not associated with any "
                               "surveys matching this template id")
            if len(answers) > 1:
                #  I really hope this can't happen.  (x . x)
                raise RepoException("This barcode is associated with more "
                                    "than one survey matching this template"
                                    " id")
            answer_ids = answers

        metadata_map = survey_answers_repo.build_metadata_map()

        all_survey_answers = []
        for answer_id in answer_ids:
            answer_model = survey_answers_repo.get_answered_survey(
                account_id, source_id, answer_id, "en-US")

            survey_answers = {}
            for k in answer_model:
                new_k = metadata_map[int(k)]
                survey_answers[k] = [new_k, answer_model[k]]

            all_survey_answers.append({
                "template":
                answer_to_template_map[answer_id],
                "response":
                survey_answers
            })

        pulldown = {
            "sample_barcode": sample_barcode,
            "host_subject_id": host_subject_id,
            "account": account,
            "source": source,
            "sample": sample,
            "survey_answers": all_survey_answers
        }

        return pulldown
Exemple #26
0
    def retrieve_diagnostics_by_barcode(self, sample_barcode, grab_kit=True):
        def _rows_to_dicts_list(rows):
            return [dict(x) for x in rows]

        with self._transaction.dict_cursor() as cur:
            ids = self._get_ids_relevant_to_barcode(sample_barcode)

            if ids is None:
                ids = {}

            # default for not found is None
            sample_id = ids.get("sample_id")
            source_id = ids.get("source_id")
            account_id = ids.get("account_id")
            # NB: this is the true UUID kit id (the primary key of
            # ag.ag_kit), NOT the kit's participant-facing string "id"
            kit_id = ids.get("kit_id")

            account = None
            source = None
            sample = None
            kit = None

            # get sample object for this barcode, if any
            if sample_id is not None:
                sample_repo = SampleRepo(self._transaction)
                sample = sample_repo._get_sample_by_id(sample_id)

            # get account object for this barcode, if any
            if account_id is not None:
                account_repo = AccountRepo(self._transaction)
                account = account_repo.get_account(account_id)

            # and source object for this barcode, if any
            if source_id is not None:
                source_repo = SourceRepo(self._transaction)
                source = source_repo.get_source(account_id, source_id)

            # get (partial) projects_info list for this barcode
            query = f"""
                    SELECT {p.DB_PROJ_NAME_KEY}, {p.IS_MICROSETTA_KEY},
                    {p.BANK_SAMPLES_KEY}, {p.PLATING_START_DATE_KEY}
                    FROM barcodes.project
                    INNER JOIN barcodes.project_barcode
                    USING (project_id)
                    WHERE barcode=%s;"""

            cur.execute(query, (sample_barcode, ))
            # this can't be None; worst-case is an empty list
            projects_info = _rows_to_dicts_list(cur.fetchall())

            # get scans_info list for this barcode
            # NB: ORDER MATTERS here. Do not change the order unless you
            # are positive you know what already depends on it.
            cur.execute(
                "SELECT barcode_scan_id, barcode, "
                "scan_timestamp, sample_status, "
                "technician_notes "
                "FROM barcodes.barcode_scans "
                "WHERE barcode=%s "
                "ORDER BY scan_timestamp asc", (sample_barcode, ))
            # this can't be None; worst-case is an empty list
            scans_info = _rows_to_dicts_list(cur.fetchall())

            latest_scan = None
            if len(scans_info) > 0:
                # NB: the correctness of this depends on the scans (queried
                # right above) being in ascending order by timestamp
                latest_scan = scans_info[len(scans_info) - 1]

            # get details about this barcode itself; CAN be None if the
            # barcode doesn't exist in db
            barcode_info = None
            cur.execute(
                "SELECT barcode, assigned_on, status, "
                "sample_postmark_date, biomass_remaining, "
                "sequencing_status, obsolete, "
                "create_date_time, kit_id "
                "FROM barcodes.barcode "
                "WHERE barcode = %s", (sample_barcode, ))
            barcode_row = cur.fetchone()
            if barcode_row is not None:
                barcode_info = dict(barcode_row)

            if account is None and source is None and sample is None and \
                    len(projects_info) == 0 and len(scans_info) == 0 \
                    and barcode_info is None:
                return None

            diagnostic = {
                "account": account,
                "source": source,
                "sample": sample,
                "latest_scan": latest_scan,
                "scans_info": scans_info,
                "barcode_info": barcode_info,
                "projects_info": projects_info
            }

            if grab_kit:
                # get kit object
                if kit_id is not None:
                    kit_repo = KitRepo(self._transaction)
                    kit = kit_repo.get_kit_all_samples_by_kit_id(kit_id)
                diagnostic["kit"] = kit

            return diagnostic
    def retrieve_diagnostics_by_barcode(self, sample_barcode, grab_kit=True):
        def _rows_to_dicts_list(rows):
            return [dict(x) for x in rows]

        with self._transaction.dict_cursor() as cur:
            ids = self._get_ids_relevant_to_barcode(sample_barcode)

            if ids is None:
                sample_id = None
                source_id = None
                account_id = None
                kit_id = None
            else:
                sample_id = ids["sample_id"]
                source_id = ids["source_id"]
                account_id = ids["account_id"]
                kit_id = ids["kit_id"]

            account = None
            source = None
            sample = None
            kit = None

            # get sample object for this barcode
            if sample_id is not None:
                sample_repo = SampleRepo(self._transaction)
                sample = sample_repo._get_sample_by_id(sample_id)

            # get account and source objects for this barcode
            if source_id is not None and account_id is not None:
                account_repo = AccountRepo(self._transaction)
                source_repo = SourceRepo(self._transaction)
                account = account_repo.get_account(account_id)
                source = source_repo.get_source(account_id, source_id)

            # get projects_info list for this barcode
            cur.execute("SELECT project, is_microsetta, "
                        "bank_samples, plating_start_date "
                        "FROM barcodes.project "
                        "INNER JOIN barcodes.project_barcode "
                        "USING (project_id) "
                        "WHERE barcode=%s",
                        (sample_barcode,))
            # this can't be None; worst-case is an empty list
            projects_info = _rows_to_dicts_list(cur.fetchall())

            # get scans_info list for this barcode
            # NB: ORDER MATTERS here. Do not change the order unless you
            # are positive you know what already depends on it.
            cur.execute("SELECT barcode_scan_id, barcode, "
                        "scan_timestamp, sample_status, "
                        "technician_notes "
                        "FROM barcodes.barcode_scans "
                        "WHERE barcode=%s "
                        "ORDER BY scan_timestamp asc",
                        (sample_barcode,))
            # this can't be None; worst-case is an empty list
            scans_info = _rows_to_dicts_list(cur.fetchall())

            latest_scan = None
            if len(scans_info) > 0:
                # NB: the correctness of this depends on the scans (queried
                # right above) being in ascending order by timestamp
                latest_scan = scans_info[len(scans_info)-1]

            # get details about this barcode itself; CAN be None if the
            # barcode doesn't exist in db
            barcode_info = None
            cur.execute("SELECT barcode, assigned_on, status, "
                        "sample_postmark_date, biomass_remaining, "
                        "sequencing_status, obsolete, "
                        "create_date_time, kit_id "
                        "FROM barcodes.barcode "
                        "WHERE barcode = %s",
                        (sample_barcode,))
            barcode_row = cur.fetchone()
            if barcode_row is not None:
                barcode_info = dict(barcode_row)

            if account is None and source is None and sample is None and \
                    len(projects_info) == 0 and len(scans_info) == 0 \
                    and barcode_info is None:
                return None

            diagnostic = {
                "account": account,
                "source": source,
                "sample": sample,
                "latest_scan": latest_scan,
                "scans_info": scans_info,
                "barcode_info": barcode_info,
                "projects_info": projects_info
            }

            if grab_kit:
                # get kit object
                if kit_id is not None:
                    kit_repo = KitRepo(self._transaction)
                    kit = kit_repo.get_kit_all_samples_by_kit_id(kit_id)
                diagnostic["kit"] = kit

            return diagnostic
Exemple #28
0
def per_sample(project, barcodes, strip_sampleid):
    summaries = []
    with Transaction() as t:
        admin_repo = AdminRepo(t)
        sample_repo = SampleRepo(t)
        template_repo = SurveyTemplateRepo(t)
        vs_repo = VioscreenSessionRepo(t)

        if project is not None:
            project_barcodes = admin_repo.get_project_barcodes(project)
        else:
            project = 'Unspecified'

        if barcodes is None:
            barcodes = project_barcodes

        for barcode in barcodes:
            diag = admin_repo.retrieve_diagnostics_by_barcode(barcode)
            if diag is None:
                raise NotFound(f"Barcode not found: {barcode}")

            sample = diag['sample']
            account = diag['account']
            source = diag['source']

            account_email = None if account is None else account.email
            source_email = None
            source_type = None if source is None else source.source_type
            vio_id = None

            if source is not None and source_type == Source.SOURCE_TYPE_HUMAN:
                source_email = source.source_data.email

                vio_id = template_repo.get_vioscreen_id_if_exists(account.id,
                                                                  source.id,
                                                                  sample.id)

            # at least one sample has been observed that "is_microsetta",
            # described in the barcodes.project_barcode table, but which is
            # unexpectedly not present in ag.ag_kit_barcodes
            if sample is None:
                sample_status = None
                sample_site = None
                ffq_complete = None
                ffq_taken = None
            else:
                sample_status = sample_repo.get_sample_status(
                    sample.barcode,
                    sample._latest_scan_timestamp
                )
                sample_site = sample.site
                ffq_complete, ffq_taken, _ = vs_repo.get_ffq_status_by_sample(
                    sample.id
                )

            summary = {
                "sampleid": None if strip_sampleid else barcode,
                "project": project,
                "source-type": source_type,
                "site-sampled": sample_site,
                "source-email": source_email,
                "account-email": account_email,
                "vioscreen_username": vio_id,
                "ffq-taken": ffq_taken,
                "ffq-complete": ffq_complete,
                "sample-status": sample_status,
                "sample-received": sample_status is not None
            }

            for status in ["sample-is-valid",
                           "no-associated-source",
                           "no-registered-account",
                           "no-collection-info",
                           "sample-has-inconsistencies",
                           "received-unknown-validity"]:
                summary[status] = sample_status == status

            summaries.append(summary)
    return summaries
def query_email_stats(body, token_info):
    validate_admin_access(token_info)

    email_list = body.get("emails", [])
    project = body.get("project")

    results = []
    with Transaction() as t:
        account_repo = AccountRepo(t)
        kit_repo = KitRepo(t)
        source_repo = SourceRepo(t)
        sample_repo = SampleRepo(t)

        for email in email_list:
            result = {'email': email, 'project': project}
            results.append(result)
            # can use internal lookup by email, because we have admin access
            account = account_repo._find_account_by_email(email)  # noqa
            if account is None:
                result['summary'] = "No Account"
                continue
            else:
                result['account_id'] = account.id
                result['creation_time'] = account.creation_time
                result['kit_name'] = account.created_with_kit_id

            if account.created_with_kit_id is not None:
                unused = kit_repo.get_kit_unused_samples(
                             account.created_with_kit_id
                         )
                if unused is None:
                    result['unclaimed-samples-in-kit'] = 0
                else:
                    result['unclaimed-samples-in-kit'] = len(unused.samples)

            sample_statuses = defaultdict(int)
            sources = source_repo.get_sources_in_account(account.id)

            samples_in_project = 0
            for source in sources:
                samples = sample_repo.get_samples_by_source(account.id,
                                                            source.id)
                for sample in samples:
                    if project is not None and \
                       project != "" and \
                       project not in sample.sample_projects:
                        continue
                    samples_in_project += 1
                    sample_status = sample_repo.get_sample_status(
                        sample.barcode,
                        sample._latest_scan_timestamp  # noqa
                    )
                    if sample_status is None:
                        sample_status = "never-scanned"
                    sample_statuses[sample_status] += 1
            result.update(sample_statuses)

            if result.get('unclaimed-samples-in-kit', 0) > 0:
                result['summary'] = 'Possible Unreturned Samples'
            elif samples_in_project == 0:
                result['summary'] = "No Samples In Specified Project"
            elif result.get('sample-is-valid') == samples_in_project:
                result['summary'] = 'All Samples Valid'
            else:
                result['summary'] = 'May Require User Interaction'

    return jsonify(results), 200
Exemple #30
0
    def retrieve_diagnostics_by_barcode(self, sample_barcode, grab_kit=True):
        with self._transaction.dict_cursor() as cur:
            ids = self._get_ids_relevant_to_barcode(sample_barcode)

            if ids is None:
                sample_id = None
                source_id = None
                account_id = None
                kit_id = None
            else:
                sample_id = ids["sample_id"]
                source_id = ids["source_id"]
                account_id = ids["account_id"]
                kit_id = ids["kit_id"]

            account = None
            source = None
            sample = None
            kit = None

            if sample_id is not None:
                sample_repo = SampleRepo(self._transaction)
                sample = sample_repo._get_sample_by_id(sample_id)

            if source_id is not None and account_id is not None:
                account_repo = AccountRepo(self._transaction)
                source_repo = SourceRepo(self._transaction)
                account = account_repo.get_account(account_id)
                source = source_repo.get_source(account_id, source_id)

            if kit_id is not None and grab_kit:
                kit_repo = KitRepo(self._transaction)
                kit = kit_repo.get_kit_all_samples_by_kit_id(kit_id)

            cur.execute("SELECT * from barcodes.barcode "
                        "LEFT OUTER JOIN barcodes.project_barcode "
                        "USING (barcode) "
                        "LEFT OUTER JOIN barcodes.project "
                        "USING (project_id) "
                        "where barcode=%s",
                        (sample_barcode,))
            barcode_info = cur.fetchall()

            # How to unwrap a psycopg2 DictRow.  I feel dirty.
            barcode_info = [{k: v for k, v in x.items()}
                            for x in barcode_info]  # Get Inceptioned!!

            # Collapse info from joined project_barcode and project tables
            # into array within barcode_info
            if barcode_info:
                first = barcode_info[0]
                first['projects'] = [
                    {
                        'project_id': r['project_id'],
                        'project': r['project']
                    }
                    for r in barcode_info]
                del first['project_id']
                del first['project']
                barcode_info = first
            else:
                barcode_info = None

            if account is None and \
                    source is None and \
                    sample is None and \
                    barcode_info is None:
                return None

            diagnostic = {
                "barcode": sample_barcode,
                "account": account,
                "source": source,
                "sample": sample,
                "barcode_info": barcode_info
            }

            if grab_kit:
                diagnostic["kit"] = kit

            return diagnostic