Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions chord_metadata_service/patients/api_views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import asyncio
from datetime import datetime

from adrf.views import APIView
from asgiref.sync import async_to_sync
from bento_lib.auth.permissions import Permission, P_QUERY_DATA
from bento_lib.auth.permissions import P_QUERY_DATA, Permission
from bento_lib.responses import errors
from bento_lib.search import build_search_response
from datetime import datetime
from django.contrib.postgres.aggregates import ArrayAgg
from django.core.exceptions import ValidationError
from django.db.models import Count, F, Q
Expand All @@ -19,11 +19,11 @@
from rest_framework.settings import api_settings

from chord_metadata_service.authz.middleware import authz_middleware
from chord_metadata_service.authz.viewset import BentoAuthzScopedModelViewSet, BentoAuthzScopedModelGenericListViewSet
from chord_metadata_service.authz.viewset import BentoAuthzScopedModelGenericListViewSet, BentoAuthzScopedModelViewSet
from chord_metadata_service.chord import data_types as dts
from chord_metadata_service.discovery import responses as dres
from chord_metadata_service.discovery.censorship import get_threshold, thresholded_count
from chord_metadata_service.discovery.exceptions import DiscoveryScopeException, DiscoveryEmptyException
from chord_metadata_service.discovery.exceptions import DiscoveryEmptyException, DiscoveryScopeException
from chord_metadata_service.discovery.filtering import discovery_filter_queryset
from chord_metadata_service.discovery.pydantic_models import DiscoveryQuery
from chord_metadata_service.discovery.scope import get_request_discovery_scope
Expand All @@ -39,15 +39,15 @@
from chord_metadata_service.phenopackets.models import Phenopacket
from chord_metadata_service.phenopackets.serializers import PhenopacketSerializer
from chord_metadata_service.restapi.api_renderers import (
PhenopacketsRenderer,
IndividualBentoSearchRenderer,
IndividualCSVRenderer,
IndividualXLSXRenderer,
IndividualBentoSearchRenderer,
PhenopacketsRenderer,
csv_fields_error_response,
)
from chord_metadata_service.restapi.constants import MODEL_ID_PATTERN
from chord_metadata_service.restapi.pagination import LargeResultsSetPagination, BatchResultsSetPagination
from chord_metadata_service.restapi.negociation import FormatInPostContentNegotiation
from chord_metadata_service.restapi.pagination import BatchResultsSetPagination, LargeResultsSetPagination
from chord_metadata_service.restapi.utils import (
build_experiments_by_subject,
get_biosamples_with_experiment_details,
Expand Down
4 changes: 2 additions & 2 deletions chord_metadata_service/patients/cleanup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import chord_metadata_service.phenopackets.models as pm

from structlog.stdlib import BoundLogger

import chord_metadata_service.phenopackets.models as pm
from chord_metadata_service.cleanup.remove import remove_not_referenced
from chord_metadata_service.utils import build_id_set_from_model

from .models import Individual

__all__ = [
Expand Down
1 change: 0 additions & 1 deletion chord_metadata_service/patients/descriptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from chord_metadata_service.restapi.description_utils import EXTRA_PROPERTIES, ontology_class


# TODO: This is part of another app
INDIVIDUAL = {
"description": "A subject of a phenopacket, representing either a human (typically) or another organism.",
Expand Down
1 change: 1 addition & 0 deletions chord_metadata_service/patients/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.db.models import Q

from chord_metadata_service.discovery.full_text_search import full_text_search_vector

from .models import Individual

GENOMIC_INTERPRETATION_QUERY = "phenopackets__interpretations__diagnosis__genomic_interpretations"
Expand Down
10 changes: 6 additions & 4 deletions chord_metadata_service/patients/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from django.apps import apps
from django.contrib.postgres.fields import ArrayField
from django.contrib.postgres.indexes import GinIndex
from django.db import models
from django.db.models import JSONField
from django.contrib.postgres.fields import ArrayField
from chord_metadata_service.discovery.scopeable_model import BaseScopeableModel

from chord_metadata_service.discovery.full_text_search import BaseFTSModel, ToFTSReprMixin
from chord_metadata_service.discovery.scopeable_model import BaseScopeableModel
from chord_metadata_service.discovery.types import ModelScopeFilters
from chord_metadata_service.restapi.models import BaseTimeStamp, IndexableMixin, SchemaType, BaseExtraProperties
from chord_metadata_service.restapi.models import BaseExtraProperties, BaseTimeStamp, IndexableMixin, SchemaType
from chord_metadata_service.restapi.schema_ref import SchemaRefs
from chord_metadata_service.restapi.validators import JsonSchemaValidator, ontology_validator
from .values import PatientStatus, Sex, KaryotypicSex

from .values import KaryotypicSex, PatientStatus, Sex


class VitalStatus(BaseTimeStamp, IndexableMixin, ToFTSReprMixin):
Expand Down
11 changes: 6 additions & 5 deletions chord_metadata_service/patients/schemas.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from django.conf import settings

from chord_metadata_service.restapi.constants import MODEL_ID_PATTERN
from chord_metadata_service.restapi.schema_utils import (
DATE_TIME,
DRAFT_07,
SchemaTypes,
array_of,
base_type,
string_with_pattern,
enum_of,
tag_ids_and_describe,
string_with_pattern,
sub_schema_uri,
tag_ids_and_describe,
)
from chord_metadata_service.restapi.schemas import ONTOLOGY_CLASS, EXTRA_PROPERTIES_SCHEMA, TIME_ELEMENT_SCHEMA
from .descriptions import INDIVIDUAL, VITAL_STATUS
from .values import Sex, KaryotypicSex
from chord_metadata_service.restapi.schemas import EXTRA_PROPERTIES_SCHEMA, ONTOLOGY_CLASS, TIME_ELEMENT_SCHEMA

from .descriptions import INDIVIDUAL, VITAL_STATUS
from .values import KaryotypicSex, Sex

phenopacket_base_uri = f"{settings.SCHEMAS_BASE_URL}/phenopacket"

Expand Down
1 change: 1 addition & 0 deletions chord_metadata_service/patients/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from chord_metadata_service.phenopackets.serializers import BiosampleSerializer, SimplePhenopacketSerializer
from chord_metadata_service.restapi.serializers import GenericSerializer

from .models import Individual, VitalStatus

__all__ = [
Expand Down
1 change: 1 addition & 0 deletions chord_metadata_service/patients/summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from chord_metadata_service.discovery.censorship import thresholded_count
from chord_metadata_service.discovery.fields import get_age_numeric_binned
from chord_metadata_service.discovery.stats import queryset_stats_for_field

from . import models

__all__ = ["individual_summary"]
Expand Down
3 changes: 2 additions & 1 deletion chord_metadata_service/patients/tests/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import uuid
import random
import uuid
from datetime import date, timedelta

import isodate


Expand Down
27 changes: 13 additions & 14 deletions chord_metadata_service/patients/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import csv
import io
import openpyxl
import random
import uuid

from bento_lib.discovery import DiscoveryConfig
from copy import deepcopy

from django.db.models import Q, F, Value
from django.urls import reverse
import openpyxl
from bento_lib.discovery import DiscoveryConfig
from django.db.models import F, Q, Value
from django.test import TestCase, override_settings
from django.urls import reverse
from rest_framework import status

from chord_metadata_service.authz.tests.helpers import AuthzAPITestCase
from chord_metadata_service.chord import models as cm
from chord_metadata_service.chord.dataset_schema import KatsuDatasetModel
Expand All @@ -19,9 +19,9 @@
from chord_metadata_service.discovery import responses as dres
from chord_metadata_service.discovery.fields_utils import JSONBPathFilter
from chord_metadata_service.discovery.tests.constants import (
CONFIG_PUBLIC_TEST_SEARCH_SEX_ONLY,
DISCOVERY_CONFIG_EXTRA_PROPERTIES,
DISCOVERY_CONFIG_TEST,
CONFIG_PUBLIC_TEST_SEARCH_SEX_ONLY,
DISCOVERY_ZERO_COUNTS,
)
from chord_metadata_service.experiments import models as ex_m
Expand Down Expand Up @@ -612,7 +612,7 @@ class DiscoveryFilteringIndividualsTest(AuthzAPITestCase, ProjectTestCase):

@staticmethod
def response_threshold_check(response):
return response["count"] if "count" in response else dres.INSUFFICIENT_DATA_AVAILABLE
return response.get("count", dres.INSUFFICIENT_DATA_AVAILABLE)

def setUp(self):
random.seed(self.random_seed)
Expand Down Expand Up @@ -1269,20 +1269,19 @@ class DiscoveryAgeRangeFilteringIndividualsTest(AuthzAPITestCase):

@staticmethod
def response_threshold_check(response):
return response["count"] if "count" in response else dres.INSUFFICIENT_DATA_AVAILABLE
return response.get("count", dres.INSUFFICIENT_DATA_AVAILABLE)

def setUp(self):
individuals = [c.generate_valid_individual(gen_random_age=(1, 100)) for _ in range(self.random_range)]
for individual in individuals:
Individual.objects.create(**individual)

for individual in Individual.objects.all():
if individual.time_at_last_encounter:
if "age" in individual.time_at_last_encounter:
age_numeric, age_unit = iso_duration_to_years(individual.time_at_last_encounter["age"])
individual.age_numeric = age_numeric
individual.age_unit = age_unit if age_unit else ""
individual.save()
if individual.time_at_last_encounter and "age" in individual.time_at_last_encounter:
age_numeric, age_unit = iso_duration_to_years(individual.time_at_last_encounter["age"])
individual.age_numeric = age_numeric
individual.age_unit = age_unit if age_unit else ""
individual.save()

@override_settings(CONFIG_PUBLIC=DISCOVERY_CONFIG_TEST)
def test_discovery_filtering_age_range(self):
Expand Down
4 changes: 2 additions & 2 deletions chord_metadata_service/patients/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from chord_metadata_service.chord.tests.helpers import ProjectTestCase
from chord_metadata_service.phenopackets.tests import constants as c
from chord_metadata_service.phenopackets import models as m
from chord_metadata_service.phenopackets.tests import constants as c
from chord_metadata_service.restapi.models import SchemaType

from ..models import Individual, VitalStatus
from ..filters import IndividualFilter
from ..models import Individual, VitalStatus


class IndividualTest(ProjectTestCase):
Expand Down
1 change: 0 additions & 1 deletion chord_metadata_service/patients/values.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from abc import ABC


__all__ = [
"Sex",
"KaryotypicSex",
Expand Down
7 changes: 4 additions & 3 deletions chord_metadata_service/utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from typing import Any

from django.db.models import Model, QuerySet
from typing import Any, Set, Type

__all__ = [
"build_id_set",
"build_id_set_from_model",
]


async def build_id_set(qs: QuerySet, field: str) -> Set[Any]:
async def build_id_set(qs: QuerySet, field: str) -> set[Any]:
s = set()
async for v in qs.values_list(field, flat=True):
s.add(v)
return s


async def build_id_set_from_model(m: Type[Model], field: str) -> Set[Any]:
async def build_id_set_from_model(m: type[Model], field: str) -> set[Any]:
return await build_id_set(m.objects.all(), field)
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
import datetime
import os
import sys
from pathlib import Path

import django
import toml

from pathlib import Path

sys.path.insert(0, os.path.abspath(".."))
os.environ["DJANGO_SETTINGS_MODULE"] = "chord_metadata_service.metadata.settings"
django.setup()
Expand Down
27 changes: 10 additions & 17 deletions scripts/ingest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
import json
import sys

import requests

"""
Expand Down Expand Up @@ -47,19 +48,15 @@ def create_project(katsu_server_url, project_title):
try:
r = requests.post(katsu_server_url + "/api/projects", json=project_request)
except requests.exceptions.ConnectionError:
print("Connection to the API server {} cannot be established.".format(katsu_server_url))
print(f"Connection to the API server {katsu_server_url} cannot be established.")
sys.exit()

if r.status_code == 201:
project_uuid = r.json()["identifier"]
print("Project {} with uuid {} has been created!".format(project_title, project_uuid))
print(f"Project {project_title} with uuid {project_uuid} has been created!")
return project_uuid
elif r.status_code == 400:
print(
"A project of title '{}' exists, please choose a different title, or delete this project.".format(
project_title
)
)
print(f"A project of title '{project_title}' exists, please choose a different title, or delete this project.")
sys.exit()
else:
print(r.json())
Expand Down Expand Up @@ -88,14 +85,10 @@ def create_dataset(katsu_server_url, project_uuid, dataset_title):

if r2.status_code == 201:
dataset_uuid = r2.json()["identifier"]
print("Dataset {} with uuid {} has been created!".format(dataset_title, dataset_uuid))
print(f"Dataset {dataset_title} with uuid {dataset_uuid} has been created!")
return dataset_uuid
elif r2.status_code == 400:
print(
"A dataset of title '{}' exists, please choose a different title, or delete this dataset.".format(
dataset_title
)
)
print(f"A dataset of title '{dataset_title}' exists, please choose a different title, or delete this dataset.")
sys.exit()
else:
print(r2.json())
Expand All @@ -115,7 +108,7 @@ def create_table(katsu_server_url, dataset_uuid, table_name):

if r3.status_code == 200 or r3.status_code == 201:
table_id = r3.json()["id"]
print("Table {} with uuid {} has been created!".format(table_name, table_id))
print(f"Table {table_name} with uuid {table_id} has been created!")
return table_id
else:
print("Something went wrong...")
Expand All @@ -139,7 +132,7 @@ def ingest_phenopackets(katsu_server_url, table_id, phenopackets_json_location):
r4 = requests.post(katsu_server_url + "/private/ingest", json=private_ingest_request)

if r4.status_code == 200 or r4.status_code == 201 or r4.status_code == 204:
print("Phenopackets have been ingested from source at {}".format(phenopackets_json_location))
print(f"Phenopackets have been ingested from source at {phenopackets_json_location}")
elif r4.status_code == 400:
print(r4.text)
sys.exit()
Expand All @@ -165,7 +158,7 @@ def main():
katsu_server_url = config["katsu_server_url"]
phenopackets_json_location = config["phenopackets_json_location"]
except KeyError as e:
print("Config file corrupted: missing key {}".format(str(e)))
print(f"Config file corrupted: missing key {e!s}")
sys.exit()

project_uuid = create_project(katsu_server_url, project_title)
Expand Down
9 changes: 5 additions & 4 deletions scripts/remove_from_db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
import subprocess
import sys

"""
A script that automates the process of deleting the data
Expand All @@ -26,15 +26,16 @@ def main():

for table in sys.argv[1:]:
response = subprocess.run(
'python ../manage.py shell --command="{}"'.format(script + "{}.objects.all().delete();".format(table)),
'python ../manage.py shell --command="{}"'.format(script + f"{table}.objects.all().delete();"),
shell=True,
stderr=subprocess.PIPE,
check=False,
)
if response.returncode:
print(response.stderr)
print('"{}" does not seem to be a valid table'.format(table))
print(f'"{table}" does not seem to be a valid table')
else:
print("Deleted data on table {}".format(table))
print(f"Deleted data on table {table}")


if __name__ == "__main__":
Expand Down
Loading